Skip to content

day07 【Collections & Set 集合】

今日内容介绍

java
Collections工具类
增强for循环
泛型
红黑树
Set集合

第一章 Collections 工具类【重点】

1.1 Collections 工具类的基本使用

java
package com.itheima.collections;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;

public class CollectionsDemo01 {
   /*
   Collections 是单列集合的工具类
      特点  构造私有,方法都是静态的,直接通过类名调用。
      T... elements  你想传几个数据 就传几个
   static <T> boolean addAll(Collection<? super T> c, T... elements)
                  将所有指定元素添加到指定 collection 中。
    */
    public static void main(String[] args) {

        ArrayList<String> list = new ArrayList<>();
        // 向集合中添加 多个元素
        Collections.addAll(list,"楚人美","范德彪","乔布斯","马大帅");

        System.out.println("查看集合中的元素:"+list);
        // static void reverse(List<?> list) 反转指定列表中元素的顺序。
        Collections.reverse(list);
        System.out.println("查看集合reverse之后里面的元素:"+list);

       // static void shuffle(List<?> list)  打乱集合中的顺序
        Collections.shuffle(list);
        System.out.println("查看打乱顺序的集合:"+list);

        // static <T> void sort(List<T> list)  T类型一定是可排序,它一定是符合排序操作的
        //                                               要实现一个接口 Comparable  比较接口
        //          根据元素的自然顺序 对指定列表按升序进行排序
        Collections.sort(list);

        System.out.println("查看集合排序之后里面的元素:"+list);



    }
}

1.2 基本的排序与修改排序规则操作

java
package com.itheima.collections;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class CollectionsDemo02 {
    /**
     * 讲解 排序功能
     *         static <T > void sort(List<T> list)
     *           根据元素的自然顺序 对指定列表按升序进行排序
     *              集合里面元素必须实现 Comparable接口
     *              如果没实现不能排序
     *
     * @param args
     */
    public static void main(String[] args) {
         //创建集合
        ArrayList<Integer> list = new ArrayList<>();
        Collections.addAll(list,100,50,20,1000,10,99);

        System.out.println("集合没有排序前:"+list);

        //进行排序
        Collections.sort(list);

        System.out.println("排序后的集合:"+list);
        //默认排序是升序

        //不满意  变成降序?
        /*
          static <T> void sort(List<T> list, Comparator<? super T> c)
          根据指定比较器产生的顺序对指定列表进行排序。
         */
        Collections.sort(list,
                // 这个接口就是体现了对 规则的定义,具体的规则由我们重写实现。
                new Comparator<Integer>() {//实现该接口就不再使用Integer默认排序规则,而是使用新的规则进行排序
                    @Override
                    public int compare(Integer o1, Integer o2) {//重写的排序方法
                        return o2-o1; // 前-后 升序  后-前 降序
                    }
                });

        System.out.println("最后排序后的集合:"+list);
    }
}

1641173757798

1.3 Collections 排序自定义规则按照学生年龄排序

java
package com.itheima.collections;

public class Student {

    private String name;
    private int age;

