数组VS集合

数组(长度开始时必须指定,而且一旦指定,不能更改)(增加/删除元素比较麻烦)

1)长度开始时必须指定,而且一旦指定,不能更改 2)保存的必须为同一类型的元素

3)使用数组进行增加/删除元素比较麻烦

【零基础 快速学Java】韩顺平 零基础30天学会Java--- 集合(2024JavaReview)_开发语言

集合(动态保存任意多个对象)(增加/删除元素比较方便)

1)可以动态保存任意多个对象,使用比较方便!
2)提供了一系列方便的操作对象的方法:add、remove、set、get等
3)使用集合添加/删除新元素很简洁

集合的框架体系

【零基础 快速学Java】韩顺平 零基础30天学会Java--- 集合(2024JavaReview)_List_02

List 接口、Set接口 ( 他们的实现子类都是单列集合

【零基础 快速学Java】韩顺平 零基础30天学会Java--- 集合(2024JavaReview)_ci_03

Map 接口(实现子类是双列集合,存放的 K-V

【零基础 快速学Java】韩顺平 零基础30天学会Java--- 集合(2024JavaReview)_java_04

线程安全不安全介绍

线程不安全

ArrayList, LinkedList,
HashSet, LinkedHashSet,TreeSet
HashMap , LinkedHashMap,TreeMap

线程安全

内置

Vector
Hashtable

并发包

CopyOnWriteArrayList
CopyOnWriteArraySet
ConcurrentHashMap

通过包装

Collections.synchronizedList
Collections.synchronizedSet
Collections.synchronizedMap

Collection 接口和常用方法

Collection 接口实现类的特点

【零基础 快速学Java】韩顺平 零基础30天学会Java--- 集合(2024JavaReview)_开发语言_05

Collection 接口常用方法,以实现子类 ArrayList 来演示

增 add,addAll

删 remove(元素|或下标)

查 contains

大小容量 size,isEmpty

清空 clear

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

/**
 * @创建人 wdl
 * @创建时间 2024/9/6
 * @描述
 */
public class CollectionMethod {
    @SuppressWarnings({"all"})
    public static void main(String[] args) {
        List list = new ArrayList();
        //	add:添加单个元素
        list.add("jack");
        list.add(10);//list.add(new Integer(10))
        list.add(true);
        System.out.println("list=" + list);

        //remove:删除指定元素
        //list.remove(0);//删除第一个元素
        list.remove(true);//指定删除某个元素
        System.out.println("list=" + list);
        
        
        //contains:查找元素是否存在
        System.out.println(list.contains("jack"));//T
        
        //size:获取元素个数
        System.out.println(list.size());//2
        
        //isEmpty:判断是否为空
        System.out.println(list.isEmpty());//F
        
        //clear:清空
        list.clear();
        System.out.println("list=" + list);
        
        //addAll:添加多个元素
        ArrayList list2 = new ArrayList();
        list2.add("红楼梦");
        list2.add("三国演义");
        list.addAll(list2);
        System.out.println("list=" + list);
        
        //containsAll:查找多个元素是否都存在
        System.out.println(list.containsAll(list2));//T
        
        //removeAll:删除多个元素
        list.add("聊斋");
        list.removeAll(list2);
        System.out.println("list=" + list);//[聊斋]


    }
}

Collection 接口遍历元素方式 1 使用 Iterator(迭代器) (lterator 仅用于遍历集合,lterator 本身并不存放对象)(在调用iterator.next()方法之前必须要调用iterator.hasNext()进行检测。若不调用,且下一条记录无效,直接调用it.next()会抛出NoSuchElementException异常)

【零基础 快速学Java】韩顺平 零基础30天学会Java--- 集合(2024JavaReview)_System_06


1)lterator对象称为选代器,主要用于遍历 Collection 集合中的元素

2)所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了lterator接口的对象,即可以返回一个选代器

3)lterator 的结构,[看一张图]

4)lterator 仅用于遍历集合,lterator 本身并不存放对象

【零基础 快速学Java】韩顺平 零基础30天学会Java--- 集合(2024JavaReview)_List_07


