Java集合中List与Set的区别及体系全览

 更新时间:2026年05月28日 09:00:14   作者:身如柳絮随风扬  
在 Java 开发中,集合(Collection)是最常用的工具之一,本文将带你从顶层 Collection 接口出发,全面梳理 Java 集合体系的继承关系,并深入对比 List 与 Set 的核心差异,希望对大家有所帮助

1. 引言

在 Java 开发中,集合(Collection)是最常用的工具之一。无论是存储一组对象、遍历数据,还是进行去重、排序操作,都离不开 ListSet 等集合接口。然而,很多初学者对集合的整体架构一知半解,对于 “List 是有序、可重复的;Set 是无序、不可重复的” 只停留在死记硬背,并不理解其底层原理和适用场景。

本文将带你从顶层 Collection 接口出发,全面梳理 Java 集合体系的继承关系,并深入对比 ListSet 的核心差异,配合 UML 类图和流程图,让你彻底搞懂:

  • 集合框架的整体结构(ListSet 两大分支)
  • ArrayListLinkedListVector 的区别与选型
  • HashSetTreeSet 的底层实现与排序机制
  • 有序/无序重复/不重复 的真正含义
  • 如何根据业务场景选择合适的集合

2. 集合体系全景图(UML 类图)

Java 集合框架的根接口是 Collection,它派生出两大核心分支:ListSet。下面是简化的继承关系图:

说明

  • List 接口:有序、可重复、有索引。
  • Set 接口:无序(或特定顺序)、不可重复、无索引。
  • 虚线箭头表示实现接口,实线箭头表示继承。

3. List:有序可重复的序列

List 代表一个有序集合(Ordered Collection),即元素按照插入顺序排列,并且可以包含重复元素。每个元素都有一个整数索引,可以精确访问。

3.1 List 的核心特性

特性描述
有序性迭代顺序 = 插入顺序(除非手动排序)。
可重复性允许存储 e1.equals(e2) == true 的元素。
索引访问提供 get(int index)set(int index, E element) 等方法。
遍历方式for 循环、增强 for、Iterator、ListIterator。

3.2 常用实现类对比

实现类底层数据结构随机访问增删效率线程安全适用场景
ArrayList动态数组O(1)尾部 O(1),中间 O(n)查询多、增删少
LinkedList双向链表O(n)头部/尾部 O(1),中间 O(n)频繁头尾增删
Vector动态数组(同步)O(1)尾部 O(1),中间 O(n)(但落后)已过时,不建议使用

示例

List<String> arrayList = new ArrayList<>();
arrayList.add("A"); // 尾部插入
arrayList.add(0, "B"); // 中间插入,元素后移

List<String> linkedList = new LinkedList<>();
linkedList.addFirst("head");
linkedList.addLast("tail");

选型建议

  • 大多数场景用 ArrayList,因为实际查询需求远多于中间插入。
  • 如果需要线程安全的 List,使用 Collections.synchronizedList()CopyOnWriteArrayList(JUC 包)。

4. Set:不重复的集合

Set 接口代表一个不包含重复元素的集合。数学上集合的特性——互异性——在 Java 中通过 equals()hashCode() 实现。

4.1 Set 的核心特性

特性描述
不可重复性set.add(e) 时,如果 e 已存在(根据 equals 比较),则插入失败。
无序性(部分实现)HashSet 不保证顺序;LinkedHashSet 按插入顺序;TreeSet 按自然顺序或比较器排序。
无索引无法通过下标访问,只能通过迭代器或增强 for 遍历。
常用操作并集、交集、差集等集合运算。

4.2 常用实现类对比

实现类底层结构排序允许 null线程安全使用场景
HashSetHashMap无(哈希散列)一个 null最快去重,不关心顺序
LinkedHashSetLinkedHashMap插入顺序一个 null去重并保持插入顺序
TreeSetTreeMap(红黑树)自然顺序/定制顺序不允许 null(默认)需要排序去重

示例

Set<String> hashSet = new HashSet<>();
hashSet.add("banana");
hashSet.add("apple");
hashSet.add("banana");
System.out.println(hashSet); // 无序,可能 [banana, apple]

Set<String> treeSet = new TreeSet<>();
treeSet.add("banana");
treeSet.add("apple");
System.out.println(treeSet); // [apple, banana] 按字典序

Set<String> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("banana");
linkedHashSet.add("apple");
System.out.println(linkedHashSet); // [banana, apple] 插入顺序

注意

  • HashSet 的“无序” ≠ 随机顺序,而是指迭代顺序与插入顺序无关(依赖哈希分布)。
  • 自定义对象放入 HashSet/TreeSet 时必须重写 equals()hashCode()HashSet)或实现 Comparable/提供 ComparatorTreeSet)。

5. List vs Set:核心区别图解

5.1 对比表格

