從38節到54節,我們介紹了多種容器類,本節進行簡要總結,我們主要從三個角度進行總結:
用法和特點
我們在52節展示過一張圖,其中包含了容器類主要的接口和類,我們還是用這個圖總結一下:
容器類有兩個根接口,分別是Collection和Map,Collection表示單個元素的集合,Map表示鍵值對的集合。
Collection表示的數據集合有基本的增、刪、查、遍歷等方法,但沒有定義元素間的順序或位置,也沒有規定是否有重復元素。
List是Collection的子接口,表示有順序或位置的數據集合,增加了根據索引位置進行操作的方法。它有兩個主要的實現類,ArrayList和LinkedList,ArrayList基於數組實現,LinkedList基於鏈表實現,ArrayList的隨機訪問效率很高,但從中間插入和刪除元素需要移動元素,效率比較低,LinkedList則正好相反,隨機訪問效率比較低,但增刪元素只需要調整鄰近節點的鏈接。
Set也是Collection的子接口,它沒有增加新的方法,但保證不含重復元素。它有兩個主要的實現類,HashSet和TreeSet,HashSet基於哈希表實現,要求鍵重寫hashCode方法,效率更高,但元素間沒有順序,TreeSet基於排序二叉樹實現,元素按比較有序,元素需要實現Comparable接口,或者創建TreeSet時提供一個Comparator對象。HashSet還有一個子類LinkedHashSet可以按插入有序。還有一個針對枚舉類型的實現類EnumSet,它基於位向量實現,效率很高。
Queue是Collection的子接口,表示先進先出的隊列,在尾部添加,從頭部查看或刪除。Deque是Queue的子接口,表示更為通用的雙端隊列,有明確的在頭或尾進行查看、添加和刪除的方法。普通隊列有兩個主要的實現類,LinkedList和ArrayDeque,LinkedList基於鏈表實現,ArrayDeque基於循環數組實現,一般而言,如果只需要Deque接口,ArrayDeque的效率更高一些。
Deque還有一個特殊的實現類PriorityQueue,它表示優先級隊列,內部是用堆實現的,堆除了用於實現優先級隊列,還可以高效方便的解決很多其他問題,比如求前K個最大的元素、求中值等。
Map接口表示鍵值對集合,經常根據鍵進行操作,它有兩個主要的實現類,HashMap和TreeMap。HashMap基於哈希表實現,要求鍵重寫hashCode方法,操作效率很高,但元素沒有順序。TreeMap基於排序二叉樹實現,要求鍵實現Comparable接口,或提供一個Comparator對象,操作效率稍低,但可以按鍵有序。
HashMap還有一個子類LinkedHashMap,它可以按插入或訪問有序。之所以能有序,是因為每個元素還加入到了一個雙向鏈表中。如果鍵本來就是有序的,使用LinkedHashMap而非TreeMap可以提高效率。按訪問有序的特點可以方便的用於實現LRU緩存。
如果鍵為枚舉類型,可以使用專門的實現類EnumMap,它使用效率更高的數組實現。
需要說明的是,我們介紹的各種容器類都不是線程安全的,也就是說,如果多個線程同時讀寫同一個容器對象,是不安全的。如果需要線程安全,可以使用Collections提供的synchronizedXXX方法對容器對象進行同步,或者使用線程安全的專門容器類。
此外,容器類提供的迭代器都有一個特點,都會在迭代中間進行結構性變化檢測,如果容器發生了結構性變化,就會拋出ConcurrentModificationException,所以不能在迭代中間直接調用容器類提供的add/remove方法,如需添加和刪除,應調用迭代器的相關方法。
在解決一個特定問題時,經常需要綜合使用多種容器類。比如要統計一本書中出現次數最多的前10個單詞,可以先使用HashMap統計每個單詞出現的次數,再使用47節介紹的TopK類用PriorityQueue求前十個10單詞,或者使用Collections提供的sort方法。
在之前各節介紹的例子中,為簡單起見,容器中的元素類型往往是簡單的,但需要說明的是,它們也可以是復雜的自定義類型,也可以是容器類型。比如在一個新聞應用中,表示當天的前十大新聞可以用一個List表示,形如List<News>,而為了表示每個分類的前十大新聞,可以用一個Map表示,鍵為分類Category,值為List<News>,形如Map<Category, List<News>>,而表示每天的每個分類的前十大新聞,可以在Map中使用Map,鍵為日期,值也是一個Map,形如Map<Date, Map<Category, List<News>>。
數據結構和算法
在容器類中,我們看到了如下數據結構的應用:
每種數據結構中往往包含一定的算法策略,這種策略往往是一種折中,比如:
Collections實現了一些通用算法,比如二分查找、排序、翻轉列表順序、隨機化重排、循環移位等,在實現大部分算法時,Collections也都根據容器大小和是否實現了RandomAccess接口采用了不同的實現方式。
設計思維和模式
在容器類中,我們也看到了Java的多種語言機制和設計思維的運用:
小結
本節我們從用法和特點、數據結構和算法、以及設計思維和模式三個角度簡要總結了之前介紹的各種容器類。到此為止,關於容器類我們就介紹完了。
到目前為止,我們還沒有接觸過文件處理,而我們在日常的電腦操作中,接觸最多的就是各種文件了,讓我們從下一節開始,一起探討文件操作。
---------------------
更多相關原創文章
(14) 類的組合
(18) 為什麼說繼承是把雙刃劍
(19) 接口的本質
(20) 為什麼要有抽象類?
(21) 內部類的本質
(38) 剖析ArrayList
(39) 剖析LinkedList
(40) 剖析HashMap
(41) 剖析HashSet
(42) 排序二叉樹
(43) 剖析TreeMap
(44) 剖析TreeSet
(45) 神奇的堆
(46) 剖析PriorityQueue
(47) 堆和PriorityQueue的應用
(48) 剖析ArrayDeque
(49) 剖析LinkedHashMap
(50) 剖析EnumMap
(51) 剖析EnumSet
(52) 抽象容器類
(53) 剖析Collections - 算法
(54) 剖析Collections - 設計模式
----------------
未完待續,查看最新文章,敬請關注微信公眾號“老馬說編程”(掃描下方二維碼),從入門到高級,深入淺出,老馬和你一起探索Java編程及計算機技術的本質。用心原創,保留所有版權。