1. 程式人生 > >java容器篇

java容器篇

改變 持久化 except util exception 控制 vector fail 1.8

容器
-Collection 存儲對象的集合;Map 存儲鍵值對的映射表
-Iterator(叠代器模式)
-集合訪問器,用於循環訪問集合中的對象
-所有實現了Collection接口的容器類都有iterator方法,用於返回一個實現了Iterator接口的對象。Iterator對象稱作叠代器,Iterator接口方法能以叠代方式逐個訪問集合中各個元素,並可以從Collection中除去適當的元素
-Collection
-set(特征:無序且不可重復)
-TreeSet:基於紅黑樹實現,支持有序性操作,例如根據一個範圍查找元素的操作。但是查找效率不如 HashSet,HashSet 查找的時間復雜度為 O(1),TreeSet 則為 O(logN)。
-HashSet:基於哈希表實現,支持快速查找,但不支持有序性操作。並且失去了元素的插入順序信息,也就是說使用 Iterator 遍歷 HashSet 得到的結果是不確定的
-LinkedHashSet:具有 HashSet 的查找效率,且內部使用雙向鏈表維護元素的插入順序
-紅黑樹:漫畫看懂紅黑樹 鏈接:https://www.sohu.com/a/201923614_466939
-list(特征:有序且可重復)
-ArrayList:基於動態數組實現,支持隨機訪問。
-概覽
-實現了 RandomAccess 接口,因此支持隨機訪問。這是理所當然的,因為 ArrayList 是基於數組實現的,其數組的默認大小為 10。
-序列化
-基於數組實現,並且具有動態擴容特性,因此保存元素的數組不一定都會被使用,那麽就沒必要全部進行序列
transient Object[] A; //transient關鍵字聲明數組默認不會被序列化
-為什麽定義A數組要用transient關鍵字修飾,使其默認不被序列化?
假如現在實際有了5個元素,而elementData的大小可能是10,那麽在序列化時只需要儲存5個元素,數組中的最後五個元素是沒有實際意義的,不需要儲存。所以ArrayList的設計者將elementData設計為transient,然後在writeObject方法中手動將其序列化,並且只序列化了實際存儲的那些元素,而不是整個數組
-序列化時需要使用 ObjectOutputStream 的 writeObject() 將對象轉換為字節流並輸出。而 writeObject() 方法在傳入的對象存在 writeObject() 的時候會去反射調用該對象的 writeObject() 來實現序列化。反序列化使用的是 ObjectInputStream 的 readObject() 方法,原理類似。
-java中序列化的目的:
-以某種存儲形式使自定義對象持久化;
-將對象從一個地方傳遞到另一個地方。
-使程序更具維護性
-擴容
-添加元素時使用 ensureCapacityInternal() 方法來保證容量足夠,如果不夠時,需要使用 grow() 方法進行擴容,新容量的大小為 oldCapacity + (oldCapacity >> 1),也就是舊容量的 1.5 倍
-擴容操作需要調用 Arrays.copyOf() 把原數組整個復制到新數組中,這個操作代價很高,因此最好在創建 ArrayList 對象時就指定大概的容量大小,減少擴容操作的次數
-刪除元素
-需要調用 System.arraycopy() 將 index+1 後面的元素都復制到 index 位置上,該操作的時間復雜度為 O(N), ArrayList 刪除元素的代價是非常高的。
-fail-fast
-modCount 用來記錄 ArrayList 結構發生變化的次數。結構發生變化是指添加或者刪除至少一個元素的所有操作,或者是調整內部數組的大小,僅僅只是設置元素的值不算結構發生變化
-在進行序列化或者叠代等操作時,需要比較操作前後 modCount 是否改變,如果改變了需要拋出 ConcurrentModificationException。
-fail-fast與fail-safe
-fail-fast
-fail-fast機制在遍歷一個集合時,當集合結構被修改,會拋出ConcurrentModificationException。
-java.util包下的集合類都是快速失敗的,不能在多線程下發生並發修改(叠代過程中被修改)。
-fail-safe
-fail-safe任何對集合結構的修改都會在一個復制的集合上進行修改,不像fail-fast在原集合上修改,因此不會拋出ConcurrentModificationException
-java.util.concurrent包下的容器都是安全失敗,可以在多線程下並發使用,並發修改。
-優點
-避免了ConcurrentModificationException
-缺點
-需要復制集合,產生大量的無效對象,開銷大
-無法保證讀取的數據是目前原始數據結構中的數據。
-叠代器並不能訪問到修改後的內容,即:叠代器遍歷的是開始遍歷那一刻拿到的集合拷貝,在遍歷期間原集合發生的修改叠代器是不知道的。
-Vector:和 ArrayList 類似,但它是線程安全的。
-它的實現與 ArrayList 類似,但是使用了 synchronized 進行同步。因此是線程安全的
-與ArrayList比較
-Vector 是同步的,因此開銷就比 ArrayList 要大,訪問速度更慢。最好使用 ArrayList 而不是 Vector,因為同步操作完全可以由程序員自己來控制;
-Vector 每次擴容請求其大小的 2 倍空間,而 ArrayList 是 1.5 倍。
-LinkedList:基於雙向鏈表實現,只能順序訪問,但是可以快速地在鏈表中間插入和刪除元素。不僅如此,LinkedList 還可以用作棧、隊列和雙向隊列。
-概覽
-基於雙向鏈表實現,使用 Node 存儲鏈表節點信息。
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
}
每個鏈表存儲了 first 和 last 指針
transient Node<E> first;
transient Node<E> last;
-與 ArrayList 的比較
-ArrayList 基於動態數組實現,LinkedList 基於雙向鏈表實現;
-ArrayList 支持隨機訪問,LinkedList 不支持;
-LinkedList 在任意位置添加刪除元素更快。
-Map
-TreeMap:基於紅黑樹實現
-HashMap:基於哈希表實現。
-存儲結構
-內部包含了一個 Entry 類型的數組 table。
transient Entry[] table;
Entry 存儲著鍵值對。它包含了四個字段,從 next 字段我們可以看出 Entry 是一個鏈表。即數組中的每個位置被當成一個桶,一個桶存放一個鏈表。HashMap 使用拉鏈法來解決沖突,同一個鏈表中存放哈希值相同的 Entry。
{{{{{{{entry結構圖}}}}}}}
- 拉鏈法的工作原理
HashMap<String, String> map = new HashMap<>();
map.put("K1", "V1");
map.put("K2", "V2");
map.put("K3", "V3");
新建一個 HashMap,默認大小為 16;
插入 <K1,V1> 鍵值對,先計算 K1 的 hashCode 為 115,使用除留余數法得到所在的桶下標 115%16=3。
插入 <K2,V2> 鍵值對,先計算 K2 的 hashCode 為 118,使用除留余數法得到所在的桶下標 118%16=6。
插入 <K3,V3> 鍵值對,先計算 K3 的 hashCode 為 118,使用除留余數法得到所在的桶下標 118%16=6,插在 <K2,V2> 前面。

