java核心技術卷I-具體的集合

具體的集合

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

鏈表

數組和數組列表都有一個重大的缺陷。這就是從數組的中間位置刪除一個元素要付出很大的代價,其原因是數組中處於被刪除元素之後的所有元素都要向數組的前端移動。在數組中間的位置上插入一個元素也是如此。
鏈表( linked list) 解決了這個問題。儘管數組在連續的存儲位置上存放對象引用, 但鏈表卻將每個對象存放在獨立的結點中。每個結點還存放着序列中下一個結點的引用。在 Java 程序設計語言中,所有鏈表實際上都是雙向鏈接的(doubly linked)—即每個結點還存放着指向前驅結點的引用
在這裏插入圖片描述
從鏈表中間刪除一個元素是一個很輕鬆的操作, 即需要更新被刪除元素附近的鏈接
在這裏插入圖片描述
鏈表是 一 個 有 序 集 合( ordered collection), 每個對象的位置十分重要。LinkedList.add 方法將對象添加到鏈表的尾部。但是,常常需要將元素添加到鏈表的中間。由於迭代器是描述集合中位置的, 所以這種依賴於位置的 add 方法將由迭代器負責。只有對自然有序的集合使用迭代器添加元素纔有實際意義。
在 Iterator 接口中就沒有add 方法。相反地,集合類庫提供了子接口 Listlterator, 其 中 包 含 add方法

interface ListIterator<E> extends Iterator<E>{
	void add(E element);
	...
}

與 Collection.add 不同, 這個方法不返回 boolean 類型的值, 它假定添加操作總會改變鏈表。另外,Listlterator 接口有兩個方法,可以用來反向遍歷鏈表。與 next 方法一樣, previous 方法返回越過的對象。

E previous()
boolean hasPrevious()

LinkedList 類的 listlterator 方法返回一個實現了 Listlterator 接口的迭代器對象

ListIterator<String> iter = staff.listiterator();

Add 方法在迭代器位置之前添加一個新對象。add 方法只依賴於迭代器的位置, 而 remove 方法依賴於迭代器的狀態。
可以想象, 如果在某個迭代器修改集合時, 另一個迭代器對其進行遍歷,一定會出現混亂的狀況。例如,一個迭代器指向另一個迭代器剛剛刪除的元素前面,現在這個迭代器就是無效的,並且不應該再使用。鏈表迭代器的設計使它能夠檢測到這種修改。如果迭代器發現它的集合被另一個迭代器修改了, 或是被該集合自身的方法修改了, 就會拋出一個ConcurrentModificationException 異常。

List<String> list = . .
ListIterator<String> iterl = list.listiterator();
ListIterator<String> iter2 = list.listiterator();
iterl.next();
iterl.remove();
iter2.next(); // throws ConcurrentModificationException

爲了避免發生併發修改的異常,請遵循下述簡單規則:可以根據需要給容器附加許多的迭代器,但是這些迭代器只能讀取列表。另外,再單獨附加一個既能讀又能寫的迭代器。
鏈表不支持快速地隨機訪問。如果要查看鏈表中第n個元素,就必須從頭開始, 越過n-1個元素。沒有捷徑可走。
儘管如此, LinkedList 類還是提供了一個用來訪問某個特定元素的 get 方法:

LinkedList<String> list = . .;
String obj = list.get(n);

數組列表

ArrayList 封裝了一個動態再分配的對象數組。
在需要動態數組時, 可能會使用 Vector 類。爲什麼要用 ArrayList 取代 Vector 呢? 原因很簡單:Vector 類的所有方法都是同步的。 可以由兩個線程安全地訪問一個 Vector 對象。但是, 如果由一個線程訪問 Vector, 代碼要在同步操作上耗費大量的時間。這種情況還是很常見的。而 ArrayList 方法不是同步的,因此,建議在不需要同步時使用 ArrayList, 而不要使用 Vector。

散列集

散列表爲每個對象計算一個整數, 稱爲散列碼(hashcode。) 散列碼是由對象的實例域產生的一個整數。更準確地說, 具有不同數據域的對象將產生不同的散列碼。
如果自定義類,就要負責實現這個類的 hashCode方法。注意,自己實現的 hashCode方法應該與 equals 方法兼容,即如果 a.equals(b) 爲 true, a 與 b 必須具有相同的散列碼。
在 Java 中,散列表用鏈表數組實現。每個列表被稱爲桶( bucket) 。 要想査找表中對象的位置, 就要先計算它的散列碼, 然後與桶的總數取餘, 所得到的結果就是保存這個元素的桶的索引。例如, 如果某個對象的散列碼爲 76268, 並且有 128 個桶, 對象應該保存在第 108 號桶中(76268除以 128餘 108 )。或許會很幸運, 在這個桶中沒有其他元素,此時將元素直接插人到桶中就可以了。
在這裏插入圖片描述
當然,有時候會遇到桶被佔滿的情況, 這也是不可避免的。這種現象被稱爲散列衝突( hash collision)。這時, 需要用新對象與桶中的所有對象進行比較,査看這個對象是否已經存在。如果散列碼是合理且隨機分佈的, 桶的數目也足夠大, 需要比較的次數就會很少。
散列表可以用於實現幾個重要的數據結構。 其中最簡單的是 set 類型。set 是沒有重複元素的元素集合。set 的 add方法首先在集中查找要添加的對象,如果不存在,就將這個對象添加進去。
Java 集合類庫提供了一個 HashSet 類,它實現了基於散列表的集。可以用 add 方法添加元素。contains方法已經被重新定義,用來快速地查看是否某個元素已經出現在集中。它只在某個桶中査找元素,而不必查看集合中的所有元素

樹集

TreeSet 類與散列集十分類似, 不過, 它比散列集有所改進。樹集是一個有序集合( sorted collection) 。 可以以任意順序將元素插入到集合中。在對集合進行遍歷時,每個值將自動地按照排序後的順序呈現。
正如 TreeSet 類名所示,排序是用樹結構完成的,當前實現使用的是紅黑樹(red-black tree)。每次將一個元素添加到樹中時,都被放置在正確的排序位置上。因此,迭代器總是以排好序的順序訪問每個元素。要使用樹集, 必須能夠比較元素。這些元素必須實現 Comparable 接口 或者構造集時必須提供一個 Comparator

隊列與雙端隊列

隊列可以讓人們有效地在尾部添加一個元素, 在頭部刪除一個元素。有兩個端頭的隊列, 即雙端隊列,可以讓人們有效地在頭部和尾部同時添加或刪除元素。不支持在隊列中間添加元素。在 Java SE 6中引人了 Deque 接口,並由 ArrayDeque 和LinkedList 類實現。這兩個類都提供了雙端隊列,而且在必要時可以增加隊列的長度。

優先級隊列

優先級隊列(priority queue) 中的元素可以按照任意的順序插人,卻總是按照排序的順序進行檢索。也就是說,無論何時調用 remove 方法,總會獲得當前優先級隊列中最小的元素。然而,優先級隊列並沒有對所有的元素進行排序。如果用迭代的方式處理這些元素,並不需要對它們進行排序。優先級隊列使用了一個優雅且高效的數據結構,稱爲堆(heap)。堆是一個可以自我調整的二叉樹,對樹執行添加( add) 和刪除(remore) 操作, 可以讓最小的元素移動到根,而不必花費時間對元素進行排序。
與 TreeSet—樣,一個優先級隊列既可以保存實現了 Comparable 接口的類對象, 也可以保存在構造器中提供的 Comparator 對象。
使用優先級隊列的典型示例是任務調度。每一個任務有一個優先級,任務以隨機順序添加到隊列中。每當啓動一個新的任務時,都將優先級最高的任務從隊列中刪除

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