1.Set系列集合

1.1、Set集合概述

image-20230810161013540.png

  • Set系列集合特点
    • 无序:存取顺序不一致
    • 不重复:可以去除重复
    • 无索引:没有带索引的方法,所以不能使用for循环遍历,也不能通过索引拿数据
  • Set集合实现类特点
    • HashSet:无序、不重复、无索引
    • LinkedHashSet:有序、不重复、无索引
    • TreeSet:排序、不重复、无索引

1.2、实现类:HashSet集合

  • HashSet底层原理

    • HashSet集合底层采取哈希表存储的数据
    • 哈希表是一种对于增删改查数据性能都较好的结构
  • 哈希表的组成

    • JDK8之前,底层使用数组+链表组成
    • JDK8之后,底层采用数组+链表+红黑树组成
  • 哈希值

    • 是JDK根据对象的地址,按照某种规则算出来的int类型的数值
  • Object类的API

    • public int hashCode():返回对象的哈希值
  • 对象的哈希值特点

    • 同一个对象多次调用hashCode()方法返回的哈希值是相同的
    • 默认情况下,不同的对象哈希值是不同的【可以重写用来匹配内容】
  • HashSet 1.7版本原理解析:数组+链表+哈希算法

image-20230810161022493.png

  • 创建一个默认长度为16的数组,数组名table
  • 根据元素的哈希值跟数组的长度求余计算出应存入的位置(哈希算法)
  • 判断当前位置是否为null,如果是null直接存入
  • 如果位置不为null,表示有元素,则调用equals方法比较
  • 如果一样,则不存,如果不一样,则存入数组

【JDK 7新元素占老元素位置,指向老元素】

【JDK 8中新元素挂在老元素下面(链表形式)】

哈希表是一种对于增删改查数据性能都较好的结构

  • HashSet 1.8版本原理解析

    • 底层结构:哈希表(数组、链表、红黑树的结合体
    • 当挂在元素下面的数据过多时,查询性能降低,从JDK8开始后,当链表长度超过8的时候,自动转为红黑树数据形式
  • Student

public class Student {
    private String name;
    private Integer age;
    private char sex;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return sex == student.sex && Objects.equals(name, student.name) && Objects.equals(age, student.age);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age, sex);
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex=" + sex +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    public Student(String name, Integer age, char sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }

    public Student() {
    }
}
  • Test
public class Test {
    public static void main(String[] args) {
        Set<Student> students = new HashSet<>();
        Student s1 = new Student("马浩楠", 21, '男');
        Student s2 = new Student("马浩楠", 21, '男');
        Student s3 = new Student("林燕",22,'女');

        students.add(s1);
        students.add(s2);
        students.add(s3);
        System.out.println(students);
        System.out.println(s1.hashCode());//-1285672988
        System.out.println(s2.hashCode());//-1285672988
        System.out.println(s3.hashCode());
    }
}

1.3、实现类:LinkedHashSet

  • 概述和特点
    • 有序、不重复、无索引
    • 这里的有序指的是保证存储的和取出的元素顺序一致
    • 原理:底层数据结构依然是哈希表,只是每个元素又额外多了一个双链表的机制记录存储的顺序

1.4、实现类:TreeSet

  • 概述和特点

    • 不重复、无索引、可排序
    • 可排序:按照元素的大小默认升序
    • TreeSet 集合底层是基于红黑树的数据结构实现排序的,增删改查性能都较好
    • 【注意】:TreeSet集合是一定排序的,可将元素按照指定的规则排序
  • TreeSet集合默认的规则

    • 对于数值类型:Integer,Double 官方默认按照大小进行升序
    • 对于字符串类型:默认按照首字符的编号升序排序
    • 对于自定义类型,无法直接排序【需要制定比较规则】
  • 自定义排序规则

    • 方式一:让自定义的类实现Comparable接口重写里面的comparaeTo方法来指定比较规则
    • 方式二:TreeSet集合存在一个有参构造器,可以设置Comparator接口对应的比较器对象,来指定比较规则
  • 两种方式中,返回值规则

    • 如果认为第一个元素大于第二个元素返回正整数即可
    • 如果认为第一个元素小于第二个元素返回负整数即可
    • 如果认为第一个元素等于第二个元素返回0即可,此时TreeSet集合只会保留一个元素,认为存在重复

【注意】:如果TreeSet集合存储的对象又实现比较规则,集合也自带比较器,默认使用集合自带的比较器排序

  • 【案例需求】:
    • 键盘录入三个学生信息(姓名,身高,成绩),按照成绩或身高排序输出
  • Student
public class Student implements Comparable<Student>{
    private String name;
    private double height;
    private int score;

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", height=" + height +
                ", score=" + score +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }

    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }

    public Student(String name, double height, int score) {
        this.name = name;
        this.height = height;
        this.score = score;
    }

    public Student() {
    }

    @Override
    public int compareTo(Student o) {
        //return this.score - o.score;会丢失成绩重复的数据
        return this.score - o.score >=0 ? 1:-1;
    }
}
  • Test
public class Test {
    public static void main(String[] args) {
        Set<Integer> set = new TreeSet<>();
        set.add(22);
        set.add(2);
        set.add(26);
        set.add(18);
        set.add(99);
        System.out.println(set);

        Set set1 = new TreeSet();
        set1.add("Java");
        set1.add("java");
        set1.add("浩楠");
        set1.add("Object");
        set1.add("UI");
        System.out.println(set1);

        System.out.println("------------------");
        //方式二(排序):集合自带比较规则
        //集合自带的比较规则要比对象内实现的比较器优先级高
        Set<Student> students = new TreeSet<>((Student o1,Student o2) -> {
            return Double.compare(o1.getHeight(),o2.getHeight());
        });
        students.add(new Student("张三",170,88));
        students.add(new Student("李四",180,90));
        students.add(new Student("王五",168,57));
        students.add(new Student("程明辉",175,92));
        students.add(new Student("李泽",150,92));
        System.out.println(students);
    }
}

2.Collection体系的特点

  • 如果希望元素可以重复,又有索引,索引查询要快?
    • 用ArrayList集合,基于数组的(用的最多)
  • 如果希望元素可以重复,又又索引,增删首尾操作快?
    • 用LinkedList集合,基于链表
  • 如果希望增删改查都快,但是元素不重复,无序,无索引
    • 用HashSet集合,基于哈希表
  • 如果希望增删改查都快,但是元素不重复、有序、无索引
    • 用LinkedHashSet集合,基于哈希表和双链表
  • 如果要用对象进行排序
    • 用TreeSet集合,基于红黑树,后续也可以用List集合实现引用类型排序

3.补充:可变参数

  • 概述
    • 可变参数用在形参中可以接受多个数据
    • 格式:数据类型 ... 参数名称
  • 可变参数的作用
    • 接收参数非常灵活,方便。也可以不接收参数,意为可接收一个或多个参数,也可以接收一个数组
    • 可变参数在方法内部本质上就是一个数组
  • 注意事项
    • 一个形参列表中可变参数只能有一个
    • 可变参数必须放在形参列表的最后面
  • 案例
    • 计算1个数据的和
    • 计算2个数据的和
    • 计算n个数据的和....
public class Test {
    public static void main(String[] args) {
        sum();
        System.out.println("-----------");
        sum(1);
        System.out.println("-----------");
        sum(new int[]{3,20,50,20,1,0});
    }
    public static void sum(int...sums){
        System.out.println("长度:"+sums.length);
        System.out.println(Arrays.toString(sums));
    }
}

4.补充:集合工具类Collections

  • Collections集合工具类
    • java.utils.Collections:集合工具类
    • 作用:Collections并不属于集合,是用来操作集合的工具类
  • 常用API

image-20230810161043805.png

  • 排序相关API(只能对于List集合排序)

image-20230810161049621.png

  • Student
public class Student implements Comparable<Student>{
    private String name;
    private double height;
    private int score;

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", height=" + height +
                ", score=" + score +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }

    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }

    public Student(String name, double height, int score) {
        this.name = name;
        this.height = height;
        this.score = score;
    }

    public Student() {
    }

    @Override
    public int compareTo(Student o) {
        return this.score - o.score;//List集合允许重复,所以不会像Set集合一样去重数据
        //return this.score - o.score >=0 ? 1:-1;
    }
}
  • Test
