搜尋
首頁Javajava教程Java 集合框架看這篇就夠了


話不多說,直接上圖:

Java 集合框架看這篇就夠了

Java 集合,也稱為容器,主要是由兩大介面(Interface) 衍生出來的:
Collection 和Map

顧名思義,容器就是用來存放資料的。

那麼這兩大介面的不同之處在於:

  • Collection 存放單一元素;
  • Map 存放key- value 鍵值對。

就是單身狗放 Collection 裡面,couple 就放 Map 裡。 (所以你屬於哪裡?

學習這些集合框架,我認為有4 個目標:

  1. 明確每個介面和類別的對應關係;
  2. 對每個介面和類別,熟悉常用的API;
  3. 對不同的場景,能夠選擇合適的資料結構並分析優缺點;
  4. 學習原始碼的設計,面試要會答啊。

#關於Map,之前那篇HashMap 的文章已經講的非常透徹詳盡了,所以本文不再贅述。如果還沒看過那篇文章的夥伴,快去公眾號內回覆「HashMap」看文章吧~

##Collection

先來看最上層的Collection.

Java 集合框架看這篇就夠了

Collection 裡也定義了許多方法,這些方法也會繼承到各個子介面和實作類別裡,而這些API 的使用也是日常工作和麵試常見常考的,所以我們先來看下這些方法。

操作集合,無非是「增刪改查」四大類,也叫CRUD:

Create, Read, Update, and Delete.

那我也把這些API 分成這四大類:

#功能 方法
#增幅 add()/addAll()
#刪除 remove()/ removeAll( )
Collection Interface 裡沒有
contains()/ containsAll()
其他 isEmpty()/size()/toArray()

下面具體來看:

增:

boolean add(E e);

add() 方法傳入的資料型別必須是Object,所以當寫入基本資料型別的時候,會做自動裝箱auto-boxing 和自動拆箱unboxing。

還有另一個方法 addAll(),可以把另一個集合裡的元素加到此集合中。

boolean addAll(Collection<? extends E> c);

刪除:

boolean remove(Object o);

#remove()是刪除的指定元素。

那和 addAll() 對應的,
自然就有removeAll(),就是把集合 B 中的所有元素都刪除。

boolean removeAll(Collection<?> c);

改:

Collection Interface 裡並沒有直接改元素的操作,反正刪和增就可以完成改了嘛!

查:

  • 查下集合中有没有某个特定的元素:
boolean contains(Object o);
  • 查集合 A 是否包含了集合 B:
boolean containsAll(Collection<?> c);

还有一些对集合整体的操作:

  • 判断集合是否为空:
boolean isEmpty();
  • 集合的大小:
int size();
  • 把集合转成数组:
Object[] toArray();

以上就是 Collection 中常用的 API 了。

在接口里都定义好了,子类不要也得要。

当然子类也会做一些自己的实现,这样就有了不同的数据结构。

那我们一个个来看。

List

Java 集合框架看這篇就夠了

List 最大的特点就是:有序可重复

看官网说的:

An ordered collection (also known as a sequence).

Unlike sets, lists typically allow duplicate elements.

這一下把 Set 的特點也說出來了,跟 List 完全相反,Set 是 無序不重複的。

List 的實作方式有 LinkedList 和 ArrayList 兩種,那面試時最常問的就是這兩個資料結構如何選擇。

對於這類選擇問題:
一是考慮資料結構是否能完成需要的功能
如果都能完成,二是考慮哪一個更高效

(萬事都是如此啊。

那具體來看這兩個 classes 的 API 和它們的時間複雜度:

O(1)##增#add(int index, E e)O(n)O(n)刪除# remove(int index)O(n)O(n)
功能 方法 ArrayList LinkedList
##增 add(E e) O(1)
####刪除######remove(E e)# #####O(n)######O(n)############改######set(int index, E e)#### ##O(1)######O(n)############查詢######get(int index)######O(1) ######O(n)#############

稍微解釋幾個:

add(E e) 是在尾巴上加元素,雖然ArrayList 可能會有擴容的情況出現,但是均攤複雜度(amortized time complexity )還是O(1) 的。

add(int index, E e)是在特定的位置上加元素,LinkedList 需要先找到這個位置,再加上這個元素,雖然單純的「加上」這個動作是O(1) 的,但是要找出這個位置還是O(n) 的。 (這個有的人就認為是O(1),和麵試官解釋清楚就行了,拒絕扛精。

remove(int index)是remove 這個index 上的元素,所以

  • ArrayList 找到這個元素的過程是O(1),但是remove 之後,後續元素都要往前移動一位,所以均攤複雜度是O(n);
  • LinkedList 也是要先找這個index,這個過程是O(n) 的,所以整體也是O(n)。

remove(E e)是remove 見到的第一個這個元素,那麼

  • #ArrayList 要先找出這個元素,這個過程是O(n),然後移除後還要往前移一位,這個更是O(n),總的還是O(n);
  • #LinkedList 也是要先找,這個過程是O(n ),然後移走,這個過程是O(1),總的是O(n).

那造成時間複雜度的區別的原因是什麼呢?

答案

  • 因為ArrayList 是用陣列來實作的。

  • ##而陣列和鍊錶的最大差別就是

    陣列是可以隨機存取的(random access)

這個特點造成了在陣列裡可以透過下標用O(1) 的時間拿到任何位置的數,而鍊錶則做不到,只能從頭開始逐一遍歷。

也就是說在「改查」這兩個功能上,因為數組能夠隨機訪問,所以ArrayList 的效率很高。

那「增刪」呢?

如果不考慮找到這個元素的時間,

數組因為物理上的連續性,當要增刪元素時,在尾部還好,但是其他地方就會導致後續元素都要移動,所以效率較低;而鍊錶則可以輕鬆的斷開和下一個元素的連接,直接插入新元素或移除舊元素。

但是呢,其實你不能不考慮找到元素的時間。 。 。而且如果是在尾部操作,資料量大時 ArrayList 會更快的。

所以說:

  1. 改查選擇ArrayList;
  2. 增刪在尾部的選擇ArrayList;
  3. 其他情況下,如果時間複雜度一樣,推薦選擇ArrayList,因為overhead 更小,或者說內存使用更有效率。

Vector

#那作為 List 的最後一個知識點,我們來聊聊 Vector。這也是一個年齡暴露帖,用過的都是大佬。

那 Vector 和 ArrayList 一樣,也是繼承自 java.util.AbstractList#,底層也是用陣列來實現的。

但現在已經被棄用了,因為...它加了太多的 synchronized!

任何好處都是有代價的,線程安全的成本就是效率低,在某些系統裡很容易成為瓶頸,所以現在大家不再在資料結構的層面加synchronized,而是把這個任務轉移給我們程式設計師==

那麼面試常問題:Vector 和ArrayList 的差別是什麼,只回答這個還不太全面。

來看stack overflow 上的高票回答:

Java 集合框架看這篇就夠了

#一是剛才已經說過的線程安全問題;
二是擴容時擴多少的差別。

這個得看看原始碼:

Java 集合框架看這篇就夠了

這是ArrayList 的擴容實現,這個算術右移操作是把這個數的二進位往右移動一位,最左邊補符號位,但是因為容量沒有負數,所以還是補0.

那右移一位的效果就是除以2 ,那麼定義的新容量就是原容量的1.5 倍

再來看 Vector 的:

Java 集合框架看這篇就夠了

因為通常 capacityIncrement 我們不會定義,所以預設情況下它是擴容兩倍

答出來這兩點,就肯定沒問題了。

Queue & Deque

#Queue 是一端進另一端出的線性資料結構;而Deque 是兩端都可以進出的。

Java 集合框架看這篇就夠了

Queue

#Java 中的這個Queue 介面稍微有點坑,一般來說佇列的語義都是先進先出(FIFO)的。

但這裡有個例外,就是PriorityQueue,也叫heap,並不按照進去的時間順序出來,而是按照規定的優先級出去,並且它的操作並不是O(1) 的,時間複雜度的計算稍微有點複雜,我們之後再單獨開一篇來講。

那 Queue 的方法官網[1]都總結好了,它有兩組 API,基本功能是一樣的,但是呢:

  • 一組是會拋異常的;
  • 另一組會回傳一個特殊值。
# #

為什麼會拋異常呢?

  • 例如佇列空了,那remove() 就會拋異常,但是poll() 就回傳null;element() 就會拋異常,而peek() 就返回null 就好了。

那 add(e) 怎麼會拋異常呢?

有些Queue 它會有容量的限制,例如BlockingQueue,那如果已經達到了它最大的容量且不會擴容的,就會拋異常;但如果offer (e),就會return false.

那要怎麼選擇呢? :

  • 首先,要用就用同一組API,前後要統一;

  • 其次,根據需求。如果你需要它拋異常,那就是用拋異常的;不過做算法題時基本上不用,所以選那組回傳特殊值的就好了。

Deque

#Deque 是兩端都可以進出的,那自然是有針對First 端的操作和對Last 端的操作,那每端都有兩組,一組拋異常,一組傳回特殊值:

功能 丟棄異常 回傳值
add(e) offer(e)
刪除 remove() poll()
element() peek()
功能 拋出異常 傳回值
#addFirst(e)/ addLast(e) offerFirst( e)/ offerLast(e)
刪除 removeFirst()/ removeLast() pollFirst()/ pollLast()
getFirst()/ getLast() peekFirst()/ peekLast()
########

使用時同理,要用就用同一組。

Queue 和 Deque 的這些 API 都是 O(1) 的時間複雜度,準確來說是均攤時間複雜度。

實作類別

它們的實作類別有這三個:

Java 集合框架看這篇就夠了

所以說,

  • 如果想實作「普通佇列- 先進先出」的語義,就使用LinkedList 或ArrayDeque 來實作;
  • 如果想實作「優先佇列」的語義,就使用PriorityQueue;
  • 如果想實作「堆疊」的語義,就使用ArrayDeque。

我們一個個來看。

在實作普通佇列時,如何選擇用 LinkedList 還是 ArrayDeque 呢?

來看看StackOverflow[2] 上的高票答案:

Java 集合框架看這篇就夠了
##總結來說就是推薦使用ArrayDeque,因為效率高,而LinkedList 還會有其他的額外開銷(overhead)。

那 ArrayDeque 和 LinkedList 的差別有哪些呢?

Java 集合框架看這篇就夠了
還是在剛才的同一個問題下,這是我認為總結的最好的:

  1. ArrayDeque是一個可擴容的數組,LinkedList 是鍊錶結構;
  2. ArrayDeque 裡不可以存null 值,但是LinkedList 可以;
  3. #ArrayDeque 在操作頭尾端的增刪操作時更有效率,但是LinkedList 只有在當要移除中間某個元素且已經找到了這個元素後的移除才是O(1) 的;
  4. ArrayDeque 在記憶體使用方面更有效率。
所以,只要不是必須要存 null 值,就選擇 ArrayDeque 吧!

那如果是很資深的面試官問你,什麼情況下你要選擇用 LinkedList 呢?

  • 答:Java 6 以前。。。因为 ArrayDeque 在 Java 6 之后才有的。。

为了版本兼容的问题,实际工作中我们不得不做一些妥协。。

那最后一个问题,就是关于 Stack 了。

Stack

Stack 在语义上是 后进先出(LIFO) 的线性数据结构。

有很多高频面试题都是要用到栈的,比如接水问题,虽然最优解是用双指针,但是用栈是最直观的解法也是需要了解的,之后有机会再专门写吧。

那在 Java 中是怎么实现栈的呢?

虽然 Java 中有 Stack 这个类,但是呢,官方文档都说不让用了!

Java 集合框架看這篇就夠了

原因也很简单,因为 Vector 已经过被弃用了,而 Stack 是继承 Vector 的。

那么想实现 Stack 的语义,就用 ArrayDeque 吧:

Deque<Integer> stack = new ArrayDeque<>();

Set

最后一个 Set,刚才已经说过了 Set 的特定是无序不重复的。

就和数学里学的「集合」的概念一致。

Java 集合框架看這篇就夠了

Set 的常用实现类有三个:

HashSet: 采用 Hashmap 的 key 来储存元素,主要特点是无序的,基本操作都是 O(1) 的时间复杂度,很快。

LinkedHashSet: 这个是一个 HashSet + LinkedList 的结构,特点就是既拥有了 O(1) 的时间复杂度,又能够保留插入的顺序。

TreeSet: 采用红黑树结构,特点是可以有序,可以用自然排序或者自定义比较器来排序;缺点就是查询速度没有 HashSet 快。

那每个 Set 的底层实现其实就是对应的 Map:

數值放在 map 中的 key 上,value 上放了個 PRESENT,是靜態的 Object,相當於 place holder,每個 key 都指向這個 object。

那麼具體的實作原理增刪改查四種操作,以及雜湊衝突hashCode( )/equals() 等問題都在HashMap 那篇文章裡講過了,這裡就不贅述了,沒有看過的小伙伴可以在公眾號後台回复“HashMap”獲取文章哦~

總結

Java 集合框架看這篇就夠了

#再回到開篇的這張圖,有沒有清楚了一些呢?

每個資料結構下面其實都有很多內容,像是 PriorityQueue 本文沒有細說,因為這傢伙一說又要半天。 。

如果你覺得文章不錯,文末的讚? 又回來啦,記得給我「讚」和「在看」喔~

最後呢,很多讀者一直有問我交流群的問題,因為考慮到我有時差不方便管理,所以一直沒做。

但現在我找了一個專業的管理員和我一起管理,所以「齊姐的秘密基地」正在籌備中,並且我會邀請國內外的一些大佬入場,給大家帶來不一樣的視角。

那麼第一期交流群組計畫於 7 月初開放,到時候我會在朋友圈發邀請,大家敬請期待!

#

以上是Java 集合框架看這篇就夠了的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文轉載於:Java学习指南。如有侵權,請聯絡admin@php.cn刪除
JVM如何處理操作系統API的差異?JVM如何處理操作系統API的差異?Apr 27, 2025 am 12:18 AM

JVM通過JavaNativeInterface(JNI)和Java標準庫處理操作系統API差異:1.JNI允許Java代碼調用本地代碼,直接與操作系統API交互。 2.Java標準庫提供統一API,內部映射到不同操作系統API,確保代碼跨平台運行。

Java 9影響平台獨立性中引入的模塊化如何?Java 9影響平台獨立性中引入的模塊化如何?Apr 27, 2025 am 12:15 AM

modularitydoesnotdirectlyaffectJava'splatformindependence.Java'splatformindependenceismaintainedbytheJVM,butmodularityinfluencesapplicationstructureandmanagement,indirectlyimpactingplatformindependence.1)Deploymentanddistributionbecomemoreefficientwi

什麼是字節碼,它與Java的平台獨立性有何關係?什麼是字節碼,它與Java的平台獨立性有何關係?Apr 27, 2025 am 12:06 AM

BytecodeinJavaistheintermediaterepresentationthatenablesplatformindependence.1)Javacodeiscompiledintobytecodestoredin.classfiles.2)TheJVMinterpretsorcompilesthisbytecodeintomachinecodeatruntime,allowingthesamebytecodetorunonanydevicewithaJVM,thusfulf

