11、集合


在任意時刻和任意位置創建任意數量的對象。
數組:具有固定的大小尺寸。
集合類:基本的類型有 List 、 Set 、 Queue 和 Map。這些類型也被稱作容器類(container classes)。
Set 對於每個值都只保存一個對象。
Map 是一個關聯數組,允許將某些對象與其他對象關聯起來。
Java集合類都可以自動地調整自己的大小。

1、類型安全的集合

通過使用泛型,就可以在編譯期防止將錯誤類型的對象放置到集合中。
類型推斷(type inference)

2、基本概念

Java集合類庫採用“持有對象”(holding objects)的思想,並將其分爲兩個不同的概念,表示爲類庫的基本接口:

  • 集合(Collection) :一個獨立元素的序列,這些元素都服從一條或多條規則。List 必須以插入的順序保存元素, Set 不能包含重複元素, Queue 按照排隊規則來確定對象產生的順序(通常與它們被插入的順序相同)。
  • 映射(Map) : 一組成對的“鍵值對”對象,允許使用鍵來查找值。 ArrayList 使用數字來查找對象,因此在某種意義上講,它是將數字和對象關聯在一起。 map 允許我們使用一個對象來查找另一個對象,它也被稱作關聯數組(associative array),因爲它將對象和其它對象關聯在一起;或者稱作字典(dictionary),因爲可以使用一個鍵對象來查找值對象,就像在字典中使用單詞查找定義一樣。 Map 是強大的編程工具。

Collection 接口概括了序列的概念——一種存放一組對象的方式。

3、添加組元素

Arrays.asList() 方法接受一個數組或是逗號分隔的元素列表(使用可變參數),並將其轉換爲 List 對象。 Collections.addAll() 方法接受一個 Collection 對象,以及一個數組或是一個逗號分隔的列表,將其中元素添加到 Collection 中。

4、集合的打印

Collection 類型在每個槽中只能保存一個元素。此類集合包括: List ,它以特定的順序保存一組元素; Set ,其中元素不允許重複; Queue ,只能在集合一端插入對象,並從另一端移除對象。

Map 在每個槽中存放了兩個元素,即鍵和與之關聯的值。鍵和值保存在 HashMap 中的順序不是插入順序,因爲 HashMap 實現使用了非常快速的算法來控制順序。 TreeMap 通過比較結果的升序來保存鍵, LinkedHashMap 在保持 HashMap 查找速度的同時按鍵的插入順序保存鍵。

5、List

  • 基本的 ArrayList ,擅長隨機訪問元素,但在 List 中間插入和刪除元素時速度較慢.
  • LinkedList 對於隨機訪問來說相對較慢,通過代價較低的在 List 中間進行的插入和刪除操作,提供了優化的順序訪問。

5.1 ArrayList

列表接口的可調整大小的數組實現。實現所有可選的列表操作,並允許所有元素,包括null。除了實現List接口之外,該類還提供了一些方法來操作數組的大小,該數組在內部用於存儲List。

size、isEmpty、get、set、iterator和listIterator操作在常數時間內運行。
add操作在平攤常數時間(amortized constant time)內運行,也就是說,添加n個元素需要O(n)個時間。

每個ArrayList實例都有一個容量。容量是用於存儲列表中的元素的數組的大小。它總是至少與列表大小一樣大。當元素被添加到ArrayList中時,它的容量會自動增長。 The details of the growth policy are not specified beyond the fact that adding an element has constant amortized time cost.

在添加大量元素之前,使用ensureCapacity操作,應用程序可以增加ArrayList實例的容量。這可以減少增加的從新分配的數量。

注意,這個實現不是同步的。
如果多個線程同時訪問一個ArrayList實例,並且至少有一個線程從結構上修改了這個列表,那麼它必須在外部進行同步。(結構修改是添加或刪除一個或多個元素,或顯式調整背景數組大小的任何操作;僅僅設置元素的值不是結構修改。)這通常是通過對一些自然封裝列表的對象進行同步來實現的。如果不存在這樣的對象,則應該使用 Collections.synchronizedList方法對列表進行“包裝”。

List list = Collections.synchronizedList(new ArrayList(...));

這個類的iterator和listIterator方法返回的迭代器是快速失效的:如果在創建迭代器之後的任何時候,以任何方式(除了通過迭代器自己的刪除或添加方法)對列表進行結構修改,迭代器將拋出ConcurrentModificationException異常。因此,在面對併發修改時,迭代器會快速而乾淨地失敗,而不是在將來某個不確定的時間冒任意的、不確定的行爲的風險。

注意,不能保證迭代器的快速故障行爲,因爲通常來說,在存在非同步併發修改的情況下,不可能做出任何嚴格的保證。因此,編寫一個依賴於這個異常的正確性的程序是錯誤的:迭代器的快速故障行爲應該只用於檢測bug。