public class Test {
    public static void main(String[] args) {
        //1.添加多条数据
        ArrayList<String> list = new ArrayList<>();
        //list.add("马浩楠");
        //list.add("程明辉");
        //list.add("李泽");
        //list.add("吴赛");
        Collections.addAll(list,"马浩楠","程明辉","李泽","吴赛");
        System.out.println(list);
        //2.shuffel:打乱集合顺序(List集合)
        Collections.shuffle(list);
        System.out.println(list);
        //3.sort:排序
        ArrayList<Student> students = new ArrayList<>();
        students.add(new Student("张三",170,88));
        students.add(new Student("李四",180,90));
        students.add(new Student("程明辉",175,92));
        students.add(new Student("李泽",150,92));
        //Collections.sort(students);
        Collections.sort(students,(o1,o2) -> Double.compare(o1.getHeight(),o2.getHeight()) );
        System.out.println(students);
    }
}

5.Collection综合案例:斗地主

【需求】

  • 在启动游戏房间的时候,应该提前准备好54张牌,完成洗牌、发牌、牌的排序。

  • Board

public class Board {
    private String size;
    private String color;
    private int index;//比较大小排序的作用

    public int getIndex() {
        return index;
    }

    public void setIndex(int index) {
        this.index = index;
    }

    public Board(String size, String color, int index) {
        this.size = size;
        this.color = color;
        this.index = index;
    }

    @Override
    public String toString() {
        return size+color;
    }

    public String getSize() {
        return size;
    }

    public void setSize(String size) {
        this.size = size;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public Board() {
    }
}
  • Test
public class Test {
    //1.定义牌集合
    public static ArrayList<Board> boards = new ArrayList<>();
    //2.定义牌装入集合
    static{
        String[] numbers = {"3","4","5","6","7","8","9","10","J","Q","K","A","2"};
        String[] colors = {"♥","♦","♣","♠"};
        int index = 0;//标记牌的大小方便排序
        for (String number : numbers) {
            index++;
            for (String color : colors) {
                boards.add(new Board(number,color,index));
            }
        }
        for (int i = 0; i < numbers.length; i++) {
            for (int j = 0; j < colors.length; j++) {

            }
        }
        boards.add(new Board("","小🃏",index++));
        boards.add(new Board("","大🃏",index++));
        System.out.println(boards.size());
    }
    public static void main(String[] args) {
        //3.洗牌
        Collections.shuffle(boards);
        //4.创建玩家牌集合
        ArrayList<Board> mahaonan = new ArrayList<>();
        ArrayList<Board> chengminghui = new ArrayList<>();
        ArrayList<Board> lize = new ArrayList<>();
        //5.发牌
        for (int i = 0; i < boards.size()-3; i++) {
            if(i%3 == 0){
                mahaonan.add(boards.get(i));
            }else if(i%3 == 1){
                chengminghui.add(boards.get(i));
            }else if(i%3 == 2){
                lize.add(boards.get(i));
            }
        }
        //6.低配(用List中的API方法来截取最后三张)
        List<Board> lastThree = Test.boards.subList(Test.boards.size() - 3, Test.boards.size());
        //7.给玩家的牌排序
        sortBoards(mahaonan);
        sortBoards(chengminghui);
        sortBoards(lize);
        //8.输出玩家的牌
        System.out.println("马浩楠的牌:"+mahaonan);
        System.out.println("张林燕的牌:"+chengminghui);
        System.out.println("燕儿的牌:"+lize);
        //9.输出底牌
        System.out.println("底牌:"+lastThree);
    }
    public static void sortBoards(ArrayList<Board> boards){
        Collections.sort(boards,(o1,o2) -> o1.getIndex()-o2.getIndex());
    }
}

6.Map集合体系

6.1、Map集合概述

