day22-集合(泛型、Set、HashSet、LinkedHashSet、TreeSet)

1.泛型

1.1泛型概述

  • 泛型的介绍

    ​ 泛型是JDK5中引入的特性,它提供了编译时类型安全检测机制

  • 泛型的好处

    1. 把运行时期的问题提前到了编译期间
    2. 避免了强制类型转换
  • 泛型的定义格式

    • <类型>: 指定一种类型的格式.尖括号里面可以任意书写,一般只写一个字母.例如:
    • <类型1,类型2…>: 指定多种类型的格式,多种类型之间用逗号隔开.例如: <E,T> <K,V>
      在这里插入图片描述

多学一点:

java中的泛型是伪泛型,只有在编译的阶段检查一下,输入到集合中的元素是否是泛型对应的类型,真正添加进去后,还是认为是Object类型,使用这些数据的时候,强转成对应的泛型
在这里插入图片描述
在这里插入图片描述

泛型的细节:

  • 泛型中不能写基本数据类型,因为要通过Object来接收,基本数据类型,Object类无法接受
  • 指定泛型的具体类型后,传递数据时,可以传入该类类型或者其子类类型,多态
  • 如果不写泛型,类型默认是object

1.2自定义泛型

在这里插入图片描述

1.2.1 泛型类

在这里插入图片描述
自定义一个泛型类:
在这里插入图片描述

1.2.2 泛型方法

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.2.3 泛型接口

在这里插入图片描述

  1. 实现类给出具体类型
    在这里插入图片描述
  2. 实现类延续泛型,创建对象时再确定
    在这里插入图片描述

1.3 泛型的通配符

泛型不具备继承性,但是数据具备继承性
在这里插入图片描述

  • 此时,泛型里面写的是什么类型,那么只能传递什么类型的数据。

    • 弊端:
      利用泛型方法有一个小弊端,此时他可以接受任意的数据类型
      Ye Fu Zi Student
    • 希望:本方法虽然不确定类型,但是以后我希望只能传递Ye Fu Zi
  • 此时我们就可以使用泛型的通配符:

    • ? 也表示不确定的类型
    • 他可以进行类型的限定 * ? extends E: 表示可以传递E或者E所有的子类类型
    • ? super E:表示可以传递E或者E所有的父类类型
    • 应用场景:
      1. 如果我们在定义类、方法、接口的时候,如果类型不确定,就可以定义泛型类、泛型方法、泛型接口。
      2. 如果类型不确定,但是能知道以后只能传递某个继承体系中的,就可以泛型的通配符
        关键点:可以限定类型的范围。
  • extends:可以看到Ye以及Ye的子类型都可以传递进来,但是其他类型不行
    在这里插入图片描述

  • super:可以看到Zi以及Zi的父类型都可以传递进来,但是其他类型不行
    在这里插入图片描述

1.4 泛型综合练习

在这里插入图片描述

						抽象动物
		         __________|__________
		       |                      |
		     抽象狗                 抽象猫
		   ____| ____             ____| ____
		  |          |           |           | 	  
       波斯猫      狸花猫        泰迪       哈士奇

要求1函数就应该写ArrayList<? extends Cat>
要求2函数就应该写ArrayList<? extends Dog>
要求3函数就应该写ArrayList<? extends Animal>

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.Set集合

2.1Set集合概述和特点【应用】

在这里插入图片描述

回顾一下Collection在这里插入图片描述

2.2Set集合的使用【应用】

存储字符串并遍历

public class Main {
    public static void main(String[] args) {

        //1.创建set集合
        Set<Integer> set = new HashSet<>();

        //2.添加元素
        //如果当前元素是第一次添加,那么可以添加成功,返回true
        //如果当前元素是第二次添加,那么添加失败,返回false
        System.out.println(set.add(1));//true
        System.out.println(set.add(2));//true
        System.out.println(set.add(3));//true
        System.out.println(set.add(1));//false

        //3.打印集合
        System.out.println(set);//可能是无序的

        //4.三种方式遍历set
        //4.1 迭代器
        Iterator<Integer> iterator = set.iterator();
        while (iterator.hasNext()){
            int num = iterator.next();
            System.out.println(num);
        }

        //4.2 增强for循环
        for (Integer i : set) {
            System.out.println(i);
        }

        //4.3 lambda表达式
        set.forEach(new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                System.out.println(integer);
            }
        });

        set.forEach(num-> System.out.println(num));
    }
}