為什麼Java被認為是一種獨立於平台的語言?為什麼Java被認為是一種獨立於平台的語言?Apr 27, 2025 am 12:03 AM

javaachievesplatformIndependencEthroughThoJavavIrtualMachine(JVM),wodecutesbytecodeonyanydenanydevicewithajvm.1)javacodeiscompiledintobytecode.2)

圖形用戶界面(GUIS)如何提出Java平台獨立性的挑戰?圖形用戶界面(GUIS)如何提出Java平台獨立性的挑戰?Apr 27, 2025 am 12:02 AM

JavaGUI開發中的平台獨立性面臨挑戰,但可以通過使用Swing、JavaFX,統一外觀,性能優化,第三方庫和跨平台測試來應對。 JavaGUI開發依賴於AWT和Swing,Swing旨在提供跨平台一致性,但實際效果因操作系統不同而異。解決方案包括:1)使用Swing和JavaFX作為GUI工具包;2)通過UIManager.setLookAndFeel()統一外觀;3)優化性能以適應不同平台;4)使用如ApachePivot或SWT的第三方庫;5)進行跨平台測試以確保一致性。

Java開發的哪些方面取決於平台?Java開發的哪些方面取決於平台?Apr 26, 2025 am 12:19 AM

JavadevelovermentIrelyPlatForm-DeTueTososeVeralFactors.1)JVMVariationsAffectPerformanceNandBehaviorAcroSsdifferentos.2)Nativelibrariesviajnijniiniininiinniinindrododerplatefform.3)

在不同平台上運行Java代碼時是否存在性能差異?為什麼?在不同平台上運行Java代碼時是否存在性能差異?為什麼?Apr 26, 2025 am 12:15 AM

Java代碼在不同平台上運行時會有性能差異。 1)JVM的實現和優化策略不同,如OracleJDK和OpenJDK。 2)操作系統的特性,如內存管理和線程調度,也會影響性能。 3)可以通過選擇合適的JVM、調整JVM參數和代碼優化來提升性能。

Java平台獨立性有什麼局限性?Java平台獨立性有什麼局限性?Apr 26, 2025 am 12:10 AM

Java'splatFormentenceHaslimitations不包括PerformanceOverhead,versionCompatibilityIsissues,挑戰WithnativelibraryIntegration,Platform-SpecificFeatures,andjvminstallation/jvminstallation/jvmintenance/jeartenance.therefactorscomplicatorscomplicatethe“ writeOnce”

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )專業的PHP整合開發工具

mPDF

mPDF

mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

MantisBT

MantisBT

Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

EditPlus 中文破解版

EditPlus 中文破解版

體積小,語法高亮,不支援程式碼提示功能