  • 概述和使用
    • Map集合是一种双列集合,每个元素包含两个数据
    • Map集合的每个元素的格式:key = value(键值对)
    • Map集合也被称为键值对集合
  • 格式:
    • Collection集合:[元素1,元素2,元素3...]
    • Map集合:{key=value,key1=value1,key2=value2,...}
  • 使用场景之一:购物车系统
    • 购物车提供的商品和购买数量需要容器存储
    • 每个商品对应着购买数量

image-20230810161104676.png

public class Test {
    public static void main(String[] args) {
        Map<String,Integer> map = new HashMap<>();
        map.put("鸿星尔克",2);
        map.put("Java",1);
        map.put("篮球鞋",2);
        map.put("Java",100);
        map.put(null,null);
        System.out.println(map);
    }
}

6.2、Map集合体系特点

  • 说明
    • 使用最多的Map集合是HashMap
  • Map集合体系特点
    • Map集合的特点都是由键决定的
    • Map集合的键是无序、不重复、无索引的,值不做要求(可重复)
    • Map集合后面重复的键对应的值会覆盖前面重复的键
    • Map集合的键值对都可以为null
  • Map集合实现类特点
    • HashMap:元素按照键是无序,不重复,无索引,值不做要求
    • LinkedHashMap:元素按照键是有序,不重复,无索引,值不做要求
    • TreeMap:元素按照键是排序,不重复,无所谓,值不做要求

6.3、Map集合常用API

image-20230810161113090.png

public class Api {
    public static void main(String[] args) {
        //1.添加元素:无需,不重复,无索引
        Map<String,Integer> maps = new HashMap<>();
        maps.put("舒肤佳",20);
        maps.put("Java",20);
        maps.put("卫衣",2);
        maps.put("羽绒服",1);
        maps.put("Java",30);
        System.out.println(maps);
        //2.清空集合
        //maps.clear();
        //System.out.println(maps);
        //3.判断集合是否为控,为空返回true
        System.out.println(maps.isEmpty());
        //4.根据键获取对应值
        System.out.println(maps.get("卫衣"));
        //5.根据键删除整个元素
        System.out.println(maps.remove("羽绒服"));
        //6.判断是否包含某个键
        System.out.println(maps.containsKey("Java"));
        //7.判断是否包含某个值
        System.out.println(maps.containsValue(30));
        //8.获取全部键的集合
        Set<String> keySet = maps.keySet();
        //9.获取全部值的集合
        Collection<Integer> values = maps.values();
        //10.集合的大小
        System.out.println(maps.size());
        //11.合并其他Map集合
        Map<String,Integer> map = new HashMap<>();
        map.put("MyBatis",20);
        map.put("Spring",2);
        Map<String,Integer> map2 = new HashMap<>();
        map.put("Spring",4);
        map.put("SpringBoot",1);
        map.putAll(map2);
        System.out.println(map);
    }
}

6.4、Map集合遍历方式:键找值

  • 先获取Map集合的全部键的Set集合
  • 遍历键的Set集合,然后通过键提取对应值
  • 涉及到的API

image-20230810161121892.png

public class Test {
    public static void main(String[] args) {
        Map<String,Integer> maps = new HashMap<>();
        maps.put("舒肤佳",20);
        maps.put("Java",20);
        maps.put("卫衣",2);
        maps.put("羽绒服",1);
        maps.put("Java",30);
        //1.获取key值的Set集合
        Set<String> keySet = maps.keySet();
        for (String s : keySet) {
            System.out.println(s +"---->"+maps.get(s));
        }
    }
}

6.5、Map集合遍历方式:键值对

  • 先把Map集合转为Set集合,Set集合中每个元素都是键值对实体类型了
  • 遍历Set集合,然后提取键以及值

  • 涉及到的API

image-20230810161130098.png

public class Test02 {
    public static void main(String[] args) {
        Map<String,Integer> maps = new HashMap<>();
        maps.put("舒肤佳",20);
        maps.put("Java",20);
        maps.put("卫衣",2);
        maps.put("羽绒服",1);
        maps.put("Java",30);

        Set<Map.Entry<String, Integer>> entries = maps.entrySet();
        for (Map.Entry<String, Integer> entry : entries) {
            System.out.println(entry.getKey()+"---->"+entry.getValue());
        }
    }
}

6.6、Map集合遍历方式:lambda表达式

  • 遍历API

1img_1.png

public class Test03 {
    public static void main(String[] args) {
        Map<String,Integer> maps = new HashMap<>();
        maps.put("舒肤佳",20);
        maps.put("Java",20);
        maps.put("卫衣",2);
        maps.put("羽绒服",1);
        maps.put("Java",30);
//        maps.forEach(new BiConsumer<String, Integer>() {
//            @Override
//            public void accept(String s, Integer integer) {
//                System.out.println(s+"---->"+integer);
//            }
//        });
        maps.forEach((s,v) -> {
            System.out.println(s+"---->"+v);
        });
    }
}
  • 案例
    • 某个班级80名学生,现在需要组成秋游活动,班长提供了四个景点以此是(A、B、C、D),每个学生只能选择一个景点,请痛击出最终哪个景点想去的人数最多
public class Test {
    public static void main(String[] args) {
        //1.吧80个学生的数据拿到
        String[] selects = {"A","B","C","D"};
        StringBuilder sb = new StringBuilder();
        Random random = new Random();
        for (int i = 0; i < 80; i++) {
            sb.append(selects[random.nextInt(selects.length)]);
        }
        System.out.println(sb);
        //2.定义Map集合,统计最终结果
        Map<Character,Integer> maps = new HashMap<>();
        //3.遍历80个学生选择的数据
        for (int i = 0; i < sb.length(); i++) {
            char c = sb.charAt(i);
            if(maps.containsKey(c)){
                maps.put(c,maps.get(c)+1);
            }else{
                maps.put(c,1);
            }
        }
        System.out.println(maps);
    }
}

6.7、Map集合的实现类HashMap

  • HashMap跟HashSet底层原理是一摸一样的,都是哈希表结构,只是HashMap的每个元素包含了两个值

【Set系列集合的底层就是Map实现的,只是Set集合中的元素只要键数据,不要值数据而已】

6.8、Map集合的实现类LinkedHashMap

  • 由键决定:有序、不重复、无索引
  • 有序指保证存储和取出的元素顺序一致
  • 原理:底层数据结构依然是哈希表,只是每个键值对元素由额外多了一个双链表的机制记录存储的顺序

6.9、Map集合的实现类TreeMap

  • 由键决定特性:不重复、无索引、可排序
  • 排序:按照数据的大小默认升序,只能对键排序
  • TreeMap集合是一定要排序的,可以默认排序,也可以将键按照指定的规则进行排序
  • TreeMap和TreeSet底层原理一样

  • TreeMap集合自定义排序规则两种

    • 类实现Comparable接口,重写比较规则
    • 集合自定义Comparator比较器对象,重写比较规则

7.补充:集合的嵌套

  • 需求
    • 某个班级多名学生,现在组织秋游活动,提供了四个景点(A、B、C、D)每个学生可以选多个景点,痛击最终最多人数选择的景点
public class Test {
    public static void main(String[] args) {
        //1.记录每个学生的选择
        Map<String, List<String>> selects = new HashMap<>();
        //2.吧学生选择的数据存入进去
        List<String> list = new ArrayList<>();
        Collections.addAll(list,"A","C","D");
        selects.put("马浩楠",list);

        List<String> list1 = new ArrayList<>();
        Collections.addAll(list1,"A","B","D");
        selects.put("程明辉",list1);

        List<String> list2 = new ArrayList<>();
        Collections.addAll(list2,"A","B","C","D");
        selects.put("李泽",list2);

        List<String> list3 = new ArrayList<>();
        Collections.addAll(list3,"A","C","D");
        selects.put("吴赛",list3);
        //3.统计每个景点的人数
        Map<String,Integer> maps = new HashMap<>();
        //4.提取所有人选择的景点信心
        Collection<List<String>> values = selects.values();
        //values = {[A,C,D],[A,B,D],[A,B,C,D]...}
        for (List<String> value : values) {
            for (String s : value) {
                //判断是否出现过这个景点
                if(maps.containsKey(s)){
                    maps.put(s,maps.get(s)+1);
                }else {
                    maps.put(s,1);
                }
            }
        }
        System.out.println(maps);
    }
}

results matching ""

    No results matching ""