在这里插入图片描述

3.HashSet集合

3.1HashSet集合概述和特点【应用】

在这里插入图片描述
在这里插入图片描述

3.2HashSet集合的基本应用【应用】

存储字符串并遍历

public class HashSetDemo {
    public static void main(String[] args) {
        //创建集合对象
        HashSet<String> set = new HashSet<String>();

        //添加元素
        set.add("hello");
        set.add("world");
        set.add("java");
        //不包含重复元素的集合
        set.add("world");

        //遍历
        for(String s : set) {
            System.out.println(s);
        }
    }
}

3.3哈希值【理解】

  • 哈希值简介

    ​ 是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值

  • 如何获取哈希值

    ​ Object类中的public int hashCode():返回对象的哈希码值

  • 哈希值的特点

    • 同一个对象多次调用hashCode()方法返回的哈希值是相同的
    • 默认情况下,不同对象的哈希值是不同的。而重写hashCode()方法,可以实现让不同对象的哈希值相同
  • 如果没有重写hashCode()方法

    public class Student {
        private String name;
        private int age;
    
        public Student() {
        }
    
        public Student(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        /**
         * 获取
         * @return name
         */
        public String getName() {
            return name;
        }
    
        /**
         * 设置
         * @param name
         */
        public void setName(String name) {
            this.name = name;
        }
    
        /**
         * 获取
         * @return age
         */
        public int getAge() {
            return age;
        }
    
        /**
         * 设置
         * @param age
         */
        public void setAge(int age) {
            this.age = age;
        }
    
        public String toString() {
            return "Student{name = " + name + ", age = " + age + "}";
        }
    }
    
    
    public class Main {
        public static void main(String[] args) {
    
            Student stu1 = new Student("张三",19);
            Student stu2 = new Student("张三",19);
            System.out.println(stu1.hashCode());//557041912
            System.out.println(stu2.hashCode());//1134712904
        }
    }
    
  • 如果重写hashCode()方法

    public class Student {
        private String name;
        private int age;
    
        public Student() {
        }
    
        public Student(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        /**
         * 获取
         * @return name
         */
        public String getName() {
            return name;
        }
    
        /**
         * 设置
         * @param name
         */
        public void setName(String name) {
            this.name = name;
        }
    
        /**
         * 获取
         * @return age
         */
        public int getAge() {
            return age;
        }
    
        /**
         * 设置
         * @param age
         */
        public void setAge(int age) {
            this.age = age;
        }
    
        public String toString() {
            return "Student{name = " + name + ", age = " + age + "}";
        }
    
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Student student = (Student) o;
            return age == student.age && Objects.equals(name, student.name);
        }
    
        @Override
        public int hashCode() {
            return Objects.hash(name, age);
        }
    }
    
    public class Main {
        public static void main(String[] args) {
    
            Student stu1 = new Student("张三",19);
            Student stu2 = new Student("张三",19);
            System.out.println(stu1.hashCode());//557041912
            System.out.println(stu2.hashCode());//1134712904
        }
    }
    

3.4哈希表结构【理解】

  • JDK1.8以前

    ​ 数组 + 链表
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述
    加载因子=0.75
    如果数组中包含了18*0.75=12个元素,则会扩充到原来的两倍,变成32个元素

  • JDK1.8以后

    • 节点个数少于等于8个

      ​ 数组 + 链表

    • 节点个数多于8个,且数组长度大于等于64个

      ​ 数组 + 红黑树
      在这里插入图片描述
      在这里插入图片描述

3.5 HashSet的三个问题

  • 问题1:HashSet为什么存和取的顺序不一样?
    因为遍历HashSet的时候,是按照数组的顺序去遍历,遍历到一个数据后,继续遍历之后的链表,这样的遍历顺序和添加顺序是不同的。
    在这里插入图片描述

  • 问题2:HashSet为什么没有索引?
    由于HashSet是由数组、链表、红黑树组成,无法给每个都分配一个索引。

  • 问题2:HashSet是利用什么机制保证数据去重的?
    利用HashCode方法和equals方法确认,如果HashCode相同,则比较equals方法,因此,自定义类一定要重写这两个方法,如果是Stirng,Integer这类java已经写好的,就不用重写这两个方法了。

3.6 小练习

在这里插入图片描述

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }

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

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

public class Main {
    public static void main(String[] args) {

        Student stu1 = new Student("张三",19);
        Student stu2 = new Student("张三",19);
        Student stu3 = new Student("李四",19);
        Student stu4 = new Student("王五",19);

        HashSet<Student> students = new HashSet<>();
        System.out.println(students.add(stu1));//true
        System.out.println(students.add(stu2));//false
        System.out.println(students.add(stu3));//true
        System.out.println(students.add(stu4));//true

        System.out.println(students);//[Student{name = 王五, age = 19}, Student{name = 张三, age = 19}, Student{name = 李四, age = 19}]
    }
}

4.LinkedHashSet集合

在这里插入图片描述

4.1 LinkedHashSet底层原理

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
遍历的时候,直接是从第一个节点开始遍历,沿着双向链表开始遍历。
实现:

public class Main {
    public static void main(String[] args) {

        Student stu1 = new Student("张三",19);
        Student stu2 = new Student("张三",19);
        Student stu3 = new Student("李四",19);
        Student stu4 = new Student("王五",19);

        LinkedHashSet<Student> students = new LinkedHashSet<>();
        System.out.println(students.add(stu1));//true
        System.out.println(students.add(stu2));//false
        System.out.println(students.add(stu3));//true
        System.out.println(students.add(stu4));//true

        System.out.println(students);//[Student{name = 张三, age = 19}, Student{name = 李四, age = 19}, Student{name = 王五, age = 19}]
    }
}

在这里插入图片描述

5.TreeSet集合

5.1TreeSet集合概述和特点【应用】

在这里插入图片描述

  • 不可以存储重复元素
  • 没有索引
  • 可以将元素按照规则进行排序
    • TreeSet():根据其元素的自然排序进行排序
    • TreeSet(Comparator comparator) :根据指定的比较器进行排序

5.2TreeSet集合基本使用【应用】

存储Integer类型的整数并遍历

public class TreeSetDemo01 {
    public static void main(String[] args) {
        //创建集合对象
        TreeSet<Integer> ts = new TreeSet<Integer>();

        //添加元素
        ts.add(10);
        ts.add(40);
        ts.add(30);
        ts.add(50);
        ts.add(20);

        ts.add(30);

        //遍历集合
        for(Integer i : ts) {
            System.out.println(i);
        }
    }
}
10
20
30
40
50

在这里插入图片描述
在这里插入图片描述

5.3 排序

在这里插入图片描述

5.3 自然排序Comparable的使用【应用】

  • 案例需求

    • 存储学生对象并遍历,创建TreeSet集合使用无参构造方法

    • 要求:按照年龄从小到大排序在这里插入图片描述

      import java.util.Objects;
      
      public class Student implements Comparable<Student>{
          private String name;
          private int age;
      
          public Student() {
          }
      
          public Student(String name, int age) {
              this.name = name;
              this.age = age;
          }
      
          /**
           * 获取
           * @return name
           */
          public String getName() {
              return name;
          }
      
          /**
           * 设置
           * @param name
           */
          public void setName(String name) {
              this.name = name;
          }
      
          /**
           * 获取
           * @return age
           */
          public int getAge() {
              return age;
          }
      
          /**
           * 设置
           * @param age
           */
          public void setAge(int age) {
              this.age = age;
          }
      
          public String toString() {
              return "Student{name = " + name + ", age = " + age + "}";
          }
      
      
          @Override
          public int compareTo(Student o) {
              //如果只看年龄来升序排列
              int result = this.getAge()-o.getAge();
              return result;
          }
      }
      
      
      public class Main {
          public static void main(String[] args) {
              TreeSet<Student> students = new TreeSet<>();
              Student stu1 = new Student("zhangsan", 18);
              Student stu2 = new Student("lisi", 17);
              Student stu3 = new Student("wangwu", 19);
      
              System.out.println(students.add(stu1));
              System.out.println(students.add(stu2));
              System.out.println(students.add(stu3));
      
              System.out.println(students);//[Student{name = lisi, age = 17}, Student{name = zhangsan, age = 18}, Student{name = wangwu, age = 19}]
          }
      }
      