    public Student() {

    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
java
package com.itheima.collections;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class CollectionsDemo03 {
    public static void main(String[] args) {
        ArrayList<Student> list = new ArrayList<>();

        list.add(new Student("jack",18));
        list.add(new Student("rose",28));
        list.add(new Student("abc",16));

        System.out.println("集合排序前 按照存的顺序:"+list);

        /*
          对集合里面的 学生 进行排序 要求 排序规则 根据学生的 年龄  降序排列!
         */
//        Collections.sort(list);list集合中的元素  student Student类型没有实现Comparable 接口
                                 //                   不具备排序规则
        /*
           怎么让我们的学生对象可以完成排序
              1: 去上Student类型 实现 Comparable接口  comparTo方法 里面去实现 排序的规则。
                  第一种 会把排序规则写死在类中。
              2: 直接使用    Collections.sort(list,Comparator接口)
                 不想把规则写死 ,每次排序我都自定义排序的规则,规则不会影响类在其他地方的使用。
                 需要在 第二参数上 实现该比较接口 即可
                                      ----  匿名内部类  你根据提示回车
         */
        Collections.sort(list, new Comparator<Student>() {
                                //我们现在来给Student 实现一个排序的新规则
            @Override
            public int compare(Student o1, Student o2) {//方法就是实现新的规则的
                // 前-后 升序   后-前 降序
                return o2.getAge()-o1.getAge();//降序  根据学生的 年龄  降序排列!
//                o2.getName().compareTo(o1.getName()) 根据姓名进行降序
            }
        });
        System.out.println("排序后:"+list);
    }
}

1627698838106

第二章 泛型【重点】

2.1 泛型的好处

没有泛型的代码

java
public class GenericDemo01 {

    public static void main(String[] args) {
        Collection coll = new ArrayList();
        //添加元素
        coll.add("itcast");
        coll.add("itheima");
        coll.add(5);//因为加的是Integer类型  自动装箱了

        // 没有泛型默认是 Object类型
        for (Object o : coll) {
            // 将Object类型向下 转型 转换成 String  因为String 可以看字符串的长度
            String s = (String)o;
            System.out.println("字符串的长度:"+s.length());
        }
        /*
        ClassCastException: 类型转换异常,前面两个元素 向下转型成 String,是OK的没毛病。
        第三个元素 是Integer类型,  它和String没关系,不能强制的进行转换。

            这种问题 是不是在运行期才暴露出来。
            编译没有爆出来!!

            把这种问题 提前的解决,这样运行时就没问题了。
            而且我们一般 一个容器中 只存储相同的数据类型。
            如果没有泛型,什么类型都可以存储,因为什么类型都是Object.

             泛型的出现解决了两件事情

             泛型的好处
                1: 将运行期的异常,提前到了编译语法阶段
                2: 避免了强转的麻烦。
                3:一旦指定了泛型,那么集合中数据在编译期就进行了统一。
                4: 泛型可以实现模板化,把数据类型当做参数传递。

         */

    }
}

加了泛型 解决了 问题

java
package com.itheima.generic;

import java.util.ArrayList;
import java.util.Collection;

public class GenericDemo02 {

    public static void main(String[] args) {
        Collection<String> coll = new ArrayList<String>();
        //添加元素
        coll.add("itcast");
        coll.add("itheima");
       // coll.add(5);//因为加的是Integer类型  自动装箱了

        // 没有泛型默认是 Object类型
        for (String o : coll) {

            System.out.println("字符串的长度:"+o.length());
        }


    }
}

泛型的概念

泛型:可以在类或方法中预支地使用未知的类型。(未知 现在不知道 未来会知道)

泛型的应用

泛型可以定义在 类 方法 接口上 等等

作用:相当于一个类的模板

1641116161709

java
package com.itheima.generic;

import java.util.ArrayList;

public class GenericDemo03 {
    /**
     * 泛型是什么?
     *       存在于源码阶段,它表示不确定的类型,在我们看来就是一个未知,未来(使用)会得知的类型。
     *     特征:
     *        将来 传什么是什么!
     *      定义在哪:
     *          类  方法  接口上  等等
     *        public class ArrayList<E>  String
     *
     *               add(E e)  String
     *
     *             Iterator<E> iterator()
            定义的时候 确定了 泛型类型吗?
     *
     *        E 只是一个代称  在 ArrayList这个E 代表 集合元素的类型,但是是什么不知道。
     *          现在源代码阶段 有没有具体的 E 类型 ,没有的

     *
     *       泛型是一个  未知的类型,未来的类型
     *          特征: 将来 传什么 是什么。
             ArrayList<String> list = new ArrayList<>();
             list.add(String s)

     *
     *             将来使用的时候 一定是一个具体的类型。
     *
     *         为什么定义这个呢?
     *             因为 在一些操作过程中,我们会遇到不确定的类型,这种不确定的类型,在源码阶段  就先使用 泛型替代。
     *             需要注意的是 一旦程序运行了,泛型作用也就失效了。--- 泛型在运行期是被擦除的。
     */
    public static void main(String[] args) {
             ArrayList<Integer>  list = new ArrayList<>();


    }
}

2.2 泛型类的定义【重点】

定义泛型类的目的

​ 1:创建一个模板,减少了相似类的创建。---高内聚的体现。

​ 2: 在我不确定具体使用什么的时候,我可以先以泛型形式,在使用的确定。

研究

定义格式:

java
修饰符 class 类名<代表泛型的字符串>{}

比如:
public class 类名<T> {
    ...
}

什么时候确定泛型的具体类型:

​ 在创建对象的时候确定泛型。

java
package com.itheima.generic02;

/**
 *  定义格式:
 *      修饰符  class 类名<>{}
 *      不要写具体的类型。
 *         泛型在定义的时候不具体,在使用的时候才变得具体。
 *
 *    1:那写什么呀,
 *       随便,官方用到过E  在 集合中 ArrayList<E>  E 是 element的缩写,因为定义这个泛型的含义 是存储到集合中的元素。
 *    2:尖括号中可以写几个
 *        需要几个写几个。
 *       但是建议
 *         见名知意
 *
 *
 */
public class MyClass01<MVP>{//MVP代表未知,将来传什么就是什么
    //是因为 你在使用类的时候 这个MVP 一定是一个具体的类型了,
    private MVP mvp;

    public MVP getMVP(){
        return mvp;
    }

    public void setMVP(MVP mvp){
        this.mvp=mvp;
    }
}
java
package com.itheima.generic;

public class GenericDemo03Class {
    public static void main(String[] args) {
        //泛型定义在类 创建对象的时候确定
        //使用时候给具体类型
        MyClass01<String> myclass = new MyClass01<>();
        myclass.setMvp("aaa");
        String mvp = myclass.getMvp();

        MyClass01<Integer> myclass2 = new MyClass01<>();

        myclass2.setMvp(1);
        Integer mvp1 = myclass2.getMvp();

    }
}

2.3 泛型方法

泛型方法定义格式:

java
 修饰符 <定义方法上的泛型> 返回值 方法名(参数列表){}
     刚刚在方法上定义的泛型,可以在方法的参数列表上使用。
  举例:
    修饰符 static <M> 返回值类型 方法名称(M t) {
        ...
    }
还可以用在返回值上

什么时候确定泛型:

​ 调用方法的时候,传什么类型,泛型就是什么类型。

java
package com.itheima.generic02;

public class MyClass02<T> {//泛型定义在类上面

    // 此方法上的泛型 是类上面定义的 是创建对象的时候就已经确定了。
    public void print(T t ){
        System.out.println(t);
    }

    /**
     *  泛型定义在  方法上的格式
     *      修饰符 <定义方法上的泛型> 返回值 方法名(参数列表){}
     *
     *
     */

    public <E> void show(E e){
        System.out.println("获取泛型的具体类型:"+e.getClass());
    }
}
package com.itheima.generic02;

import com.itheima.collections.Student;

public class Demo02 {

    public static void main(String[] args) {

        MyClass02<String> myClass02 = new MyClass02<>();


        myClass02.show(new Student());
    }
}

2.4 泛型接口

泛型: jdk1.5 添加的新特性

泛型接口: 看 MyClass05 定义接口时,该接口中需要处理某种类型的数据,但是什么类型,不确定,所以定义成泛型 泛型变量一般用大写字母表示: T(Type),E(Element),K(Key),V(Value)

定义接口的定义格式:

java
    public interface 接口名称<泛型变量> {
        ...
    }
    举例:
    public interface 接口名称<T> {
        ...
    }

接口上定义的泛型,什么时间确定具体的类型呢?

​ 1.实现类实现接口时,确定接口上泛型的具体类型的话, ​ 直接指定具体类型

java
public class MyClass03 implements MyInter<Integer>{

2.定义实现类时,也不确定接口上的泛型 该实现类必须定义为泛型类 而且实现类上的泛型和接口上的泛型要保持一致 创建实现类对象时,确定具体的类型

java
/**
 * 现在有个接口
 *    泛型定义格式
 *         修饰符 interface 接口名<泛型>{}
 */

public interface MyInter01<T> {

    public abstract  void add(T t);
}
java
package com.itheima.generic02;
/*
    泛型定义在接口上
      什么时候 确定泛型类型呢
        1:定义类实现接口的时候,确定。
 */
public class MyInterImplA implements MyInter01<String> {


    @Override
    public void add(String s) {

    }
}
java
package com.itheima.generic02;
/*
    泛型定义在接口上
      什么时候 确定泛型类型呢
        1:定义类实现接口的时候,确定。
        2: 定义类实现接口的时候,没有确定,
             那么就在 创建对象的时候完成确定泛型类型。
 */
public class MyInterImplB<T> implements MyInter01<T> {


    @Override
    public void add(T t) {

    }
}
java
package com.itheima.generic02;

import java.util.ArrayList;

public class Demo03 {

    public static void main(String[] args) {
        MyInterImplB<String> myInterImplB = new MyInterImplB<>();

        ArrayList<String> list = new ArrayList<>();
    }
}

2.5 泛型通配符

概述

我们在定义泛型类,泛型方法,泛型接口的时候经常会碰见很多不同的通配符,比如 T,E,K,V 等等,这些通配符又都是什么意思呢?

本质上这些个都是通配符,没啥区别,只不过是编码时的一种约定俗成的东西。比如上述代码中的 T ,我们可以换成 A-Z 之间的任何一个 字母都可以,并不会影响程序的正常运行,但是如果换成其他的字母代替 T ,在可读性上可能会弱一些。通常情况下,T,E,K,V,?是这样约定的:

  • ?表示不确定的 java 类型

  • T (type) 表示具体的一个 java 类型

  • K V (key value) 分别代表 java 键值中的 Key Value

  • E (element) 代表 Element

无界通配符

接下来讲一个简单的使用,但实际上它是用于进行边界处理的。

java
package com.itheima.generic03;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class Demo {

    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("abc");
        list.add("xyz");

        ArrayList<Integer> list2 = new ArrayList<>();
        list2.add(100);
        list2.add(200);

        printList(list);
        printList(list2);

        LinkedList<String> list3 = new LinkedList<>();
        list.add("abc");
        list.add("xyz");
        printList(list3);
    }

    //定义一个方法  遍历打印集合中的数据

    /**
     *   如果 这里写了泛型
     *      泛型  这块应该写什么?
     *
     *      在方法参数上,如果不确定 将来传递的泛型是什么类型
     *        1:不写泛型,去泛型化。
     *        2:另外一种 使用 通配符 ? 来表示 未知
     *          ?叫做 未知通配符
     *            未知通配符方法中的代码 也是按照默认Object接收。
     *
     *          ArrayList<String> list
     *           ArrayList<Integer> list2
     *            LinkedList<String> list3
     *
     *            类型
     *               ArrayList<String>     整体是一个类型
     *               ArrayList<Integer>    整体是一个类型
     *
     *               List<String>  list= new  ArrayList<String>()
     *               List<Integer>  list= new  ArrayList<Integer>()
     *               List<Object>  list= new  ArrayList<Object>()
     *
     *               下面两种是错误的!!
     *               List<Object>  list= new  ArrayList<String>()
     *               ArrayList<Object>  list= new  ArrayList<String>()
     */
    public static void printList(List<?> list){

        for (Object o : list) {
            System.out.println(o);
        }
    }
}

2.6 泛型的上限(上界通配)

?extends 概念:

  • 用 extends 关键字声明,表示参数化的类型可能是所指定的类型,或者是此类型的子类。
  • 用法: < ? extends E>

相当于设置了泛型向上的界限,最大是 E 类型,还可以是其子类。

注意:

  • 如果传入的类型不是 E 或者 E 的子类,编译不成功
  • 泛型中可以使用 E 的方法,要不然还得强转成 E 才能使用
java
/*
    父类: Person
    子类: Worker
    子类: Teacher
    子类: JavaTeacher

        一个父类的子类可以有任意多个,如何表示出一个父类的任意子类呢?
        ?: 任意一种引用类型
        ? extends Person: 表示Person类型或者Person类型的任意子类型
        ? extends E: 表示E类型或者E类型的任意子类型


    泛型的上限:
        ? extends Person: 表示Person类型或者Person类型的任意子类型
        ? extends E: 表示E类型或者E类型的任意子类型
*/
java
package com.itheima.generic03;

import java.util.ArrayList;
import java.util.List;

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

        ArrayList<Person> list1 = new ArrayList<>();
        list1.add(new Person("zs", 18));
        list1.add(new Person("ls", 28));
        list1.add(new Person("ww", 38));

        ArrayList<Worker> list2 = new ArrayList<>();
        list2.add(new Worker("zs01", 18));
        list2.add(new Worker("ls01", 28));
        list2.add(new Worker("ww01", 38));

        ArrayList<Teacher> list3 = new ArrayList<>();
        list3.add(new Teacher("zs02", 18));
        list3.add(new Teacher("ls02", 28));
        list3.add(new Teacher("ww02", 38));

        ArrayList<String> list4 = new ArrayList<>();
        list4.add("aaa");
        list4.add("bbb");

        ArrayList<Integer> list5 = new ArrayList<>();
        list5.add(100);
        list5.add(200);


        ArrayList<Object> list6 = new ArrayList<>();
        list6.add(new Object());

        printList(list1);
        printList(list2);
        printList(list3);
//        printList(list4);
//        printList(list5);
//        printList(list6);
    }
    /*
       打印方法
           设定泛型的上限!
              设置 最大的
                需求  只允许 集合里面的类型是  Person以及Person的子类

                Person的子类
                  extends Person
     */
    public static void printList(List<? extends Person> list){

        for (Object o : list) {
            System.out.println(o);
        }
    }
}