提示:在调用iterator.next()方法之前必须要调用iterator.hasNext()进行检测。若不调用,且下一条记录无效,直接调用it.next()会抛出NoSuchElementException异常。

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

/**
 * @创建人 wdl
 * @创建时间 2024/9/6
 * @描述
 */
public class CollectionIterator {
    @SuppressWarnings({"all"})
    public static void main(String[] args) {


        Collection col = new ArrayList();


        col.add(new Book("三国演义", "罗贯中", 10.1));
        col.add(new Book("小李飞刀", "古龙", 5.1));
        col.add(new Book("红楼梦", "曹雪芹", 34.6));


        //System.out.println("col=" + col);
        //现在老师希望能够遍历 col 集合
        //1. 先得到 col 对应的 迭代器
        Iterator iterator = col.iterator();
        //2. 使用 while 循环遍历
        while (iterator.hasNext()) {//判断是否还有数据
            //返回下一个元素,类型是 Object
        	Object obj = iterator.next();
        	System.out.println("obj=" + obj);
        }

        //3. 当退出 while 循环后 , 这时 iterator 迭代器,指向最后的元素
        //	iterator.next();//NoSuchElementException

        //4. 如果希望再次遍历,需要重置我们的迭代器
        iterator = col.iterator();
        System.out.println("===第二次遍历===");
        while (iterator.hasNext()) {
            Object obj = iterator.next();
            System.out.println("obj=" + obj);

        }


    }
}


class Book {
    private String name;
    private String author;
    private double price;

    public Book(String name, String author, double price) {
        this.name = name;
        this.author = author;
        this.price = price;
    }

    public String getName() {
        return name;
    }


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


    public String getAuthor() {
        return author;
    }


    public void setAuthor(String author) {
        this.author = author;
    }


    public double getPrice() {
        return price;
    }


    public void setPrice(double price) {
        this.price = price;
    }


    @Override
    public String toString() {
        return "Book{" +
                "name='" + name + '\'' +
                ", author='" + author + '\'' + ", price=" + price +
                '}';
    }
}

Collection 接口遍历对象方式 2 for 循环增强(增强for就是简化版的iterator本质一样

增强for循环,可以代替iterator迭代器,特点:增强for就是简化版的iterator本质一样。只能用于遍历集合或数组。
基本语法

for(元素类型 元素名:集合名或数组名){
	访问元素
}

【零基础 快速学Java】韩顺平 零基础30天学会Java--- 集合(2024JavaReview)_ci_08

/**
 * @创建人 wdl
 * @创建时间 2024/9/6
 * @描述
 */

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

public class CollectionExercise {
    @SuppressWarnings({"all"})
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add(new Dog("小黑", 3));
        list.add(new Dog("大黄", 100));
        list.add(new Dog("大壮", 8));


        //先使用 for 增强
        for (Object dog : list) {
            System.out.println("dog=" + dog);
        }


        //使用迭代器
        System.out.println("===使用迭代器来遍历===");
        Iterator iterator = list.iterator();
        while (iterator.hasNext()) {
            Object dog = iterator.next();
            System.out.println("dog=" + dog);

        }


    }
}

/**
 * 创建	3 个 Dog {name, age}	对象,放入到 ArrayList 中,赋给 List 引用
 * 用迭代器和增强 for 循环两种方式来遍历
 * 重写 Dog 的 toString 方法, 输出 name 和 age
 */
class Dog {
    private String name;
    private int age;

    public Dog(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 "Dog{" +
                "name='" + name + '\'' + ", age=" + age +
                '}';
    }
}

List 接口和常用方法(有序且可重复,支持添加null,支持索引)(只能通过set方法进行修改)(下列所有方法对其实现类通用)

1)List集合类中元素有序(即添加顺序和取出顺序一致)、且可重复 2)List集合中的每个元素都有其对应的顺序索引,即支持索引

3) List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素。

4) JDK API中List接口的实现类有:

【零基础 快速学Java】韩顺平 零基础30天学会Java--- 集合(2024JavaReview)_java_09

