Java集合框架筆記
0 引言
大家都學過資料結構這門課,應該對資料的基本儲存和組織方式有一定的概念了吧。我們可以把大量的資料的儲存到“容器”裡,這裡的“容器”就是一種被封裝起來的資料結構,為我們提供了很多便捷好用的介面,而將內部的實現細節給隱藏起來了。
打個比方,我們使用電飯煲做飯,可以把電飯煲看作是容器,只需要知道怎麼開啟電飯煲把米放進去,按哪個按鈕開始做飯,怎麼把蓋子開啟把飯盛出來,這些就是電飯煲為我們提供的介面。而我們不需要了解電飯煲內部的電路結構,使用什麼電子元件,蓋子的機械傳動結構是什麼樣的,這些是被隱藏起來的實現細節。
當我們用 C 語言寫連結串列時,會先定義一個長這樣的結構體:
typedef struct Node { ElementType val; struct Node * next; } *List;
然後在程式裡各種malloc
函式、next
指標滿天飛,跑起來就空指標野指標程式崩潰是吧。
當然我們也可以將寫的連結串列操作封成函式,類似這樣:
void insertList(List L, int idx, ElementType e);
void removeNode(List L);
List find(List L, ElementType e);
這些是我們自己寫的連結串列函式,用函式來實現連結串列的功能,一是方便 debug,二是可以讓程式更有條理。C 畢竟還只是面向過程的程式語言,而封裝到極致就進化成了面向物件。
再比如,做程式設計題的時候,要寫一個棧,於是你上來就開一個大小 100005 的陣列,再設一個全域性變數 top。真要寫大工程的時候是萬萬不能這樣搞的,尤其不能動不動就設全域性變數。
在 C++ 中就友好多了,C++ 提供了很多拿來就用的容器,比如可變陣列 vector
、棧 stack
、字典 Map
等等。而到了 Java 這邊,容器更要複雜而精緻得多。
1 集合框架概述
集合框架的四部分:
- 資料結構
- 比較器
- 演算法:Collections 和 Arrays;
- 迭代器
集合類的特點:
- 只容納物件,基本資料型別要封裝成類的物件;
Java 中的集合框架為我們提供了各種各樣的容器類,每一種容器都有各自的性質,當然底層的實現方式也不盡相同,因此其使用方式、操作效率和安全性也不一樣。
同時 Java 中的容器還具有能動態增長、高效能、提供豐富的方法等特點,很方便使用者使用。
繼承是面嚮物件語言的一大特點,而不同的容器也是有繼承關係的,就好像生物學中的分類一樣。Java 的所有容器都發源於 Iterable 介面,從 Iterable 介面又分出兩大派系(介面),一是每個單元格儲存資料本身的 Collection 介面,二是單元格需要儲存鍵值對(<key, value>
)的 Map 介面。其它的容器類或介面都是繼承或實現了這兩個介面。下面用一張圖來說明一下 Java 容器的家族關係:
???怎麼這麼多啊
其實很多細節我們也用不上在這篇文章也不會詳細介紹,用到了就查官方文件吧。下面就一些重要的部分簡單說一下:
- Collection 介面:
- List 介面:資料被組織為線性結構,每一個元素在容器中有固定的位置,可以通過索引訪問;
- Queue 介面:佇列,可以往裡放元素,但是隻有一個出口
- PriorityQueue:優先佇列,每次只能彈出或訪問權值最大(或小)的元素;
- Deque:雙端佇列,兩頭都可以進出元素。
- Set 介面:每個元素無固定位置,元素不能重複,因此元素必須實現
equals()
方法- SortedSet:可以有序遍歷的集合;
- Map:
- HashMap:雜湊實現的
<key, value>
集合,高效插入查詢; - SortedMap:同樣是
<key, value>
集合,但是內部儲存從某種程度來說是有序的,方便遍歷。
- HashMap:雜湊實現的
這些多種多樣的容器跟比較器(Comparable、Comparator)、演算法(Collactions、Arrays)、迭代器(Iterator)共同組成了 Java 的集合框架。
下面的內容便是對這部分的較為詳細的介紹。
2 容器
2.1 泛型
Java 5 之前的容器都是放的 Object
類的物件作為元素,一旦被放進去就會被自動轉型。雖然什麼都能往裡面放,但是拿出來的話那個物件也不是原來的物件了,就需要強制轉回原來的類才能用。但是這樣操作不僅麻煩,而且很容易在執行時由於轉換錯誤丟擲執行時異常。所以 Java 5 及之後的版本使用泛型來解決這個問題。
在宣告一個容器的時候必須指定容器內元素的型別:
ArrayList<Integer> arrayList = new ArrayList<>();
// 宣告一個存放整數的可變陣列
HashMap<String, Object> hashMap = new HashMap<>();
// 宣告一個以 String 為鍵, Object 為值的雜湊表
注意:
- 泛型中不能使用基本型別,但可以用其對應的類,比如
Integer
存整數,Boolean
存布林變數; - 宣告好的容器可以存放宣告型別的子類,比如宣告成
Object
類就可以放任何類。 - 泛型中也可以使用介面,裡面的可以存放實現該介面的子類。
2.2 Collection 介面
直接說一下這個接口裡的常用方法吧:
-
int size()
:返回元素個數; -
boolean isEmpty()
:返回是否空; -
boolean add(E e)
:加入一個元素,返回是否成功; -
boolean remove(Object o)
:刪除一個元素,返回是否成功; -
boolean contains(Object o)
:返回是否含有某個元素,呼叫equals()
方法作比較; -
boolean addAll(Collection<? extends E> c)
:加入c
中的所有元素,返回是否成功; -
boolean removeAll(Collection<?> c)
:刪除c
中的所有元素,返回是否有元素被刪除; -
boolean containsAll(Collection<?> c)
:檢查是否含有c
中的所有元素; -
Iterator<E> iterator()
:返回一個迭代器; -
Object[] toArray()
:返回一個數組,存放裡面的元素; -
<T> T[] toArray(T[] a)
:- 首先將集合中的元素造型為
T
; - 若
a
的大小足以容納集合中的元素,則將a
中填為集合中的元素,同時返回a
本身; - 否則另開一個數組填為集合中的所有元素並返回。
- 首先將集合中的元素造型為
Collection<Object> collection = new Collection<>();
String[] strings = collection.toArray(new String[5]);
-
default boolean removeIf(Predicate<? super E> filter)
:傳進一個謂詞 filter,符合該條件的元素被刪除,返回是否有元素被刪除; -
boolean retainAll(Collection<?> c)
:僅保留c
中含有的元素,返回是否有元素被刪除; -
void clear()
:清除集合。
2.1.1 List
-
default void replaceAll(UnaryOperator<E> operator)
:傳一個操作類的物件UnaryOperator
,對每一個元素施以這個操作。 -
default void sort(Comparator<? super E> c)
:傳一個比較器,進行排序; -
E get(int index)
:取得指定位置的元素; -
E set(int index, E element)
:將指定位置位置的元素替換為 element; -
void add(int index, E element)
:向指定位置插入一個元素; -
E remove(int index)
:刪除指定位置的元素; -
int indexOf(Object o)
:查詢第一次出現的位置; -
int lastIndexOf(Object o)
:查詢最後一次出現的位置; -
ListIterator<E> listIterator()
:返回一個迭代器; -
ListIterator<E> listIterator(int index)
:返回值定位置的迭代器。
2.1.1.1 ArrayList
動態陣列,順序儲存,每次擴張容量增大 50%。
-
public ArrayList(int initialCapacity)
:建構函式,初始化大小; -
public ArrayList()
:建構函式,預設大小為 10; -
public ArrayList(Collection<? extends E> c)
:從c
中初始化; -
public void trimToSize()
:將容器佔用空間收縮至長度; -
public void ensureCapacity(int minCapacity)
:擴大大小至minCapacity
。 -
public List<E> subList(int fromIndex, int toIndex)
:返回子列。
2.1.1.2 LinkedList
鏈式儲存,也可以快速刪除首尾元素。
-
public LinkedList()
:構造空連結串列; -
public LinkedList(Collection<? extends E> c)
:從c
中初始化; -
public E getFirst()
:獲取頭部; -
public E getLast()
:獲取尾部; -
public E removeFirst()
:刪除頭部; -
public E removeLast()
:刪除尾部; -
public void addFirst(E e)
:從頭部新增; -
public void addLast(E e)
:從尾部新增。
2.1.1.3 Vector
ArrayList 的執行緒安全版,但是效率不如 ArrayList。
2.1.2 Set
元素必須唯一,所以裡面的元素必須定義 equals() 方法。不允許新增重複的元素。
方法與 Collection 相同,只是當插入失敗(試圖插入重複元素)時 add()
方法會返回 false
。
2.1.2.1 HashSet
初始化時可以規定大小,也可以從某集合初始化,是無序的。
2.1.2.2 TreeSet
初始化時可以規定大小,也可以從某集合初始化,是有序的。
-
public Iterator<E> descendingIterator()
:返回一個降序的迭代器; -
public NavigableSet<E> descendingSet()
:返回降序的集合; -
public NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive)
:返回從fromElement
到toElement
子集合; -
public NavigableSet<E> headSet(E toElement, boolean inclusive)
:返回從頭到toElement
的自集合; -
public NavigableSet<E> tailSet(E fromElement, boolean inclusive)
:返回從fromElement
到尾的子集合; -
public Comparator<? super E> comparator()
:返回比較器; -
public E first()
:返回首個元素; -
public E last()
:返回最後一個元素; public E lower(E e)
public E floor(E e)
public E ceiling(E e)
public E higher(E e)
public E pollFirst()
public E pollLast()
2.1.3 Queue
-
boolean add(E e)
:加入一個元素,容量滿會異常; -
boolean offer(E e)
:功能同add()
,插入失敗會返回false
而不會異常; -
E remove()
:返回並刪除隊頭,佇列為空則異常; -
E poll()
:返回並刪除隊頭,佇列為空返回null
,不會異常; -
E element()
:取隊首元素,佇列為空則異常; -
E peek()
:取隊首元素,佇列為空則返回null
不會異常。
2.3 Map 介面
其含有的方法如下:
int size()
boolean isEmpty()
boolean containsKey(Object key)
boolean containsValue(Object value)
V get(Object key)
V put(K key, V value)
V remove(Object key)
void putAll(Map<? extends K, ? extends V> m)
void clear()
Set<K> keySet()
Collection<V> values()
-
Set<Map.Entry<K, V>> entrySet()
:返回一個集合。
2.3.1 HashMap
方法基本同上。可以從一個 Map 初始化。
2.3.2 TreeMap
使用紅黑樹儲存便於遍歷。
3 比較器
Java 中要實現自動排序需要定義比較器,對於 String 和包裝類有自動的比較器,但是對於其他類就需要使用我們自己定義的比較器了。要實現比較功能,可以實現 Comparable 介面或定義 Comparator 類。
3.1 Comparable 介面
自定義類可以實現 Comparable<E>
介面,其中 E
為自定義類,同時類中必須實現 compareTo
方法,例如:
class Cell implements Comparable<Cell>{
private final int id;
public Item(int id) {
this.id = id;
}
@Override
public int compareTo(Item o) {
return this.id - o.id;
}
}
3.2 Comparator 類
在使用一些容器的 sort()
方法之前,需要定義一個 Comparator
類作為引數傳入,定義可以是這樣:
Comparator<Cell> comparator = new Comparator<Cell>() {
@Override
public int compare(Cell o1, Cell o2) {
...;
return o1.compareTo(o2);
}
};
arrayList.sort(comparator);
當然可以用 lambda 表示式化簡:
Comparator<Cell> comparator = (o1, o2) ->{
...;
o1.compareTo(o2)
};
當只是呼叫到 Cell
的一個函式時,可以用方法的引用進一步化簡:
Comparator<Cell> comparator = Cell::compareTo;
以至於你在使用容器的排序方法時可以:
arrayList.sort(Cell::compareTo);
4 迭代器 Iterator
迭代器是用於容器的遍歷的,對於一個容器,遍歷方法通常有以下三種:
- for 迴圈遍歷:
for (int i = 0; i < array.size(); i++) {
System.out.println(array.get(i));
}
- 增強型 for 迴圈:
for (Cell cell : array) {
System.out.println(cell);
}
- 迭代器遍歷
下面將要細說迭代器是怎麼用的了。
4.1 初始化迭代器
呼叫容器的返回迭代器的方法即可獲得一個迭代器,如:
Iterator<Cell> iterator = array.iterator();
4.2 迭代器的方法
-
boolean hasNext()
:判斷迭代器是否到達末尾(此時指的是一個空元素); -
next()
:返回迭代器所指的成員,並且自身後移一位; -
default void remove()
:刪除迭代器所指的前一個元素,要跟在next()
後面,且每呼叫一次next()
最多隻能刪除一次。
示例:
ArrayList<Integer> arrayList = new ArrayList<>();
arrayList.add(1);
arrayList.add(2);
arrayList.add(15);
Iterator<Integer> iterator = arrayList.iterator();
while (iterator.hasNext()) {
Integer i = iterator.next();
if (i < 10)
iterator.remove();
}
System.out.println(arrayList);
4.3 ListIterator
相比於一般的 Iterator,ListIterator 可以前向遍歷,也可以返回所指元素的的位置,具體方法見文件。
5 演算法類
Java 的集合架構提供了兩個功能強大的演算法庫 Collections 和 Arrays,用這兩個庫可以在集合上進行排序、序列化等等操作。
5.1 Collections
-
void sort(List<T> list)
:對 list 進行排序,要求其中的元素 T 必須實現了 Comparable 介面,重寫 compareTo 方法; -
void sort(List<T> list, Comparator<? super T> c)
:當 list 沒有實現 Comparable 介面時,可以傳入一個 Comparator 進行排序操作; -
int binarySearch(List<? extends Comparable<? super T>> list, T key)
:對一個實現了 Comparable 的有序 list 進行二分查詢,返回第一個找到的元素的下標; -
int binarySearch(List<? extends T> list, T key, Comparator<? super T> c)
:二分查詢,傳入一個比較器; -
void reverse(List<?> list)
:翻轉 list; -
void shuffle(List<?> list)
:打亂 list; -
void shuffle(List<?> list, Random rnd)
:以 rnd 為種子打亂 list; -
void swap(List<?> list, int i, int j)
:交換兩個元素的位置; -
void fill(List<? super T> list, T obj)
:將所有元素都賦為 obj; -
void copy(List<? super T> dest, List<? extends T> src)
:將 src 的內容拷貝到 dest 中; -
T min(Collection<? extends T> coll)
:獲得最小值,coll 要實現 Comparable 介面; -
T min(Collection<? extends T> coll, Comparator<? super T> comp)
:根據 comp 獲得最小元素; -
T max(Collection<? extends T> coll)
:獲得最大元素; -
T max(Collection<? extends T> coll, Comparator<? super T> comp)
:獲得最大元素; -
void rotate(List<?> list, int distance)
:可以理解為迴圈列表的整體平移操作; -
boolean replaceAll(List<T> list, T oldVal, T newVal)
; -
int indexOfSubList(List<?> source, List<?> target)
:查詢字列; -
int lastIndexOfSubList(List<?> source, List<?> target)
:找最後一個字列;
5.2 Arrays
這個類中的方法都是對陣列進行操作的。
-
void sort()
:可以對基本型別的陣列進行排序,也可以排自定義類的陣列,也可以傳入 Comparator,還可以指定排序的起始和終止位置; -
void parallelSort()
:歸併排序; -
int binarySearch()
:二分查詢; -
boolean equals()
:判斷兩陣列是否相等; -
void fill()
:將陣列用 obj 填滿; -
T[] copyOf(T[] original, int newLength)
:深拷貝; -
T[] copyOfRange(T[] original, int from, int to)
:區間深拷貝; -
List<T> asList(T... a)
:轉 ArrayList;