5.4 比较器排序Comparator的使用【应用】

在这里插入图片描述

  • 实现步骤

    • 用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序的
    • 比较器排序,就是让集合构造方法接收Comparator的实现类对象,重写compare(T o1,T o2)方法
    • 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
      在这里插入图片描述
  • 代码实现

    public static void main(String[] args) {
        TreeSet<String> strings = new TreeSet<>(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                //按照长度排序
                int result = o1.length() - o2.length();
                //如果一样长则按首字母排序
                result = result == 0 ? o1.compareTo(o2) : result;
                return result;
            }
        });
    
        strings.add("c");
        strings.add("ab");
        strings.add("df");
        strings.add("qwer");
    
        System.out.println(strings);
    }
    

练习题:

在这里插入图片描述

  • 自然排序实现:

    import java.util.Objects;
    
    public class Student implements Comparable<Student>{
        private String name;
        private int age;
    
        private int chinese;
        private int math;
        private int english;
    
        private int sum;
    
        public Student() {
        }
    
        public Student(String name, int age, int chinese, int math, int english) {
            this.name = name;
            this.age = age;
            this.chinese = chinese;
            this.math = math;
            this.english = english;
            countSum();
        }
    
        private void countSum(){
            this.sum = this.chinese+this.math+this.english;
        }
    
        /**
         * 获取
         * @return name
         */
        public String getName() {
            return name;
        }
    
        /**
         * 设置
         * @param name
         */
        public void setName(String name) {
            this.name = name;
        }
    
        /**
         * 获取
         * @return age
         */
        public int getAge() {
            return age;
        }
    
        /**
         * 设置
         * @param age
         */
        public void setAge(int age) {
            this.age = age;
        }
    
        /**
         * 获取
         * @return chinese
         */
        public int getChinese() {
            return chinese;
        }
    
        /**
         * 设置
         * @param chinese
         */
        public void setChinese(int chinese) {
            this.chinese = chinese;
            countSum();
        }
    
        /**
         * 获取
         * @return math
         */
        public int getMath() {
            return math;
        }
    
        /**
         * 设置
         * @param math
         */
        public void setMath(int math) {
            this.math = math;
            countSum();
        }
    
        /**
         * 获取
         * @return english
         */
        public int getEnglish() {
            return english;
        }
    
        /**
         * 设置
         * @param english
         */
        public void setEnglish(int english) {
            this.english = english;
            countSum();
        }
    
        public String toString() {
            return "Student{name = " + name + ", age = " + age + ", chinese = " + chinese + ", math = " + math + ", english = " + english + ", sum="+sum+"}";
        }
    
    
        @Override
        public int compareTo(Student o) {
            //先按照总分排序
            int result = o.sum-this.sum;
            //如果总分相同,则按照语文成绩排序
            result=result==0?o.getChinese()-this.getChinese():result;
            //如果语文也相同,则按照数学成绩排序
            result=result==0?o.getMath()-this.getMath():result;
            //如果数学也相同,则按照英语成绩排序
            result=result==0?o.getEnglish()-this.getEnglish():result;
            //如果英语也相同,则按照年龄排序
            result=result==0?o.getAge()-this.getAge():result;
            //如果年龄也相同,则按照姓名字母顺序排序
            result=result==0?o.getName().compareTo(this.getName()):result;
            return result;
        }
    }
    
    
    import java.util.*;
    import java.util.function.Consumer;
    
    public class Main {
        public static void main(String[] args) {
    
            //1. 创建学生对象
            Student s1 = new Student("zhangsan",18,23,5,6);
            Student s2 = new Student("zhangsan",19,56,34,32);
            Student s3 = new Student("zhangsan",17,3,5,6);
            Student s4 = new Student("zhangsan",17,93,93,93);
    
            //2. 创建集合
            TreeSet<Student> students = new TreeSet<>();
    
            //3. 添加元素
            students.add(s1);
            students.add(s2);
            students.add(s3);
            students.add(s4);
    
            for (Student student : students) {
                System.out.println(student);
            }
        }
    }
    
    
  • 比较器排序实现:

import java.util.Objects;

public class Student{
    private String name;
    private int age;

    private int chinese;
    private int math;
    private int english;

    private int sum;

    public int getSum() {
        return sum;
    }

    public Student() {
    }

    public Student(String name, int age, int chinese, int math, int english) {
        this.name = name;
        this.age = age;
        this.chinese = chinese;
        this.math = math;
        this.english = english;
        countSum();
    }

    private void countSum(){
        this.sum = this.chinese+this.math+this.english;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    /**
     * 获取
     * @return chinese
     */
    public int getChinese() {
        return chinese;
    }

    /**
     * 设置
     * @param chinese
     */
    public void setChinese(int chinese) {
        this.chinese = chinese;
        countSum();
    }

    /**
     * 获取
     * @return math
     */
    public int getMath() {
        return math;
    }

    /**
     * 设置
     * @param math
     */
    public void setMath(int math) {
        this.math = math;
        countSum();
    }

    /**
     * 获取
     * @return english
     */
    public int getEnglish() {
        return english;
    }

    /**
     * 设置
     * @param english
     */
    public void setEnglish(int english) {
        this.english = english;
        countSum();
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + ", chinese = " + chinese + ", math = " + math + ", english = " + english + ", sum="+sum+"}";
    }

}

public class Main {
    public static void main(String[] args) {

        //1. 创建学生对象
        Student s1 = new Student("zhangsan",18,23,5,6);
        Student s2 = new Student("zhangsan",19,56,34,32);
        Student s3 = new Student("zhangsan",17,3,5,6);
        Student s4 = new Student("zhangsan",17,93,93,93);

        //2. 创建集合
        TreeSet<Student> students = new TreeSet<>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                //先按照总分排序
                int result = o2.getSum()-o1.getSum();
                //如果总分相同,则按照语文成绩排序
                result=result==0?o2.getChinese()-o1.getChinese():result;
                //如果语文也相同,则按照数学成绩排序
                result=result==0?o2.getMath()-o1.getMath():result;
                //如果数学也相同,则按照英语成绩排序
                result=result==0?o2.getEnglish()-o1.getEnglish():result;
                //如果英语也相同,则按照年龄排序
                result=result==0?o2.getAge()-o1.getAge():result;
                //如果年龄也相同,则按照姓名字母顺序排序
                result=result==0?o2.getName().compareTo(o1.getName()):result;
                return result;
            }
        });

        //3. 添加元素
        students.add(s1);
        students.add(s2);
        students.add(s3);
        students.add(s4);

        for (Student student : students) {
            System.out.println(student);
        }
    }
}

5.5两种比较方式总结【理解】

  • 两种比较方式小结
    • 自然排序: 自定义类实现Comparable接口,重写compareTo方法,根据返回值进行排序

    • 比较器排序: 创建TreeSet对象的时候传递Comparator的实现类对象,重写compare方法,根据返回值进行排序

    • 在使用的时候,默认使用自然排序,当自然排序不满足现在的需求时(一般都是,已经写好的类按照自然排序已经实现了,但是不满足我们的要求),必须使用比较器排序

    • 如果两种方式都实现了,则以比较器为准

  • 两种方式中关于返回值的规则
    • 如果返回值为负数,表示当前存入的元素是较小值,存左边
    • 如果返回值为0,表示当前存入的元素跟集合中元素重复了,不存
    • 如果返回值为正数,表示当前存入的元素是较大值,存右边

在这里插入图片描述

7. 使用场景

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值