5.2 、LinkedList

雙鏈表實現了列表和Deque接口。實現所有可選的列表操作,並允許所有元素(包括null)。
所有操作的執行都符合雙鏈表的預期。索引到列表中的操作將從頭或尾遍歷列表,以更接近指定索引的操作爲準。
LinkedList 還添加了一些方法,使其可以被用作棧、隊列或雙端隊列(deque)

注意,這個實現不是同步的。
如果多個線程同時訪問一個linked list實例,並且至少有一個線程從結構上修改了這個列表,那麼它必須在外部進行同步。(結構修改是添加或刪除一個或多個元素,或顯式調整背景數組大小的任何操作;僅僅設置元素的值不是結構修改。)這通常是通過對一些自然封裝列表的對象進行同步來實現的。如果不存在這樣的對象,則應該使用 Collections.synchronizedList方法對列表進行“包裝”。

List list = Collections.synchronizedList(new ArrayList(...));

這個類的iterator和listIterator方法返回的迭代器是快速失效的:如果在創建迭代器之後的任何時候,以任何方式(除了通過迭代器自己的刪除或添加方法)對列表進行結構修改,迭代器將拋出ConcurrentModificationException異常。因此,在面對併發修改時,迭代器會快速而乾淨地失敗,而不是在將來某個不確定的時間冒任意的、不確定的行爲的風險。

注意,不能保證迭代器的快速故障行爲,因爲通常來說,在存在非同步併發修改的情況下,不可能做出任何嚴格的保證。因此,編寫一個依賴於這個異常的正確性的程序是錯誤的:迭代器的快速故障行爲應該只用於檢測bug。

  • getFirst()element() 是相同的,它們都返回列表的頭部(第一個元素)而並不刪除它,如果 List 爲空,則拋出 NoSuchElementException 異常。 peek() 方法與這兩個方法只是稍有差異,它在列表爲空時返回 null 。
  • removeFirst()remove() 也是相同的,它們刪除並返回列表的頭部元素,並在列表爲空時拋出 NoSuchElementException 異常。 poll() 稍有差異,它在列表爲空時返回 null 。
  • addFirst() 在列表的開頭插入一個元素。
  • offer()add()addLast() 相同。 它們都在列表的尾部(末尾)添加一個元素。
  • removeLast() 刪除並返回列表的最後一個元素

6、迭代器

迭代器是一個對象,它在一個序列中移動並選擇該序列中的每個對象,而客戶端程序員不知道或不關心該序列的底層結構。
Java 的 Iterator 只能單向移動。這個 Iterator 只能用來:

  • 使用 iterator() 方法要求集合返回一個 Iterator。 Iterator 將準備好返回序列中的第一個元素。
  • 使用 next() 方法獲得序列中的下一個元素。
  • 使用 hasNext() 方法檢查序列中是否還有元素。
  • 使用 remove() 方法將迭代器最近返回的那個元素刪除。

能夠將遍歷序列的操作與該序列的底層結構分離。

6.1、ListIterator

只能由各種 List 類生成,ListIterator 可以

  • 雙向移動

  • 生成相對於迭代器在列表中指向的當前位置的後一個和前一個元素的索引

  • 使用 set() 方法替換它訪問過的最近一個元素

  • 通過調用 listIterator() 方法來生成指向 List 開頭處的 ListIterator

  • 通過調用 listIterator(n) 創建一個一開始就指向列表索引號爲 n 的元素處的 ListIterator

8、Stack

堆棧是“後進先出”(LIFO)集合,ArrayDeque ,其中包含直接實現堆棧功能的方法。是Deque接口的可調整大小的數組實現。

Array deques沒有容量限制;它們根據需要增長以支持使用。它們不是線程安全的;在缺乏外部同步的情況下,它們不支持多線程的併發訪問。禁止空元素。這個類用作堆棧時可能比Stack快,用作隊列時可能比LinkedList快。

大部分的矩陣運算都是在平攤常數時間內進行的。除了remove、removeFirstOccurrence、removeLastOccurrence、contains、iterator.remove()和bulk操作,這些操作都在線性時間內運行。

這個類的迭代器方法返回的迭代器是快速失效的:如果deque在迭代器創建後的任何時候被修改,除了通過迭代器自己的remove方法之外的任何方式,迭代器通常會拋出ConcurrentModificationException。因此,在面對併發修改時,迭代器會快速而乾淨地失敗,而不是在將來某個不確定的時間冒任意的、不確定的行爲的風險。

注意,不能保證迭代器的快速故障行爲,因爲通常來說,在存在非同步併發修改的情況下,不可能做出任何嚴格的保證。故障快速迭代器在最大努力的基礎上拋出ConcurrentModificationException。因此,編寫一個依賴於這個異常的正確性的程序是錯誤的:迭代器的快速故障行爲應該只用於檢測bug。