维度ListSet
顺序保证插入顺序一般无序(除 LinkedHashSetTreeSet
重复允许重复不允许重复
索引有索引(通过整数访问)无索引,只能通过迭代器或增强 for
常用实现ArrayList, LinkedList, VectorHashSet, LinkedHashSet, TreeSet
典型场景需要保持顺序、可能需要重复、根据位置访问去重、集合运算、自动排序
性能(查找)随机访问 O(1)(数组),链表 O(n)HashSet O(1),TreeSet O(log n)
允许 null允许任意多个 null最多一个 null(TreeSet 不允许)

5.2 代码对比示例

// List 可以重复
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(1);
System.out.println(list.size()); // 2

// Set 自动去重
Set<Integer> set = new HashSet<>();
set.add(1);
set.add(1);
System.out.println(set.size()); // 1

6. 如何选择 List 还是 Set?

决策要点

  • 如果元素必须按某个特定顺序(如用户操作日志),用 List
  • 如果业务要求元素唯一(如用户ID集合),用 Set
  • 如果既需要去重又需要保持插入顺序,用 LinkedHashSet
  • 如果既需要去重又需要自动排序,用 TreeSet

7. 常见面试题

Q1: 为什么HashSet不保证顺序,而LinkedHashSet可以?

因为 HashSet 底层使用 HashMap,元素的存储位置由哈希码决定,插入顺序无法保留。LinkedHashSetHashMap 基础上额外维护了一个双向链表,记录了插入顺序,因此迭代时按插入顺序输出。

Q2:ArrayList和LinkedList谁的内存占用更大?

LinkedList 每个节点需要存储前驱和后继引用,额外内存开销比 ArrayList 大。ArrayList 底层数组会有一定的容量预留(扩容策略),也可能存在空间浪费。总体而言,元素数量较多时 ArrayList 内存效率更高。

Q3:Vector已经过时,为什么还在某些旧项目中出现?

Vector 是 JDK 1.0 就存在的线程安全 List,方法使用 synchronized 修饰,性能较差。Java 1.2 引入 ArrayList 后,Vector 被标记为遗留类。如需线程安全,推荐使用 Collections.synchronizedListCopyOnWriteArrayList

Q4: 可以将Set转换为List吗?

可以,List 构造函数可以接收任何 Collection

Set<String> set = new HashSet<>(Arrays.asList("A", "B"));
List<String> list = new ArrayList<>(set);
// 之后可对 list 排序、索引访问等

8. 总结

  • List:有序、可重复、有索引,适合需要按照插入顺序访问或根据位置操作的场景。
  • Set:无序(或特定顺序)、不可重复、无索引,适合去重和集合运算。
  • 选择集合时,优先考虑元素是否唯一、顺序要求、访问模式。
  • 熟悉底层数据结构(数组、链表、哈希表、红黑树)有助于理解性能差异。

记忆口诀

List 排队可重复,Set 唯一不重复。

索引增删看实现,Hash 最快 Tree 排序。

到此这篇关于Java集合中List与Set的区别及体系全览的文章就介绍到这了,更多相关Java集合List与Set区别内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java插件扩展机制之SPI案例讲解

    Java插件扩展机制之SPI案例讲解

    这篇文章主要介绍了Java插件扩展机制之SPI案例讲解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-07-07
  • 使用maven编译Java项目实例

    使用maven编译Java项目实例

    这篇文章主要介绍了使用maven编译Java项目实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,,需要的朋友可以参考下
    2019-06-06
  • Java引用类型interface的用法总结

    Java引用类型interface的用法总结

    这篇文章主要为大家详细介绍了Java中引用类型interface的用法的相关资料,文中的示例代码讲解详细,对我们学习Java有一定帮助,感兴趣的可以了解一下
    2022-10-10
  • spring中的注解事务演示和添加步骤详情

    spring中的注解事务演示和添加步骤详情

    这篇文章主要介绍了spring中的注解事务演示和添加步骤详情,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的朋友可以参考一下
    2022-07-07
  • 如何修改JSON字符串中的敏感信息

    如何修改JSON字符串中的敏感信息

    这篇文章主要介绍了如何修改JSON字符串中的敏感信息,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • spring boot 静态资源处理方法

    spring boot 静态资源处理方法

    本篇文章主要介绍了spring boot 静态资源处理方法。小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-03-03
  • 详解Java编程中Annotation注解对象的使用方法

    详解Java编程中Annotation注解对象的使用方法

    这篇文章主要介绍了Java编程中Annotation注解对象的使用方法,注解以"@注解名"的方式被编写,与类、接口、枚举是在同一个层次,需要的朋友可以参考下
    2016-03-03
  • java之swing实现复选框的方法

    java之swing实现复选框的方法

    这篇文章主要介绍了java之swing实现复选框的方法,实例分析了java基于图形界面复选框的实现技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-09-09
  • java避免多层嵌套循环用到的一些小技巧分享

    java避免多层嵌套循环用到的一些小技巧分享

    这篇文章主要介绍了java避免多层嵌套循环用到的一些小技巧分享,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-10-10
  • java实现voctor按指定方式排序示例分享

    java实现voctor按指定方式排序示例分享

    这篇文章主要介绍了java实现voctor按指定方式排序示例,需要的朋友可以参考下
    2014-03-03

最新评论