List和Set的区别

一、先明确:为什么考察 List 和 Set 的区别?

  1. 你是否能区分两者的核心特性?(有序性、重复性、索引访问)
  2. 能否掌握各自核心实现类(ArrayList/LinkedList、HashSet/TreeSet)的差异和适用场景?
  3. 能否结合实际业务说出选型依据?(比如 "存储有序数据用 List,去重数据用 Set")

二、先铺垫:List 和 Set 的通俗认知

先通过一句话建立直观印象,避免一上来就讲抽象概念:

  • List:"有序、可重复的集合,像一个'有序的抽屉',每个元素有固定位置(索引),可以存放重复的物品,还能通过位置快速找到物品";
  • Set:"无序(部分实现类有序)、不可重复的集合,像一个'去重的篮子',不能存放重复物品,也无法通过位置查找,只能判断物品是否存在"。

三、核心:List 和 Set 的维度对比

从 5 个核心维度展开对比,每个维度都搭配通俗解释,避免空洞表格:

对比维度 List 接口 Set 接口
元素重复性 允许存储重复元素(如可添加多个 "张三") 不允许存储重复元素(添加重复元素会失败,返回 false)
有序性 保证元素的「插入顺序」(有序集合),元素的存放顺序和添加顺序一致 默认无序(如 HashSet),不保证插入顺序;仅部分实现类(如 TreeSet)保证「排序顺序」(自然排序 / 自定义排序),非插入顺序
索引访问 支持索引访问(通过下标get(int index)获取元素,下标从 0 开始) 不支持索引访问(无get(int index)方法),只能通过迭代器或增强 for 循环遍历
底层实现逻辑 核心实现类(ArrayList/LinkedList)基于数组 / 链表实现,无需处理元素重复问题 核心实现类(HashSet/TreeSet)基于哈希表(HashMap)/ 红黑树(TreeMap)实现,通过 "哈希值 / 比较器" 保证元素唯一性
常用核心方法 包含索引相关方法:get(int index)add(int index, E e)remove(int index)set(int index, E e) 无索引相关方法,新增contains(Object o)方法(高频用于判断元素是否存在),添加元素方法add(E e)返回 boolean(判断是否添加成功)

四、重点:各自核心实现类解析

接口本身无法实例化,实际开发中使用的是实现类,这是考察的重点:

1. List 接口的核心实现类

1.1 ArrayList(最常用)

  • 底层结构:动态数组
  • 核心特性:查询效率高(通过索引直接访问),增删效率低(中间位置增删需移动数组元素);
  • 适用场景:业务中高频查询、少量增删的场景(如存储用户列表、商品列表,通过索引分页查询);
  • 注意点:初始容量为 10,扩容时默认扩容 1.5 倍,扩容会消耗性能(可提前指定初始容量优化)。

1.2 LinkedList

  • 底层结构:双向链表
  • 核心特性:查询效率低(需遍历链表),增删效率高(只需修改链表节点的引用,无需移动元素);
  • 适用场景:频繁增删(尤其是首尾增删)的场景(如实现队列、栈,或消息队列的消息存储);
  • 注意点:实现了 Deque 接口,提供addFirst()、addLast()、removeFirst()等首尾操作方法。

2. Set 接口的核心实现类

2.1 HashSet(最常用)

  • 底层结构:基于 HashMap 实现 (元素存储在 HashMap 的 key 中,value 为固定常量PRESENT);
  • 核心特性:无序(不保证插入顺序)、不可重复,查询 / 增删效率高(O (1) 时间复杂度);
  • 保证唯一性的原理:先通过元素的hashCode()方法计算哈希值,再通过equals()方法判断是否相等,两者都相同则视为重复元素;
  • 适用场景:无需关注顺序,只需去重的场景(如存储已选商品 ID、去重的手机号列表);
  • 注意点:存储的元素需重写hashCode()和equals()方法(否则无法正确去重,比如自定义实体类)。

2.2 TreeSet

  • 底层结构:基于 TreeMap 实现(红黑树);
  • 核心特性:有序(默认自然排序,如 Integer 升序、String 字典序;支持自定义排序(实现 Comparator 接口))、不可重复;
  • 保证唯一性 + 有序性的原理:通过元素的比较逻辑(自然排序Comparable/ 自定义排序Comparator)判断是否重复及排序;
  • 适用场景:需要去重且按指定规则排序的场景(如按价格排序的商品集合、按时间排序的日志 ID 集合);
  • 注意点:存储的元素需实现Comparable接口(或创建 TreeSet 时指定 Comparator),否则会抛出ClassCastException。

加分项

  1. 结合项目举例:"我在实训项目的用户管理模块中,用 ArrayList 存储用户列表(需要分页查询,按索引获取数据);用 HashSet 存储用户的角色 ID(需要去重,无需关注顺序);用 TreeSet 存储商品列表(需要按价格排序并去重)";
  2. 细节优化意识:"使用 ArrayList 时,我会提前指定初始容量(如new ArrayList<>(100)),避免频繁扩容损耗性能;自定义实体类存入 HashSet 时,会重写hashCode()和equals()方法,保证去重生效";
  3. 深入理解实现类:"我知道 ArrayList 底层是动态数组,查询快增删慢;LinkedList 底层是双向链表,增删快查询慢;HashSet 底层依赖 HashMap,TreeSet 底层依赖 TreeMap"。

踩坑点

  1. 混淆 Set 的有序性:误以为 "所有 Set 都是无序的",忽略 TreeSet 是有序(排序顺序)的;
  2. 自定义实体类存入 Set 未重写方法 :直接将自定义实体类存入 HashSet,未重写hashCode()equals(),导致无法去重;
  3. 滥用 ArrayList 做频繁增删:在频繁中间增删的场景下使用 ArrayList,导致性能低下(应使用 LinkedList);
  4. **试图通过索引访问 Set:**调用 Set 的get(int index)方法,导致编译报错(Set 无索引访问方法)。

举一反三

  1. "ArrayList 和 LinkedList 的核心区别是什么?各自的适用场景是什么?"(答案:底层结构不同(数组 / 双向链表);ArrayList 查询快、增删慢,适用于查询多的场景;LinkedList 增删快、查询慢,适用于增删多的场景);
  2. "HashSet 是如何保证元素唯一性的?如果两个元素的 hashCode 相同但 equals 不同,会发生什么?"(答案:通过hashCode()+equals()判断;hashCode 相同但 equals 不同,会在哈希表的同一桶位形成链表(JDK8 后链表长度超过 8 转为红黑树),仍能存储为不同元素);
  3. "如何让 TreeSet 按自定义规则排序(比如按用户年龄倒序)?"(答案:1. User 类实现Comparable<User>接口,重写compareTo方法;2. 创建 TreeSet 时传入Comparator<User>匿名内部类 / Lambda 表达式,指定排序规则);
  4. "List 和 Set 都继承自 Collection 接口,它们有哪些共同的方法?"(答案:add(E e)、remove(Object o)、contains(Object o)、size()、isEmpty()、clear()等)。
相关推荐
草履虫建模15 小时前
力扣算法 1768. 交替合并字符串
java·开发语言·算法·leetcode·职场和发展·idea·基础
naruto_lnq17 小时前
分布式系统安全通信
开发语言·c++·算法
qq_2975746718 小时前
【实战教程】SpringBoot 实现多文件批量下载并打包为 ZIP 压缩包
java·spring boot·后端
老毛肚18 小时前
MyBatis插件原理及Spring集成
java·spring·mybatis
学嵌入式的小杨同学18 小时前
【Linux 封神之路】信号编程全解析:从信号基础到 MP3 播放器实战(含核心 API 与避坑指南)
java·linux·c语言·开发语言·vscode·vim·ux
lang2015092818 小时前
JSR-340 :高性能Web开发新标准
java·前端·servlet
Re.不晚18 小时前
Java入门17——异常
java·开发语言
缘空如是18 小时前
基础工具包之JSON 工厂类
java·json·json切换
精彩极了吧18 小时前
C语言基本语法-自定义类型:结构体&联合体&枚举
c语言·开发语言·枚举·结构体·内存对齐·位段·联合
追逐梦想的张小年19 小时前
JUC编程04
java·idea