該類及其迭代器實現集合和迭代器接口的所有可選方法。
該類是Java集合框架的成員。

9、Set

Set 最常見的用途是測試歸屬性,可以很輕鬆地詢問某個對象是否在一個 Set 中。
Set 就是一個 Collection ,只是行爲不同。

  • TreeSet 將元素存儲在紅-黑樹數據結構中。

  • HashSet 使用散列函數。

  • LinkedHashSet 因爲查詢速度的原因也使用了散列,但是看起來使用了鏈表來維護元素的插入順序。

最常見的操作之一是使用 contains() 測試成員歸屬性。

10、Map

將對象映射到其他對象的能力是解決編程問題的有效方法。
Map 與數組和其他的 Collection 一樣,可以輕鬆地擴展到多個維度,只需要創建一個值爲 Map 的 Map(這些 Map 的值可以是其他集合,甚至是其他 Map)。因此,能夠很容易地將集合組合起來以快速生成強大的數據結構。

11、Queue

隊列是一個典型的“先進先出”(FIFO)集合。隊列在併發編程中尤爲重要,因爲它們可以安全地將對象從一個任務傳輸到另一個任務。

LinkedList 實現了 Queue 接口,並且提供了一些方法以支持隊列行爲,因此 LinkedList 可以用作 Queue 的一種實現。

Queue<Integer> queue = new LinkedList<>();

11.1、PriorityQueue

優先級隊列聲明下一個彈出的元素是最需要的元素(具有最高的優先級)。
當在 PriorityQueue 上調用 offer() 方法來插入一個對象時,該對象會在隊列中被排序。默認的排序使用隊列中對象的自然順序(natural order),但是可以通過提供自己的 Comparator 來修改這個順序。 PriorityQueue 確保在調用 peek() , poll() 或 remove() 方法時,獲得的元素將是隊列中優先級最高的元素。
優先級隊列算法通常會按插入順序排序(維護一個堆),但它們也可以在刪除時選擇最重要的元素。 如果對象的優先級在它在隊列中等待時可以修改,那麼算法的選擇就顯得很重要了。

Collection 是所有序列集合共有的根接口。使用接口描述的一個理由是它可以使我們創建更通用的代碼。
實現 Collection 就意味着需要提供 iterator() 方法。

12、集合與迭代

Collection 是所有序列集合共有的根接口。

當需要實現一個不是 Collection 的外部類時,由於讓它去實現 Collection 接口可能非常困難或麻煩,因此使用 Iterator 就會變得非常吸引人。如果實現了 Collection ,就必須實現 iterator()。這時,繼承並提供創建迭代器更好。

13、for-in和迭代器

for-in 語句適用於數組或其它任何 Iterable。Java 5 引入了一個名爲 Iterable 的接口,該接口包含一個能夠生成 Iterator 的 iterator() 方法。for-in 使用此 Iterable 接口來遍歷序列。

嘗試將數組作爲一個 Iterable 參數傳遞會導致失敗。這說明不存在任何從數組到 Iterable 的自動轉換; 必須手工執行這種轉換。

小結

Java 提供了許多保存對象的方法:

  • 數組將數字索引與對象相關聯。它保存類型明確的對象,因此在查找對象時不必對結果做類型轉換。它可以是多維的,可以保存基本類型的數據。雖然可以在運行時創建數組,但是一旦創建數組,就無法更改數組的大小。

  • Collection 保存單一的元素,而 Map 包含相關聯的鍵值對。使用 Java
    泛型,可以指定集合中保存的對象的類型,因此不能將錯誤類型的對象放入集合中,並且在從集合中獲取元素時,不必進行類型轉換。各種
    Collection 和各種 Map
    都可以在你向其中添加更多的元素時,自動調整其尺寸大小。集合不能保存基本類型,但自動裝箱機制會負責執行基本類型和集合中保存的包裝類型之間的雙向轉換。

  • 像數組一樣, List 也將數字索引與對象相關聯,因此,數組和 List 都是有序集合。

  • 如果要執行大量的隨機訪問,則使用 ArrayList ,如果要經常從表中間插入或刪除元素,則應該使用 LinkedList 。

  • 隊列和堆棧的行爲是通過 LinkedList 提供的。

  • Map 是一種將對象(而非數字)與對象相關聯的設計。 HashMap 專爲快速訪問而設計,而 TreeMap
    保持鍵始終處於排序狀態,所以沒有 HashMap 快。 LinkedHashMap 按插入順序保存其元素,但使用散列提供快速訪問的能力。

  • Set 不接受重複元素。 HashSet 提供最快的查詢速度,而 TreeSet 保持元素處於排序狀態。 LinkedHashSet
    按插入順序保存其元素,但使用散列提供快速訪問的能力。

  • 不要在新代碼中使用遺留類 Vector ,Hashtable 和 Stack 。
    在這裏插入圖片描述在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章