2.7 泛型的下限(下界通配)

? super 概念:

  • 用 super 进行声明,表示参数化的类型可能是所指定的类型,或者是此类型的父类型,直至 Object。
  • 用法: < ? super E>

相当于设置了泛型向下的界限,最小是 E 类型,还可以是其父类。

注意:

  • 在类型参数中使用 super 表示这个泛型中的参数必须是 E 或者 E 的父类。
java
/*
    //使用3.7中定义的Person/Worker/Teacher/JavaTeacher类
    子类: JavaTeacher
    父类: Teacher
    父类: Worker
    父类: Person

        一个子类的父类可以有任意多个,如何表示出一个子类的任意父类呢?
        ?: 任意一种引用类型
        ? super JavaTeacher: 表示JavaTeacher类型或者JavaTeacher类型的任意父类型
        ? super E: 表示E类型或者E类型的任意父类型


    泛型的下限:
        ? super JavaTeacher: 表示JavaTeacher类型或者JavaTeacher类型的任意父类型
        ? extends E: 表示E类型或者E类型的任意父类型
*/
java
package com.itheima.generic03;

import java.util.ArrayList;
import java.util.List;

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

        ArrayList<Person> list1 = new ArrayList<>();
        list1.add(new Person("zs", 18));
        list1.add(new Person("ls", 28));
        list1.add(new Person("ww", 38));

        ArrayList<Worker> list2 = new ArrayList<>();
        list2.add(new Worker("zs01", 18));
        list2.add(new Worker("ls01", 28));
        list2.add(new Worker("ww01", 38));

        ArrayList<Teacher> list3 = new ArrayList<>();
        list3.add(new Teacher("zs02", 18));
        list3.add(new Teacher("ls02", 28));
        list3.add(new Teacher("ww02", 38));

        ArrayList<String> list4 = new ArrayList<>();
        list4.add("aaa");
        list4.add("bbb");

        ArrayList<Integer> list5 = new ArrayList<>();
        list5.add(100);
        list5.add(200);


        ArrayList<Object> list6 = new ArrayList<>();
        list6.add(new Object());

        printList(list1);
