Java集合中List與Set的區(qū)別及體系全覽
1. 引言
在 Java 開(kāi)發(fā)中,集合(Collection)是最常用的工具之一。無(wú)論是存儲(chǔ)一組對(duì)象、遍歷數(shù)據(jù),還是進(jìn)行去重、排序操作,都離不開(kāi) List、Set 等集合接口。然而,很多初學(xué)者對(duì)集合的整體架構(gòu)一知半解,對(duì)于 “List 是有序、可重復(fù)的;Set 是無(wú)序、不可重復(fù)的” 只停留在死記硬背,并不理解其底層原理和適用場(chǎng)景。
本文將帶你從頂層 Collection 接口出發(fā),全面梳理 Java 集合體系的繼承關(guān)系,并深入對(duì)比 List 與 Set 的核心差異,配合 UML 類(lèi)圖和流程圖,讓你徹底搞懂:
- 集合框架的整體結(jié)構(gòu)(
List、Set兩大分支) ArrayList、LinkedList、Vector的區(qū)別與選型HashSet、TreeSet的底層實(shí)現(xiàn)與排序機(jī)制- 有序/無(wú)序 與 重復(fù)/不重復(fù) 的真正含義
- 如何根據(jù)業(yè)務(wù)場(chǎng)景選擇合適的集合
2. 集合體系全景圖(UML 類(lèi)圖)
Java 集合框架的根接口是 Collection,它派生出兩大核心分支:List 和 Set。下面是簡(jiǎn)化的繼承關(guān)系圖:

說(shuō)明:
List接口:有序、可重復(fù)、有索引。Set接口:無(wú)序(或特定順序)、不可重復(fù)、無(wú)索引。- 虛線箭頭表示實(shí)現(xiàn)接口,實(shí)線箭頭表示繼承。
3. List:有序可重復(fù)的序列
List 代表一個(gè)有序集合(Ordered Collection),即元素按照插入順序排列,并且可以包含重復(fù)元素。每個(gè)元素都有一個(gè)整數(shù)索引,可以精確訪問(wèn)。
3.1 List 的核心特性
| 特性 | 描述 |
|---|---|
| 有序性 | 迭代順序 = 插入順序(除非手動(dòng)排序)。 |
| 可重復(fù)性 | 允許存儲(chǔ) e1.equals(e2) == true 的元素。 |
| 索引訪問(wèn) | 提供 get(int index)、set(int index, E element) 等方法。 |
| 遍歷方式 | for 循環(huán)、增強(qiáng) for、Iterator、ListIterator。 |
3.2 常用實(shí)現(xiàn)類(lèi)對(duì)比
| 實(shí)現(xiàn)類(lèi) | 底層數(shù)據(jù)結(jié)構(gòu) | 隨機(jī)訪問(wèn) | 增刪效率 | 線程安全 | 適用場(chǎng)景 |
|---|---|---|---|---|---|
ArrayList | 動(dòng)態(tài)數(shù)組 | O(1) | 尾部 O(1),中間 O(n) | 否 | 查詢(xún)多、增刪少 |
LinkedList | 雙向鏈表 | O(n) | 頭部/尾部 O(1),中間 O(n) | 否 | 頻繁頭尾增刪 |
Vector | 動(dòng)態(tài)數(shù)組(同步) | O(1) | 尾部 O(1),中間 O(n) | 是(但落后) | 已過(guò)時(shí),不建議使用 |
示例:
List<String> arrayList = new ArrayList<>();
arrayList.add("A"); // 尾部插入
arrayList.add(0, "B"); // 中間插入,元素后移
List<String> linkedList = new LinkedList<>();
linkedList.addFirst("head");
linkedList.addLast("tail");
選型建議:
- 大多數(shù)場(chǎng)景用
ArrayList,因?yàn)閷?shí)際查詢(xún)需求遠(yuǎn)多于中間插入。 - 如果需要線程安全的
List,使用Collections.synchronizedList()或CopyOnWriteArrayList(JUC 包)。
4. Set:不重復(fù)的集合
Set 接口代表一個(gè)不包含重復(fù)元素的集合。數(shù)學(xué)上集合的特性——互異性——在 Java 中通過(guò) equals() 和 hashCode() 實(shí)現(xiàn)。
4.1 Set 的核心特性
| 特性 | 描述 |
|---|---|
| 不可重復(fù)性 | set.add(e) 時(shí),如果 e 已存在(根據(jù) equals 比較),則插入失敗。 |
| 無(wú)序性(部分實(shí)現(xiàn)) | HashSet 不保證順序;LinkedHashSet 按插入順序;TreeSet 按自然順序或比較器排序。 |
| 無(wú)索引 | 無(wú)法通過(guò)下標(biāo)訪問(wèn),只能通過(guò)迭代器或增強(qiáng) for 遍歷。 |
| 常用操作 | 并集、交集、差集等集合運(yùn)算。 |
4.2 常用實(shí)現(xiàn)類(lèi)對(duì)比
| 實(shí)現(xiàn)類(lèi) | 底層結(jié)構(gòu) | 排序 | 允許 null | 線程安全 | 使用場(chǎng)景 |
|---|---|---|---|---|---|
HashSet | HashMap | 無(wú)(哈希散列) | 一個(gè) null | 否 | 最快去重,不關(guān)心順序 |
LinkedHashSet | LinkedHashMap | 插入順序 | 一個(gè) null | 否 | 去重并保持插入順序 |
TreeSet | TreeMap(紅黑樹(shù)) | 自然順序/定制順序 | 不允許 null(默認(rèn)) | 否 | 需要排序去重 |
示例:
Set<String> hashSet = new HashSet<>();
hashSet.add("banana");
hashSet.add("apple");
hashSet.add("banana");
System.out.println(hashSet); // 無(wú)序,可能 [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的“無(wú)序” ≠ 隨機(jī)順序,而是指迭代順序與插入順序無(wú)關(guān)(依賴(lài)哈希分布)。- 自定義對(duì)象放入
HashSet/TreeSet時(shí)必須重寫(xiě)equals()和hashCode()(HashSet)或?qū)崿F(xiàn)Comparable/提供Comparator(TreeSet)。
5. List vs Set:核心區(qū)別圖解

