程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> 計算機程序的思維邏輯 (55),思維55

計算機程序的思維邏輯 (55),思維55

編輯:JAVA綜合教程

計算機程序的思維邏輯 (55),思維55


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

數據結構和算法

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

  • 動態數組: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提供的通用算法,操作的也都是接口對象,我們平時在使用容器類時,一般也只在創建對象時使用具體類,而其他地方都使用接口。
  • 設計模式:我們在容器類中看到了迭代器、工廠方法、適配器、裝飾器等多種設計模式的應用。

小結

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

到目前為止,我們還沒有接觸過文件處理,而我們在日常的電腦操作中,接觸最多的就是各種文件了,讓我們從下一節開始,一起探討文件操作。

---------------------

更多相關原創文章

(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編程及計算機技術的本質。用心原創,保留所有版權。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved