1. 程式人生 > >Java基礎系列(三十八):集合總覽

Java基礎系列(三十八):集合總覽

前言

在我們日常的開發中,集合佔據著舉足輕重的地位。在不同的情況下,我們會去選擇效能更佳(或更安全的)集合類作為一個容器去儲存資料。在接下來的幾節中,我會帶著大家對於集合的知識進行一次系統的深入梳理,相信梳理過後,面試或日常開發再遇到有關集合的問題對我們來說都不會是問題了。

總覽圖

首先我們先以一個耳熟能詳的集合鳥瞰圖開始說起。

(PS:截圖自《程式設計思想》)

通過這個圖,我們可以獲得哪些有用的資訊呢?

這個圖由Map指向CollectionProduces並不是說MapCollection的一個子類(子介面),這裡的意思是指MapKeySet獲取到的一個檢視是Collection

的子介面(檢視是什麼,我們後面會講到)。
我們可以看到集合有兩個基本介面:MapCollection。但是我個人認為Map並不能說是一個集合,稱之為對映或許更為合適,因為它的KeySet檢視是一個Set型別的鍵集,所以我們姑且把它也當做集合。
Collection繼承了Iterator介面,而Iterator的作用是給我們提供一個只能向後遍歷集合元素的迭代器,也就是說所有實現Collection的類都可以使用Iterator遍歷器去遍歷。
每種介面都有一個Abstr act開頭的抽象子類,這個子類中包括了一些預設的實現,我們在自定義類的時候都需要去繼承這個抽象類,然後根據我們不同的需求,對於其中的方法進行重寫。
從容器角度上來說,只有四種容器:Map
QueueSetList

簡單介紹:

下面我們對基本的分類進行一個簡單的介紹和了解,後續我們會從資料結構和用法等方面去詳細學習。

ArrayList: 一種可以動態增長和縮減的的索引序列
LinkedList:一種可以在任何位置進行高效地插入和刪除操作的有序序列
ArrayDeque:一種用迴圈陣列實現的雙端佇列
HashSet:一種沒有重複元素的無序集合
TreeSet:一種有序集
EnumSet:一種包含列舉型別值的集
LinkedHashSet:一種可以記住元素插入次序的集
PriorityQueue:一種允許高效刪除最小元素的集合
HashMap:一種儲存鍵/值關聯的資料結構
TreeMap:一種鍵值有序排列的對映表
EnumMap:一種鍵值屬於列舉型別的對映表
LinkedHashMap:一種可以記住鍵/值項新增次序的對映表
WeakHashMap:一種其值無用武之地後可以被垃圾回收期回收的對映表
IdentityHashMap:一種用==而不是用equals比較鍵值的對映表
Vector:目前使用較少,因為設計理念的陳舊和效能的問題被ArrayList所取代
Hashtable:執行緒非同步可以使用HashMap來替代,同步的話可以使用ConcurrentHashMap來替代

Iterator

我們接下來聊一下迭代器,從鳥瞰圖中我們可以看到,所有實現Collection的子類都繼承了Iterable介面。這個介面提供了一個iterator()方法可以構造一個Iterator介面物件。然後我們可以使用這個迭代器物件依次訪問集合中的元素
迭代器一般使用方法是這樣的:

Collection<String> c = ...;
Iterator<String> iter = c.iterator();
while (iter.hasNext()) {
    String s = iter.next();
    System.out.println(s);
}

或者是這樣的:

//適用於JDK1.8以後的版本
iter.forEachRemaining(element -> System.out.println(element));

我們接下來看一下Iterator的原始碼(Base jdk1.8):

package java.util;

import java.util.function.Consumer;

public interface Iterator<E> {
    
    boolean hasNext();

    E next();

    default void remove() {
        throw new UnsupportedOperationException("remove");
    }

    default void forEachRemaining(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        while (hasNext())
            action.accept(next());
    }
}

迭代器的next()工作原理是這樣的:

在這裡插入圖片描述可以看出,迭代器是位於兩個集合元素之間的位置,當我們呼叫next()方法的時候迭代器指標就會越過一個元素,並且返回剛剛越過的元素,所以,當我們迭代器的指標在最後一個元素的時候,就會丟擲會丟擲一個NoSuchElementException的異常。所以,在呼叫next()之前需要呼叫hasNext()去判斷這個集合的迭代器是否走到了最後一個元素。

通過呼叫next()方法可以逐個的去訪問集合中的每個元素,而訪問元素的順序跟該容器的資料結構有關,比如ArrayList就是按照索引值開始,每次迭代都會使索引值加1,而對於HashSet這種資料結構是散列表的集合,就會按照某種隨機的次序出現。

Iterator的介面中還有一個remove()方法,這個方法實際上刪除的是上次呼叫next()方法返回的元素,下面我來展示一下remove()方法的使用方法

Collection<String> c = ...;
Iterator<String> iter = c.iterator();
iter.next();
iter.remove();

這樣就可以刪除該集合中的第一個元素,但是需要注意一點,如果我們需要刪除兩個元素,必須這樣做:

iter.remove();
iter.next();
iter.remove();

而不能這麼做:

iter.remove();
iter.remove();

因為next()方法和remove()方法之間是有依賴性的,如果呼叫remove之前沒有呼叫next就會丟擲一個IllegalStateException的異常。

PS: 我們日常中用的很多的foreach迴圈,其實就是一種語法糖,編譯器會把foreach編譯為帶有迭代器的迴圈。

下節預告

接下來,我會陸續帶領大家去從原始碼,資料結構等方面去深入瞭解每一個類,下一節我們要學習Collection的相關知識,敬請期待~

公眾號

在這裡插入圖片描述