-應該註意到鏈表的插入是以頭插法方式進行的,例如上面的 <K3,V3> 不是插在 <K2,V2> 後面,而是插入在鏈表頭部。
-查找需要分成兩步進行:
-計算鍵值對所在的桶;
-在鏈表上順序查找,時間復雜度顯然和鏈表的長度成正比。
-put操作
-HashMap 允許插入鍵為 null 的鍵值對。但是因為無法調用 null 的 hashCode() 方法,也就無法確定該鍵值對的桶下標,只能通過強制指定一個桶下標來存放。HashMap 使用第 0 個桶存放鍵為 null 的鍵值對。
-確定桶下標
-擴容
-基本原理
-重新計算桶下標
-計算數組容量
-鏈表轉紅黑樹
-從 JDK 1.8 開始,一個桶存儲的鏈表長度大於 8 時會將鏈表轉換為紅黑樹
-與 HashTable 的比較
HashTable 使用 synchronized 來進行同步。
HashMap 可以插入鍵為 null 的 Entry。
HashMap 的叠代器是 fail-fast 叠代器。
HashMap 不能保證隨著時間的推移 Map 中的元素次序是不變的。
-HashTable:和 HashMap 類似,但它是線程安全的,這意味著同一時刻多個線程可以同時寫入 HashTable 並且不會導致數據不一致。它是遺留類,不應該去使用它。現在可以使用 ConcurrentHashMap 來支持線程安全,並且 ConcurrentHashMap 的效率會更高,因為 ConcurrentHashMap 引入了分段鎖。
-LinkedHashMap:使用雙向鏈表來維護元素的順序,順序為插入順序或者最近最少使用(LRU)順序。

java容器篇