5.1 對(duì)比表格
| 維度 | List | Set |
|---|---|---|
| 順序 | 保證插入順序 | 一般無(wú)序(除 LinkedHashSet、TreeSet) |
| 重復(fù) | 允許重復(fù) | 不允許重復(fù) |
| 索引 | 有索引(通過(guò)整數(shù)訪問(wèn)) | 無(wú)索引,只能通過(guò)迭代器或增強(qiáng) for |
| 常用實(shí)現(xiàn) | ArrayList, LinkedList, Vector | HashSet, LinkedHashSet, TreeSet |
| 典型場(chǎng)景 | 需要保持順序、可能需要重復(fù)、根據(jù)位置訪問(wèn) | 去重、集合運(yùn)算、自動(dòng)排序 |
| 性能(查找) | 隨機(jī)訪問(wèn) O(1)(數(shù)組),鏈表 O(n) | HashSet O(1),TreeSet O(log n) |
| 允許 null | 允許任意多個(gè) null | 最多一個(gè) null(TreeSet 不允許) |
5.2 代碼對(duì)比示例
// List 可以重復(fù) List<Integer> list = new ArrayList<>(); list.add(1); list.add(1); System.out.println(list.size()); // 2 // Set 自動(dòng)去重 Set<Integer> set = new HashSet<>(); set.add(1); set.add(1); System.out.println(set.size()); // 1
6. 如何選擇 List 還是 Set?

決策要點(diǎn):
- 如果元素必須按某個(gè)特定順序(如用戶(hù)操作日志),用
List。 - 如果業(yè)務(wù)要求元素唯一(如用戶(hù)ID集合),用
Set。 - 如果既需要去重又需要保持插入順序,用
LinkedHashSet。 - 如果既需要去重又需要自動(dòng)排序,用
TreeSet。
7. 常見(jiàn)面試題
Q1: 為什么HashSet不保證順序,而LinkedHashSet可以?
因?yàn)?HashSet 底層使用 HashMap,元素的存儲(chǔ)位置由哈希碼決定,插入順序無(wú)法保留。LinkedHashSet 在 HashMap 基礎(chǔ)上額外維護(hù)了一個(gè)雙向鏈表,記錄了插入順序,因此迭代時(shí)按插入順序輸出。
Q2:ArrayList和LinkedList誰(shuí)的內(nèi)存占用更大?
LinkedList 每個(gè)節(jié)點(diǎn)需要存儲(chǔ)前驅(qū)和后繼引用,額外內(nèi)存開(kāi)銷(xiāo)比 ArrayList 大。ArrayList 底層數(shù)組會(huì)有一定的容量預(yù)留(擴(kuò)容策略),也可能存在空間浪費(fèi)。總體而言,元素?cái)?shù)量較多時(shí) ArrayList 內(nèi)存效率更高。
Q3:Vector已經(jīng)過(guò)時(shí),為什么還在某些舊項(xiàng)目中出現(xiàn)?
Vector 是 JDK 1.0 就存在的線程安全 List,方法使用 synchronized 修飾,性能較差。Java 1.2 引入 ArrayList 后,Vector 被標(biāo)記為遺留類(lèi)。如需線程安全,推薦使用 Collections.synchronizedList 或 CopyOnWriteArrayList。
Q4: 可以將Set轉(zhuǎn)換為L(zhǎng)ist嗎?
可以,List 構(gòu)造函數(shù)可以接收任何 Collection:
Set<String> set = new HashSet<>(Arrays.asList("A", "B"));
List<String> list = new ArrayList<>(set);
// 之后可對(duì) list 排序、索引訪問(wèn)等
8. 總結(jié)
List:有序、可重復(fù)、有索引,適合需要按照插入順序訪問(wèn)或根據(jù)位置操作的場(chǎng)景。Set:無(wú)序(或特定順序)、不可重復(fù)、無(wú)索引,適合去重和集合運(yùn)算。- 選擇集合時(shí),優(yōu)先考慮元素是否唯一、順序要求、訪問(wèn)模式。
- 熟悉底層數(shù)據(jù)結(jié)構(gòu)(數(shù)組、鏈表、哈希表、紅黑樹(shù))有助于理解性能差異。
記憶口訣:
List 排隊(duì)可重復(fù),Set 唯一不重復(fù)。
索引增刪看實(shí)現(xiàn),Hash 最快 Tree 排序。
到此這篇關(guān)于Java集合中List與Set的區(qū)別及體系全覽的文章就介紹到這了,更多相關(guān)Java集合List與Set區(qū)別內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Activiti如何啟動(dòng)流程并使流程前進(jìn)
這篇文章主要介紹了Activiti如何啟動(dòng)流程并使流程前進(jìn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03
java 中Excel轉(zhuǎn)shape file的實(shí)例詳解
這篇文章主要介紹了java 中Excel轉(zhuǎn)shape file的實(shí)例詳解的相關(guān)資料,希望通過(guò)本文大家能實(shí)現(xiàn)這樣的功能,需要的朋友可以參考下2017-09-09
Java實(shí)現(xiàn)支付寶之第三方支付寶即時(shí)到賬支付功能
這篇文章主要介紹了Java實(shí)現(xiàn)支付寶之第三方支付寶即時(shí)到賬支付功能的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-07-07
springboot項(xiàng)目中controller層與前端的參數(shù)傳遞方式
這篇文章主要介紹了springboot項(xiàng)目中controller層與前端的參數(shù)傳遞方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10
Java DelayQueue延遲隊(duì)列的原理與應(yīng)用場(chǎng)景詳解
DelayQueue是一個(gè)無(wú)界阻塞隊(duì)列,里面裝滿(mǎn)了實(shí)現(xiàn)Delayed接口的元素,本文主要為大家詳細(xì)介紹了Java如何使用DelayQueue,感興趣的小伙伴可以了解下2025-07-07
SpringBoot整合RabbitMQ實(shí)現(xiàn)延遲隊(duì)列和死信隊(duì)列
RabbitMQ的死信隊(duì)列用于接收其他隊(duì)列中的“死信”消息,所謂“死信”,是指滿(mǎn)足一定條件而無(wú)法被消費(fèi)者正確處理的消息,死信隊(duì)列通常與RabbitMQ的延遲隊(duì)列一起使用,本文給大家介紹了SpringBoot整合RabbitMQ實(shí)現(xiàn)延遲隊(duì)列和死信隊(duì)列,需要的朋友可以參考下2024-06-06
詳解Springboot整合ActiveMQ(Queue和Topic兩種模式)
這篇文章主要介紹了詳解Springboot整合ActiveMQ(Queue和Topic兩種模式),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04

