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()等)。
相关推荐
独自破碎E几秒前
什么是RabbitMQ中的死信队列?
java·rabbitmq·java-rabbitmq
码界奇点1 分钟前
基于Spring与Netty的分布式配置管理系统设计与实现
java·分布式·spring·毕业设计·源代码管理
计算机毕设指导62 分钟前
基于微信小程序的咖啡店点餐系统【源码文末联系】
java·spring boot·微信小程序·小程序·tomcat·maven·intellij-idea
Geoking.2 分钟前
【设计模式】外观模式(Facade)详解
java·设计模式·外观模式
JaguarJack5 分钟前
2026 年 PHP 8.4 依然重要:跳到 8.5 之前你该掌握的特性
后端·php·服务端
程序员爱钓鱼6 分钟前
Node.js 博客系统实战(一):项目需求分析
前端·后端·node.js
ID_180079054737 分钟前
除了Python,还有哪些语言可以解析淘宝商品详情API返回的JSON数据?
开发语言·python·json
BingoGo7 分钟前
2026 年 PHP 8.4 依然重要:跳到 8.5 之前你该掌握的特性
后端·php
闻道且行之7 分钟前
NLP 部署实操:Langchain-Chatchat 配置文件深度修改与精细化调试
java·自然语言处理·langchain
h7ml9 分钟前
企业微信回调模式解析:从XML到POJO的自定义JAXB编解码器设计
xml·java·企业微信