//        printList(list2);
        printList(list3);
//        printList(list4);
//        printList(list5);
        printList(list6);
    }
    /*
       打印方法
          设置泛型的下限
              设置最小  传的参数类型只能比它大

              最小设置  Teacher
                 最小是Teacher 里面可以填 Teacher以及Teacher的父类
                                                   super来表示父类引用

                ? super Teacher 泛型只能传Teacher 以及 Teacher的父类
     */
    public static void printList(List<? super Teacher> list){

        for (Object o : list) {

            System.out.println(o);
        }
    }
}

第三章 红黑树数据结构【了解】

1627721015340

java
红黑树并不追求“完全平衡”——它只要求部分地达到平衡要求,降低了对旋转的要求,从而提高了性能。
红黑树能够以O(log2n)的时间复杂度进行搜索、插入、删除操作。
此外,由于它的设计,任何不平衡都会在三次旋转之内解决。
当然,还有一些更好的,但实现起来更复杂的数据结构 能够做到一步旋转之内达到平衡,
但红黑树能够给我们一个比较“便宜”的解决方案。

第四章 Set 集合

4.1 Set 集合的特点

java
 * set集合特点
    元素唯一:不能重复  (equals方法不为true)
   无序:为了保证元素的唯一,所以没有关系是否有序。
      不保证存取顺序一致。

4.2 Set 集合的基本使用

java
package com.itheima.set;

        import java.util.HashSet;

public class HashSetDemo {
    /**
     * set集合特点
     *      元素唯一    什么是唯一?字符串内容唯一。
     *        一会儿研究  为什么元素会唯一。
     *      无序:存取顺序不一致。
     *
     */
    public static void main(String[] args) {
        //创建 set集合对象
        HashSet<String>  set = new HashSet<>();
        //存数据
        set.add("nba");//进行hacode计算  108845  存
        set.add("cba");// 进行hacode计算 98274    存
        set.add("cca");//98305    存
        set.add("cba");// 进行hacode计算  98274    哈希冲突  进行 equals比较  true 不存
        set.add(new String("cba"));//98274    哈希冲突  进行 equals比较  true 不存
        set.add("cuba");//存 不一样 109999

        ;

        System.out.println("集合的长度:"+set.size());
        //遍历
        for (String s : set) {
            System.out.println(s);
        }
    }
}

4.3 hashCode 方法

1627722951359

4.5 哈希表的结构

1627720177716

1627720188580

总结:

java
 1.哈希值不同,能否说明内容一定不同?
            肯定的,必须的
  2.哈希值相同,能否说明内容一定相同?
            不能的
                继续调用equals方法
                    返回false: 内容不相同
                    返回true: 内容相同
