1. 程式人生 > >JDK部分源碼閱讀與理解

JDK部分源碼閱讀與理解

轉換成 有序集合 子集合 之前 對象 淺拷貝 其他 空元素 地方法

本文為博主原創,允許轉載,但請聲明原文地址:http://www.coselding.cn/article/2016/05/31/JDK部分源碼閱讀與理解/

不喜歡重復造輪子,不喜歡貼各種東西、JDK代碼什麽的,讓整篇文章很亂。。。JDK源碼誰都有,沒什麽好貼的。。。如果你沒看過JDK源碼,建議打開Eclipse邊看源碼邊看這篇文章,看過的可以把這篇文章當成是知識點備忘錄。。。

JDK容器類中有大量的空指針、數組越界、狀態異常等異常處理,這些不是重點,我們關註的應該是它的一些底層的具體實現,這篇文章就是我看完這部分源碼之後的覺得有用的知識點總結,渣渣一枚,大神勿噴。。。


ArrayList
1、 容器默認大小為10,位置不夠了自動擴增,每次增加當前長度的一半;(擴增時用Arrays.copyOf進行擴增)
2、 數組容量擴增到Integer.MAX_VALUE-8的時候,就會開始限制數組擴充,超過Integer.MAX_VALUE的時候,拋內存溢出異常;
3、 clone是淺拷貝,List中的引用指向的還是相同數據;
4、 線程不安全的;
5、 叠代器中的expectedModCount和modCount:叠代器初始化時,會用modCount去初始化expectedModCount,在叠代器的操作中都要檢查這兩個值的等值性,不想等拋出ConcurrentModificationException異常表示當前List正在被修改,為什麽呢?如下:
List本身的add和remove操作時都會修改modCount的值,如果在叠代器循環叠代過程中調用了List本身的add和remove方法就會導致兩個值不想等而拋出異常,而叠代器本身的remove則不會有這個問題,它會在remove完成後重新更新一遍expectedModCount保持兩個值的等值性。
6、 ListIterator和普通叠代器類似,只是多了雙向叠代的功能;
7、 ArrayList本身的sort其實是調用Arrays.sort傳入比較器實現的;


LinkedList
1、 沒ArrayList那麽麻煩了,不需要擴容什麽的,處理好指針引用的指向關系就行。
2、 內部維護size,first,last,支持雙向遍歷,鏈表增刪操作時,註意維護好這三個值;
3、 線程不安全;
4、 鏈表支持鏈表頭增刪,鏈表尾增刪,指定節點前或後增刪;
5、 List中的Node為private內部類,無法手動創建,這是為了保證用戶拿到的所有Node都是LinkedList生成的,即其成員next和pre為正確指定的值;
6、 LinkedList中的Element元素支持插入空值,和HashMap一樣;
7、 LinkedList的叠代器中也有expectedModCount檢測鏈表的修改狀態,叠代器循環叠代時只能用叠代器的remove方法;
8、 clone為淺拷貝;
9、 LinkedList可以當Deque用,因為有實現相應接口;


HashMap
1、 HashMap支持null的key和value;
2、 Equals會比較兩個Map地址,若地址不等,則叠代比較每個元素,都相同才返回true;
3、 Hashcode為每個元素Entry的hashcode的和;
4、 Entry是一個鍵值對封裝體,都復寫了Object的很多相應方法,建立了K-V映射關系;
5、 Clone淺拷貝;
6、 HashMap默認大小16,最大大小2的30次方;
7、 結構:一開始使用數據結構書中的以鏈表為元素的數組存儲元素,元素得到的哈希值相同的放在數組中哈希值對應位置的鏈表的末尾,操作很簡單,但是有弊端,當同一個哈希值存在的元素過多的話,查詢速度很差,這不是是同哈希表想看到的結果,這時候采取的是轉換成紅黑樹的辦法。
8、 填充比:其中設置一個填充比,當鏈表中的元素數量超過填充比對應的值的時候,鏈表會轉換成紅黑樹,提高查詢效率,填充比在HashMap的構造函數可以設置,默認0.75。
9、 臨界值:數組中的位置使用率達到臨界值的時候,數組會擴容;
10、 線程不安全;
11、 Put方法:hashCode()計算Key的哈希值,從而找到數組中的元素位置,
(1)該位置中沒元素,直接放入;
(2)該位置中有元素調用equals,對比Key是否真的相等,若相等,用新的Value替代舊的Value;
(3)若equals不相等(沖突),則把這個元素放到原來的元素後面(鏈表)
註:因此作為HashMap的Key的數據類型,有必要好好考慮Key的hashCode和equals重寫,盡量避免沖突,提升效率;
12、 resize():重新調整數組大小,新舊數組復制,較耗時;
13、 get()方法:計算key的hashCode,從而找到數組位置,對比數組中元素第一個節點和key是否一致,一致返回該節點,否則判斷節點是否是紅黑樹節點來分類討論(紅黑樹按照紅黑樹的方式查找該key,鏈表則循環下去接著找),如果找到key一致的則返回,否則返回null,表示該key暫時還沒有值;
14、 put()方法步驟:
(1)檢查表是否需要擴容;
(2)檢查hashCode指定位置是否需要newNode;
(3)數組中指定位置已經有節點了,先檢查和第一個節點是否一致,一致則找到節點;
(4)不一致,檢查是否為紅黑樹,通過紅黑樹查找節點;
(5)不是紅黑樹的,接著鏈表查找,找到元素節點或者找不到新建節點,並檢查是否需要轉換成紅黑樹;
(6)若找到的節點不為空,賦值Value;
(7)檢查數組是否需要擴容;
(8)返回oldValue;
15、 Remove()方法:通過hashCode找到數組位置,對比第一個節點,再對比紅黑樹或鏈表,找到節點之後刪除節點並返回;
16、 HashMap的增刪方法也和List一樣有ConcurrentModificationException異常,使用叠代器需註意;
17、


LinkedHashMap
1、 HashMap的子類;
2、 存儲方式和HashMap都一樣,只是在put元素的時候順帶存儲了元素的存入先後順序,在叠代器遍歷的時候,輸出的結果順序是和插入元素時的插入先後順序一致的;
3、 有ConcurrentModificationException;
4、 可以設置插入元素時自動刪除最舊的元素;


TreeMap
1、 實現了NavigableMap接口(是SortedMap的子接口);
2、 使用紅黑樹作為底層實現,排序平衡樹,有序,提供一系列有序集合才有的方法(獲取第一個,獲取某一範圍內的),排序按照Key為標準進行比較;
3、 默認為升序排序,descendingMap()返回降序排序的實例;
4、 維護了比較器comparator、樹根節點root、size屬性;
5、 putAll方法時,先判斷傳入的集合是否是SortedMap,是的話利用比較器直接通過紅黑樹批量插入再調整紅黑樹,較為高效,若不是,則遍歷整個集合把每個元素一一插入;
6、 元素間比較:若內部比較器不為空,則用內部比較器進行key的比較,否則元素本身要實現Comparable接口以提供比較;
7、 不允許key為空;
8、 Entry就是紅黑樹的節點;
9、 Put:按照排序樹查找key,找到直接修改value,沒找到就插入一個節點,並進行紅黑樹調整;
10、 Remove:按照key查找相應的Entry,再把這個節點從紅黑樹中刪除,並進行紅黑樹調整;
11、 淺拷貝;
12、 Replace要求提供的oldValue要和key對應的Entry的value一致才能成功替換;
13、 非叠代器的增刪方法也有ConcurrentModificationException;


HashTable
1、 Dictionary的子類;
2、 淺拷貝;
3、 Keyset,ValueSet,EntrySet三種值的獲取方式使用了標記位的方式,代碼重用,不像之前的,每個都各寫一個;
4、 Map操作是方法粒度的線程安全的;
5、 不允許key為空或value為空;
6、 遍歷叠代器使用Enumeration方式;
7、 HashMap的get方法返回null表示沒有該key或者該key對應的value為空;而HashTable的get返回空則一定是沒有該key;


IdentityHashMap
1、 和WeakHashMap一樣,用一個特殊值表示null的key;
2、 線程不安全;
3、 淺拷貝;
4、 底層用一個數組實現存儲,下標0,2,4,6…存儲key,下標1,3,5,7…存儲value,
remove時,將相應位置的值賦空,並把後面的元素往前移;
put時一次遞增兩個位置下標來查找;
hashCode采用native底層實現,根據內存地址計算哈希值,兩個key只有==時才算相同,在字符串常量池和堆區的兩個相同字符串不算相等;


HashSet
底層由HashMap提供支持,實現容器中元素單一出現的目的。


TreeSet
1、 有序的Set,元素需要實現Comparable接口,要求compareTo和equals的返回結果一致;
2、 Comparator負責提供給TreeMap比較器;
3、 具有有序集合的一些性質:比如提取比某個值大的子集合,提取在某一範圍內的子集合等有序才具有的操作;
4、 實現了NavigableSet接口,NavigableSet是SortedSet的子接口;
5、 底層為TreeMap提供支持,Set中的元素就是Map的Key;
6、 淺拷貝;
7、 PRESENT:Set提供給底層Map的Value,所有Key共用通過一個Value;


Queue
單向隊列,隊頭取出,隊尾加入。


Deque
雙向隊列,雙向添加刪除,分別有兩個方向的叠代器。


Vector
和ArrayList都一樣,但是提供了方法粒度級別的同步機制。


Stack
1、 繼承於Vector,線程安全,底層數組實現,自動擴張;
2、 棧頂增刪查操作,並提供整棧元素搜索功能。