增,boolean add(E e)、void add(int index, E element)、boolean addAll(int index, Collection eles):从 index 位置开始将 eles 中的所有元素添加进来

删,Object remove(int index):移除指定 index 位置的元素,并返回此元素,boolean remove(Object o);

改,Object set(int index, Object ele):设置指定 index 位置的元素为 ele , 相当于是替换.

查,Object get(int index):获取指定 index 位置的元素;int indexOf(Object obj):返回 obj 在集合中首次出现的位置,int lastIndexOf(Object obj):返回 obj 在当前集合中末次出现的位置

插,void add(int index, E element)

截取,List subList(int fromIndex, int toIndex):返回从 fromIndex 到 toIndex 位置的子集合 [fromIndex,toIndex)

其他 , Collection 接口中的方法

三种遍历方式(1.使用iterator 2.使用增强for 3.使用普通for)

【零基础 快速学Java】韩顺平 零基础30天学会Java--- 集合(2024JavaReview)_List_10

ArrayList 底层结构和源码分析(由数组来实现数据存储的)(ArrayList 基本等同于Vector,但是其线程不安全(没有额外的同步机制)(执行效率高))(无参 0,10,1.5倍)(有参 指定,1.5倍)

  1. ArrayList中维护了一个Object类型的数组elementData.
    ArrayList 实现了 Serializable 接口,所以它本身是可以序列化的,但ArrayList 的设计者选择将这个数组声明为 transient,这样在序列化时不会直接序列化整个数组,而是通过自定义的方法只序列化实际存储的元素,这样可以节省空间 transient Object[l elementData; //transient 表示瞬间,短暂的,表示该属性不会被序列化
  2. 当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0第1次添加,则扩容elementData为10,如需要再次扩容,则扩容elementData为1.5倍
  3. 如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容则直接扩容elementData为1.5倍。

Vector(用的少) 底层结构和源码分析(底层也是一个对象数组)(线程同步的,线程安全)(无参 10,2倍)(有参 指定,2倍)

  1. Vector类的定义说明
public class Vector<E>
    extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable
  1. Vector底层也是一个对象数组
protected Object[]elementData;
  1. Vector 是线程同步的,即线程安全Vector类的操作方法带有synchronized
public synchronized E get(int index) {
        if (index >= elementCount)
            throw new ArrayIndexOutOfBoundsException(index);

        return elementData(index);
    }
  1. 当创建Vector对象时,如果使用的是无参构造器,则初始elementData容量为10,如需要扩容,则扩容elementData为2倍
  2. 如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容则直接扩容elementData为2倍。
import java.util.Vector;

/**
 * @创建人 wdl
 * @创建时间 2024/9/7
 * @描述
 */
@SuppressWarnings({"all"})
public class Vector_ {
    public static void main(String[] args) {
        //无参构造器
        //有参数的构造
        Vector vector = new Vector(8);
        for (int i = 0; i < 10; i++) {
            vector.add(i);
        }
        vector.add(100);
        System.out.println("vector=" + vector);
//解读源码
//1. new Vector() 底层
/*
public Vector() { this(10);
}
补充:如果是 Vector vector = new Vector(8);
走的方法:
public Vector(int initialCapacity) { this(initialCapacity, 0);
}
2.vector.add(i)
2.1//下面这个方法就添加数据到 vector 集合
public synchronized boolean add(E e) { modCount++; ensureCapacityHelper(elementCount + 1); elementData[elementCount++] = e;
return true;
}
2.2//确定是否需要扩容 条件 : minCapacity - elementData.length>0 private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
2.3 //如果 需要的数组大小 不够用,就扩容 , 扩容的算法
//newCapacity = oldCapacity + ((capacityIncrement > 0) ?
//	capacityIncrement : oldCapacity);
//就是扩容两倍.
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0) newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
*/


    }
}

LinkedList 底层结构和源码分析(底层维护了一个双向链表,而且维护了两个属性first和last分别指向首节点和尾节点,添加和删除效率较高)(线程不安全(没有同步机制))

  1. LinkedList底层维护了一个双向链表.
  2. LinkedList中维护了两个属性first和last分别指向 首节点和尾节点
  3. 每个节点(Node对象),里面又维护了prev、next、item三个属性,其中通过prev指向前一个,通过next指向后一个节点。最终实现双向链表.
  4. 所以LinkedList的元素的添加和删除,不是通过数组完成的,相对来说效率较高

模拟一个简单的双向链表

/**
 * @创建人 wdl
 * @创建时间 2024/9/7
 * @描述
 */
public class LinkedList01 {
    public static void main(String[] args) {
        //模拟一个简单的双向链表
        
        Node jack = new Node("jack");
        Node tom = new Node("tom");
        Node hsp = new Node("老韩");

        //连接三个结点,形成双向链表
        //jack -> tom -> hsp
        jack.next = tom;
        tom.next = hsp;
        //hsp -> tom -> jack
        hsp.pre = tom;
        tom.pre = jack;

        Node first = jack;//让 first 引用指向 jack,就是双向链表的头结点
        Node last = hsp; //让 last 引用指向 hsp,就是双向链表的尾结点

        //演示,从头到尾进行遍历 System.out.println("===从头到尾进行遍历===");
        while (true) {
            if (first == null) {
                break;
            }
            //输出 first 信息
            System.out.println(first);
            first = first.next;
        }


        //演示,从尾到头的遍历 System.out.println("====从尾到头的遍历====");
        while (true) {
            if (last == null) {
                break;
            }
            //输出 last 信息
            System.out.println(last);
            last = last.pre;
        }


        //演示链表的添加对象/数据,是多么的方便
        //要求,是在 tom --------- 老韩直接,插入一个对象 smith


        //1. 先创建一个 Node 结点,name 就是 smith
        Node smith = new Node("smith");
        //下面就把 smith 加入到双向链表了
        smith.next = hsp;
        smith.pre = tom;
        hsp.pre = smith;
        tom.next = smith;

        //让 first 再次指向 jack
        first = jack;//让 first 引用指向 jack,就是双向链表的头结点


        System.out.println("===从头到尾进行遍历===");
        while (true) {
            if (first == null) {
                break;
            }
            //输出 first 信息
            System.out.println(first);
            first = first.next;
        }


        last = hsp; //让 last 重新指向最后一个结点
        //演示,从尾到头的遍历
        System.out.println("====从尾到头的遍历====");
        while (true) {
            if (last == null) {
                break;
            }
            //输出 last 信息
            System.out.println(last);
            last = last.pre;
        }


    }
}


//定义一个 Node 类,Node 对象 表示双向链表的一个结点
class Node {
    public Object item; //真正存放数据
    public Node next; //指向后一个结点
    public Node pre; //指向前一个结点

    public Node(Object name) {
        this.item = name;
    }

    @Override
    public String toString() {
        return "Node name=" + item;
    }
}

Set 接口和常用方法(无序不可重复,支持添加null( TreeSet 需要对元素进行排序,不能存储null),不支持索引)(注意:添加的顺序和取出的顺序不一致,但是他是固定的)(下列所有方法对其实现类通用)

【零基础 快速学Java】韩顺平 零基础30天学会Java--- 集合(2024JavaReview)_ci_11

和 List 接口一样, Set 接口也是 Collection 的子接口,因此,常用方法和 Collection 接口一样

增,boolean add(E e)、、boolean addAll(Collection eles):将 eles 中的所有元素添加进来

删,boolean remove(Object o);

其他 Collection 接口中的方法

两种遍历方式(1.使用iterator 2.使用增强for )(不能使用索引的方式来获取

HashSet 底层结构和源码分析(HashSet实际上是HashMap,HashMap底层是(数组+链表/红黑树))(0,16(看阈值,为其0.75,实际12时候要扩容),2倍)(与hashCode() 和 equals() 息息相关)(O(1))

public HashSet() {
        map = new HashMap<>();
    }

添加元素原理(附面试题)

【零基础 快速学Java】韩顺平 零基础30天学会Java--- 集合(2024JavaReview)_java_12

  1. 添加一个元素时,先得到hash值 一会转成-> 索引值
  2. 找到存储数据表table,看这个索引位置是否已经存放的有元素
  3. 如果没有,直接加入
  4. 如果有 , 调用 equals 比较逐个比较,如果有相同,就放弃添加,全部比较完都不相同,则添加到最后
经典面试问题:字符串加入 HashSet(String 类已经重写了 hashCode() 和 equals() 方法,两个内容相同的字符串对象被认为是相等的)
set.add(new String("hsp")); // ok
set.add(new String("hsp")); // won't add

String 类已经重写了 hashCode() 和 equals() 方法,两个内容相同的字符串对象被认为是相等的。因此,虽然两个 String 对象是不同的实例,但由于它们的值相同,所以第二次添加 “hsp” 时,HashSet 判断它已经存在,因此不会再次添加。

扩容机制(太大了转化成红黑树)

  1. HashSet底层是HashMap,第一次添加时,table 数组扩容到 16,临界值(threshold)是 16*加载因子(loadFactor)是0.75 =12
  2. 如果table 数组使用到了临界值 12,就会扩容到 16*2 =32,新的临界值就是32*0.75 =24,依次类推

转成红黑树机制

注意:在Java8中,如果一条链表的元素个数到达 TREEIFY _THRESHOLD(默认是8),并且 table的大小>=MIN_TREEIFY_CAPACITY(默认是64)就会进行树化(红黑树),否则仍然采用数组扩容机制

LinkedHashSet(是 HashSet 的子类)底层结构和源码分析(底层是一个 LinkedHashMap,底层维护了一个 数组+链表)(O(1))(有序,因为其多维护了一个和其插入顺序有关的双向链表)

TreeSet底层结构和源码分析(底层是一个 TreeMap,底层维护了一个 红黑树)(O(logn))( TreeSet 需要对元素进行排序,不能存储null

TreeSet treeSet = new TreeSet(new Comparator() { 			  @Override
	public int compare(Object o1, Object o2) {
	//下面 调用 String 的 compareTo 方法进行字符串大小比较
	//如果老韩要求加入的元素,按照长度大小排序
	//return ((String) o2).compareTo((String) o1); 	
	return ((String) o1).length() - ((String) 		o2).length();
	}
});

Map 接口和常用方法(key 不允许重复(当有相同的 k , 就等价于替换), value 可以重复)(key 和 value 之间存在单向一对一关系)(常用String类作为Map的 key)

增,puk(key,value)

map.put("邓超", new Book("", 100));

删,V remove(Object key):移除指定键key的元素,并返回此元素的value

改,puk(key,value),修改键为key的元素

查,V get(Object key):获取键为key的value值;V getOrDefault(Object key, V defaultValue) ;boolean containsKey(Object key);判断键key是否存在;keySet:获取所有的键;values:获取所有的值;entrySet:获取所有关系k-v(Set<Map.Entry<K, V>> entrySet();需要向下类型转化为Map.Entry)

for (Object entry : entrySet) {
            //将 entry 转成 Map.Entry
            Map.Entry m = (Map.Entry) entry;
            System.out.println(m.getKey() + "-" + m.getValue());
        }
        //(2) 迭代器
        System.out.println("----使用 EntrySet 的 迭代器(第 4 种)	");
        Iterator iterator3 = entrySet.iterator();
        while (iterator3.hasNext()) {
            Object entry = iterator3.next();
            //System.out.println(next.getClass());//HashMap$Node -实现-> Map.Entry (getKey,getValue)
            //向下转型 Map.Entry
            Map.Entry m = (Map.Entry) entry;
            System.out.println(m.getKey() + "-" + m.getValue());
        }

大小容量 size,isEmpty

清空 clear

两种遍历方式(1.使用iterator 2.使用增强for)

【零基础 快速学Java】韩顺平 零基础30天学会Java--- 集合(2024JavaReview)_System_13

HashMap 底层结构和源码分析(不保证映射的顺序,因为底层是以hash表的方式来存储的)(jdk7的hashMap 底层 数组+链表,jdk8的hashMap 底层 数组+链表/红黑树)(线程不安全自,方法没有做同步互斥的操作,没有synchronized)(注意:是整个 HashMap 都变为红黑树:只有冲突较多的链表才会被转换为红黑树`,而不是整个 HashMap)(扩容机制等见HashSet)

  1. HashMap底层维护了Node类型的数组table默认为null
  2. 当添加key-val时,通过key的哈希值(hashcode)得到在table的索引。然后判断该索引处是否有元素,如果没有元素直接添加。如果该索引处有元素(链表/红黑树),继续判断该元素的key和准备加入的key相是否相等(equals),如果相等,则直接替换val;如果不相等需要判断是树结构还是链表结构,做出相应处理。如果添加时发现容量不够,则需要扩容。

LinkedHashMap(是 HashMap 的子类)底层结构和源码分析(底层维护了一个 数组+链表)(O(1))(有序,因为其多维护了一个和其插入顺序有关的双向链表)

TreeMap 底层结构和源码分析(底层维护了一个 红黑树)(O(logn))( TreeSet 需要对元素进行排序,不能存储key为null的元素

TreeMap treeMap = new TreeMap(new Comparator() { @Override
public int compare(Object o1, Object o2) {
	//按照传入的 k(String) 的大小进行排序
	//按照 K(String) 的长度大小排序
	//return ((String) o2).compareTo((String) o1); 
	return ((String) o2).length() - ((String) o1).length();
}
});

HashTable 底层结构和源码分析(键和值都不能为null)(线程安全的synchronized)

Properties(是 HashTable 的子类) 底层结构和源码分析(还可以用于 从 xxx.properties 文件中,加载数据到Properties类对象,并进行读取和修改)

开发中如何选择集合实现类(记住)

  1. 先判断存储的类型(一组对象[单列]或一组键值对[双列])
  2. 一组对象[单列]:Collection接口
  • 允许重复:List
    增删多:LinkedList[底层维护了一个双向链表]
    改查多:ArrayList[底层维护 Object类型的可变数组]
  • 不允许重复:Set
    无序:HashSet[底层是HashMap ,维护了一个哈希表 即(数组+链表+红黑树)]
    有序:LinkedHashSet,维护数组+双向链表
    排序:TreeSet
  1. 一组键值对[双列]:Map
  • 键无序:HashMap[底层是:哈希表 jdk7:数组+链表,jdk8: 数组+链表+红黑树]
  • 键有序:LinkedHashMap
  • 键排序:TreeMap
  • 读取文件:Properties

Collections 工具类注意:不是Collection接口)(里面的方法均为static 方法)

1)Collections 是一个操作 Set、List 和 Map 等集合的工具类
2)Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作

排序, sort(List);sort(List,Comparator)

Collections.sort(list);
Collections.sort(list, new Comparator() { 	@Override
	public int compare(Object o1, Object o2) {
	//可以加入校验代码.
	return ((String) o2).length() - ((String) 	o1).length();
	}
});

翻转, reverse

指定元素的出现次数 ,frequency(Collection,Object)

交换, swap(List,int, int):将指定 list 集合中的 i 处元素和 j 处元素进行交换

最大/小元素, max(Collection);max(Collection,Comparator)

复制,copy(List dest,List src)

替换,replaceAll(List list,Object oldVal,Object newVal)

本章练习题

1. 放入TreeSet里面的元素一定是可以比较的(内部通过比较来确保元素的顺序和去重,因此无法比较的元素会导致ClassCastException异常)

【零基础 快速学Java】韩顺平 零基础30天学会Java--- 集合(2024JavaReview)_ci_14

import java.util.TreeSet;

class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    // Getter for name
    public String getName() {
        return name;
    }
}

public class Homework01 {
    public static void main(String[] args) {
        TreeSet<Person> treeSet = new TreeSet<>();
        // 试图添加Person对象,但由于Person没有实现Comparable接口,运行时会抛出ClassCastException
        treeSet.add(new Person("John"));  // 会抛出异常
    }
}

2.HashSet里面放入对象的常见陷阱

【零基础 快速学Java】韩顺平 零基础30天学会Java--- 集合(2024JavaReview)_ci_15