改口:
    以前调用toString方法,说返回的是对象的地址值
    但本质是对象的哈希值

4.6 HashSet 存储元素的原理

java
/*
    HashSet集合存储元素的过程
        1.计算哈希值,使用哈希值%数组长度,计算在数组中存储的索引
        2.判断该索引下是否有元素
        3.没有元素: 直接存储
        4.如果有元素:
            调用equals方法
            true: 不存储
            false: 存储

    HashSet集合保证元素唯一: 依赖hashCode方法和equals方法
    要求:
        HashSet集合存储对象所属的类要覆盖重写hashCode方法和equals方法
 */
public class Demo06HashSet {
    public static void main(String[] args) {
        HashSet<String> set = new HashSet<>();
        set.add("abc");//96354
        set.add("重地");//1179395
        set.add("通话");//1179395
        set.add("abc");//96354
        System.out.println("abc".hashCode());
        System.out.println("重地".hashCode());
        System.out.println("通话".hashCode());

        System.out.println(96354%16);
        System.out.println(1179395%16);
    }
}

1601130808210

4.7 HashSet 存储自定义对象

java
package com.itheima.set;

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;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    @Override
    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);//重写的哈希地址值 是根据  对象中 name 属性 和age属性 进行计算而来的 !!跟原来的new的地址没关系
    }

    @Override
    public int compareTo(Student other) {
        return this.age-other.age;// this 当前的学生对象  other 其他的学生对象  当前和其他进行比较 如果是正数 就交换位置
    }
}
java
package com.itheima.set;

import java.util.HashSet;

public class Demo {
    public static void main(String[] args) {
        //几个学生对象
        Student s1 = new Student("潘子",61);
        Student s2 = new Student("嘎子",31);
        Student s3 = new Student("柳岩",36);
        Student s4 = new Student("潘子",61);


        HashSet<Student> set = new HashSet<>();

        set.add(s1);//0x11  存   更新之后 根据name和age进行一轮哈希运算 得到的结果  28118022  存
        set.add(s2);//0x22  存   21896478 存
        set.add(s3);//0x33  存    26309519  存
        set.add(s4);//0x44  存   28118022  出现了哈希碰撞 比较 属性成员 name一样 age一样 返回true  不添加

        System.out.println("set集合长度:"+set.size());

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

    }
}

4.8 LinkedHashSet 集合的使用

java
package com.itheima.set;

        import java.util.HashSet;
        import java.util.LinkedHashSet;
        import java.util.LinkedList;

public class LinkedHashSetDemo {

    public static void main(String[] args) {
        //创建 set集合对象
        LinkedHashSet<String> set = new LinkedHashSet<>();
        //存数据
        set.add("nba");
        set.add("cba");
        set.add("cca");
        set.add(new String("cba"));
        set.add("cuba");//存 不一样 109999

        ;

        System.out.println("集合的长度:"+set.size());
        //遍历
        for (String s : set) {
            System.out.println(s);
        }
    }
}

Released under the MIT License.