WeakHashMap
1、 允許Key為空,但是在存儲底層是使用了內部一個特殊Object對象來表示Null的Key;
2、 維持了一個弱引用隊列,Map的Entry就是一個弱引用對象;
3、 底層實現和HashMap基本一致,只是tables中的引用對象都是弱引用;
4、 弱引用:不能保證引用還存在時避免引用指向的內存不被垃圾回收機制回收;
5、 Entry在new的時候創建了一個弱引用對象並加入弱引用隊列中,該弱引用指向的內存空間隨時可能被回收,被回收後再使用該弱引用返回null,並且內部的多數Map操作都附帶了弱引用回收操作,把那些已經被回收的弱引用從弱引用隊列中移除,不影響該弱引用在hashTable中鏈表指向的下一個節點。


Collections
1、 封裝了集合的很多常用工具靜態方法,二分查找、排序、子集合、替換、比較等;
2、 synchronizedCollection等一系列的方法提供了將普通集合轉換成代碼塊粒度的同步,使用mutex進行同步;
3、 singleton提供了集合中單元素的集合靜態方法;
4、 unmodifiableCollection提供了將所給集合變成不可寫的集合的方法;
5、 等等。。。。。


Arrays
1、 數組的常用工具靜態方法,排序,二分查找,數組填充,子數組提取,深equals,深hashCode,數組復制等;
2、 基本數據類型使用插入排序(長度小於7)和快速排序(長度大於7);
3、 Object的數組使用歸並排序(穩定排序);


Array
反射包中的Array,表示所有的數組,由native本地方法實現,一個數組就相當於是一個Object。原生數組中的相關調用就相當於是調用這個類中的相關方法。


String
1、 對char數組的封裝,相當於是一個元素為char的ArrayList,沒有長度自動伸縮功能;
2、 封裝了字符串操作的常用方法,底層實現都是在反復操縱char[];
3、 Equals重寫成了char[]的各個元素;
4、 hashCode重寫成char[]計算的哈希值;
5、 split通過不斷查找切割子串的位置來substring字符串得到字符串分割結果數組;
6、 String長度創建後不變,改變的話就是整個char[]變了,不能對char[]數組中的指定下標進行隨機修改;


StringBuilder
1、 底層char[]實現,相當於ArrayList的char元素的實現,提供元素增刪改查;
2、 線程不安全;
3、 可設定初始長度,防止擴充長度的資源消耗;
4、 可修改指定下標的char值;
5、 動態修改動態添加,長度可變;


StringBuffer
與StringBuilder繼承相同抽象類,底層相同實現,但是StringBuffer是線程安全的。


ThreadLocal
1、 多線程同時訪問統一共享屬性的時候產生的線程安全問題的一種解決方案,適用於每個線程對這個共享屬性的修改都不會影響到其他線程,即不需要線程間通信的這種情況;這樣做是為了避免使用同步鎖導致的效率問題(每個線程各創建一個共享數據的副本分別服務於各自的線程);
2、 每個Thread內部有兩個類似Map的屬性ThreadLocalMap,通過Thread.currentThread();獲取當前的線程實例,再獲取本線程中的Map實例,通過對這個Map中進行數據的增刪,實現共享數據的副本和線程綁定的目的,從而在一定程度上避免線程安全問題。


額外收獲:
1. transient:關鍵字,修飾的變量不參與序列化操作;
2. volatile:關鍵字,修飾的變量在每次使用時都會去查詢其最新值,防止編譯器優化使用緩存錯過他的最新值;
註:這裏的每次使用指的是從堆區復制這個值的瞬間,當一個線程訪問時,會從堆區拷貝這個值一份到自己的線程棧中,之後線程就只修改線程棧中的這個值,對於堆區的值沒有修改,這是volatile的局限性。詳細看:http://www.cnblogs.com/aigongsi/archive/2012/04/01/2429166.html
3. 集合equals判斷:先判斷地址是否相同,再判斷類型是否相同,再判斷size,之後判斷集合中每個元素是否相同;
4. 集合hashCode:集合中每個非空元素的hashCode之和;
5. Remove如果刪除了集合中沒有的元素,集合沒有任何改變,返回false;
6. HashMap、LinkedHashMap、TreeMap區別(叠代遍歷順序):
HashMap存儲和查詢順序根據Hash而定,順序不確定;
LinkedHashMap:按照元素插入先後順序;
TreeMap:按照元素Key比較順序,或臨時設定的比較器決定的順序輸出;
7. Comparable和Comparator:
Comparable是Entity元素的可實現接口,實現該接口的Entity在和本類型的其他Entity比較時就是通過該接口的compareTo方法進行比較;
Comparator:提供給集合的比較器接口,該比較器可以通過傳入相同類型的兩個對象進行compare比較並返回相應值判斷出大小關系(通常保證比較結果要和該類型的equals返回結果相一致)。


參考博客:
1. http://zengzhaoshuai.iteye.com/blog/1130376
2. http://mikewang.blog.51cto.com/3826268/880775/

本文為博主原創,允許轉載,但請聲明原文地址:http://www.coselding.cn/article/2016/05/31/JDK部分源碼閱讀與理解/

JDK部分源碼閱讀與理解