1. 程式人生 > 其它 >Java集合框架筆記

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> 集合,但是內部儲存從某種程度來說是有序的,方便遍歷。

這些多種多樣的容器跟比較器(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):返回從 fromElementtoElement 子集合;
  • 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;