計算機程序的思惟邏輯 (55) - 容器類總結

本系列文章經補充和完善,已修訂整理成書《Java編程的邏輯》(馬俊昌著),由機械工業出版社華章分社出版,於2018年1月上市熱銷,讀者好評如潮!各大網店和書店有售,歡迎購買:京東自營連接 html

38節54節,咱們介紹了多種容器類,本節進行簡要總結,咱們主要從三個角度進行總結:算法

  • 用法和特色
  • 數據結構和算法
  • 設計思惟和模式

用法和特色

咱們在52節展現過一張圖,其中包含了容器類主要的接口和類,咱們仍是用這個圖總結一下:編程

容器類有兩個根接口,分別是Collection和Map,Collection表示單個元素的集合,Map表示鍵值對的集合。設計模式

Collection表示的數據集合有基本的增、刪、查、遍歷等方法,但沒有定義元素間的順序或位置,也沒有規定是否有重複元素。數組

List是Collection的子接口,表示有順序或位置的數據集合,增長了根據索引位置進行操做的方法。它有兩個主要的實現類,ArrayListLinkedList,ArrayList基於數組實現,LinkedList基於鏈表實現,ArrayList的隨機訪問效率很高,但從中間插入和刪除元素須要移動元素,效率比較低,LinkedList則正好相反,隨機訪問效率比較低,但增刪元素只須要調整鄰近節點的連接。緩存

Set也是Collection的子接口,它沒有增長新的方法,但保證不含重複元素。它有兩個主要的實現類,HashSetTreeSet,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對象,操做效率稍低,但能夠按鍵有序。dom

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>>

數據結構和算法

在容器類中,咱們看到了以下數據結構的應用:

  • 動態數組:ArrayList內部就是動態數組,HashMap內部的鏈表數組也是動態擴展的,ArrayDeque和PriorityQueue內部也都是動態擴展的數組。
  • 鏈表:LinkedList是用雙向鏈表實現的,HashMap中映射到同一個鏈表數組的鍵值對是經過單向鏈表連接起來的,LinkedHashMap中每一個元素還加入到了一個雙向鏈表中以維護插入或訪問順序。
  • 哈希表:HashMap是用哈希表實現的,HashSet, LinkedHashSet和LinkedHashMap基於HashMap,內部固然也是哈希表。
  • 排序二叉樹:TreeMap是用紅黑樹(基於排序二叉樹)實現的,TreeSet內部使用TreeMap,固然也是紅黑樹,紅黑樹能保持元素的順序且綜合性能很高。
  • :PriorityQueue是用堆實現的,堆邏輯上是樹,物理上是動態數組,堆能夠高效地解決一些其餘數據結構難以解決的問題。
  • 循環數組:ArrayDeque是用循環數組實現的,經過對頭尾變量的維護,實現了高效的隊列操做。
  • 位向量:EnumSet是用位向量實現的,對於只有兩種狀態,且須要進行集合運算的數據,使用位向量進行表示、位運算進行處理,精簡且高效。

每種數據結構中每每包含必定的算法策略,這種策略每每是一種折中,好比:

  • 動態擴展算法:動態數組的擴展策略,通常是指數級擴展的,是在兩方面進行平衡,一方面是但願減小內存消耗,另外一方面但願減小內存分配、移動和拷貝的開銷。
  • 哈希算法:哈希表中鍵映射到鏈表數組索引的算法,算法要快,同時要儘可能隨機和均勻。
  • 排序二叉樹的平衡算法:排序二叉樹的平衡很是重要,紅黑樹是一種平衡算法,AVL樹是另外一種,平衡算法一方面要保證儘可能平衡,另外一方面要儘可能減小綜合開銷。

Collections實現了一些通用算法,好比二分查找、排序、翻轉列表順序、隨機化重排、循環移位等,在實現大部分算法時,Collections也都根據容器大小和是否實現了RandomAccess接口採用了不一樣的實現方式。

設計思惟和模式

在容器類中,咱們也看到了Java的多種語言機制和設計思惟的運用:

  • 封裝:封裝就是提供簡單接口,並隱藏實現細節,這是程序設計的最重要思惟。在容器類中,不少類、方法和變量都是私有的,好比迭代器方法,基本都是經過私有內部類或匿名內部類實現的。
  • 繼承和多態:繼承能夠複用代碼,便於按父類統一處理,但咱們也說過,繼承是一把雙刃劍。在容器類中,Collection是父接口,List/Set/Queue繼承自Collection,經過Collection接口能夠統一處理多種類型的集合對象。容器類定義了不少抽象容器類,具體類經過繼承它們以複用代碼,每一個抽象容器類都有詳細的文檔說明,描述其實現機制,以及子類應該如何重寫方法。容器類的設計展現了接口繼承、類繼承、以及抽象類的恰當應用。
  • 組合:通常而言,組合應該優先於繼承,咱們看到HashSet經過組合的方式使用HashMap,TreeSet經過組合使用TreeMap,適配器和裝飾器模式也都是經過組合實現的。
  • 接口:面向接口編程是一種重要的思惟,可下降代碼間的耦合,提升代碼複用程度,在容器類方法中,接受的參數和返回值每每都是接口,Collections提供的通用算法,操做的也都是接口對象,咱們平時在使用容器類時,通常也只在建立對象時使用具體類,而其餘地方都使用接口。
  • 設計模式:咱們在容器類中看到了迭代器、工廠方法、適配器、裝飾器等多種設計模式的應用。

小結

本節咱們從用法和特色、數據結構和算法、以及設計思惟和模式三個角度簡要總結了以前介紹的各類容器類。到此爲止,關於容器類咱們就介紹完了。

到目前爲止,咱們尚未接觸過文件處理,而咱們在平常的電腦操做中,接觸最多的就是各類文件了,讓咱們從下一節開始,一塊兒探討文件操做。


未完待續,查看最新文章,敬請關注微信公衆號「老馬說編程」(掃描下方二維碼),深刻淺出,老馬和你一塊兒探索Java編程及計算機技術的本質。用心原創,保留全部版權。

相關文章
相關標籤/搜索