【傳智播客上海校區】TreeMap原始碼解析
案例剖析
關於Map集合的特點,我們都知道底層資料結構是控制Map集合的鍵,而和值即value是無關的。通過查閱JDK的API我們發現TreeMap集合的底層是基於紅黑樹的即二叉樹。該對映根據其鍵的自然順序進行排序,或者根據建立對映時提供的Comparator進行排序,具體取決於使用的構造方法。如果使用的構造方法是TreeMap()那麼底層的鍵就是自然排序,如果使用的構造方法是TreeMap(Comparator<? super K> comparator) ,那麼底層的鍵就是Comparator進行排序的。
接下來我們先看如下案例:
向TreeMap集合中儲存資料,要求鍵為自定義Student類的物件,家庭住址作為TreeMap的值,型別是String型別。
學生Student類程式碼如下:
packagecn.itcast.sh.set;/** 描述學生*/publicclassStudent {//屬性Stringname;intage;publicStudent(Stringname, intage) {this.name=name;this.age=age;}@OverridepublicStringtoString() {return"Student [name="+name+", age="+age+"]";}}
測試類TreeMapDemo01程式碼如下:
publicclassTreeMapDemo01 {publicstaticvoidmain(String[] args) {// 建立集合物件TreeMap<Student, String>tm=newTreeMap<Student,String>();//新增資料tm.put(newStudent("張三",18), "上海");tm.put(newStudent("李四",19), "北京");tm.put(newStudent("王五",18), "深圳");tm.put(newStudent("張三",18), "上海");//獲取所有的鍵Set<Student>keys=tm.keySet();//遍歷Set集合for (Studentkey : keys) {//輸出資料System.out.println(key+"----"+tm.get(key));}}}
上述程式碼執行結果會報如下錯誤:
分析異常的原因:
通過以上報異常錯誤原因大概知道我們要將自己定義的類Student的儲存到TreeMap集合中的時候,發生自定義類Student不能被轉換到Comparable比較器介面的異常,我們在程式碼中明明沒有書寫和Comparable介面相關的程式碼啊,那怎麼會報這個異常呢?
這裡我們需要檢視一下TreeMap底層的原始碼進行進一步分析。
關於TreeMap集合的深入分析
問題1:為什麼集合中儲存的元素可以不重複呢?
TreeMap集合底層使用了二叉樹結構,在儲存元素時,拿儲存的元素會和樹結構中已經存在元素進行比較大小(結果有三種:大於、小於、相等)。當比較的結果相等時,表示該元素已經存在了,則不儲存。
問題2:為什麼TreeMap集合中儲存的元素會排序呢?
在儲存元素時,先拿要儲存的元素和樹結構中已經存在的元素進行比較大小,如果要儲存的元素大於樹結構中已存在的元素,則把要儲存的元素存放比較元素的右邊;如果要要儲存的元素小於樹結構中已存在的元素,則把要儲存的元素放在左邊。
分析TreeMap集合的原始碼查詢為什麼會報上述異常:
當上述程式碼中我們使用集合物件tm呼叫put方法向集合新增資料時,報了異常。所以我們可以檢視put方法的原始碼:
TreeMap集合中的put方法原始碼如下所示:
publicVput(Kkey, Vvalue) {Entry<K,V>t=root;//表示二叉樹的根元素if (t==null) {compare(key, key); // type (and possibly null) checkroot=newEntry<>(key, value, null);size=1;modCount++;returnnull; }intcmp;//記錄比較的結果,有三種結果 大 小 相等Entry<K,V>parent;// split comparator and comparable pathsComparator<?superK>cpr=comparator;//自定義比較器物件if (cpr!=null) {//判斷自定義比較器物件是否為null,由於我們在建立TreeMap集合的時候根本就沒有傳遞自定義比較器物件,所以cpr是null,那麼這裡false,所以不會執行if中的程式碼,執行elsedo {parent=t;cmp=cpr.compare(key, t.key);if (cmp<0)t=t.left;elseif (cmp>0)t=t.right;elsereturnt.setValue(value); } while (t!=null); }else {if (key==null)thrownewNullPointerException();Comparable<?superK>k= (Comparable<?superK>) key;do {parent=t;cmp=k.compareTo(t.key);if (cmp<0)t=t.left;elseif (cmp>0)t=t.right;elsereturnt.setValue(value); } while (t!=null); }Entry<K,V>e=newEntry<>(key, value, parent);if (cmp<0)parent.left=e;elseparent.right=e;fixAfterInsertion(e);size++;modCount++;returnnull; }
對於上述原始碼的解釋:
Entry<K,V> t = root;//表示二叉樹的根元素
if (t==null) {//判斷根元素t是否為空,在插入第一個資料之前根元素肯定是nullcompare(key, key); // type (and possibly null) checkroot=newEntry<>(key, value, null);size=1;modCount++;returnnull; }compare(key, key);比較key,其實這裡是判斷key位置物件所屬類即Student是否實現Comparable介面。程式碼如下:```javafinal int compare(Object k1, Object k2) { return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2) : comparator.compare((K)k1, (K)k2); }
由於我們在建立TreeMap物件時並沒有傳遞自定義比較器Comparator的物件,所以comparator等於null。這裡會執行 ((Comparable<? super K>)k1).compareTo((K)k2)。這句話的意思是對兩個鍵進行比較,按照自然排序Comparable介面中的compareTo進行排序,但是必須將傳遞進來的Student物件進行強轉。但是Student根本就沒有實現Comparable介面,所以這裡會報類轉換異常。為了解決上述錯誤,只需Student實現Comparable介面即可。
root = new Entry<>(key, value, null); 表示建立根元素物件,即將第一個元素新增到二叉樹的根元素位置。
由於在建立TreeMap集合物件時並沒有傳遞自定義比較器Comparator的物件,所以自定義比較器物件是null,執行如下程式碼:
else {if (key==null)//在TreeMap集合中key不能是null,只要是null就會報空指標異常thrownewNullPointerException();Comparable<?superK>k= (Comparable<?superK>) key;//將新增到集合中的key強轉為Comparable型別do {parent=t;cmp=k.compareTo(t.key);if (cmp<0)t=t.left;elseif (cmp>0)t=t.right;elsereturnt.setValue(value); } while (t!=null); }
cmp = k.compareTo(t.key);
t.key表示根元素的鍵,k表示後新增的鍵,按照自然排序進行比較。將返回值放到cmp變數中。
如果cmp變數小於0,將執行t = t.left;如果cmp變數大於0,將執行 t = t.right;如果cmp等於0,後新增的值將之前的值覆蓋。
這裡由於 t.left或者t.right初始化值都是null,所以t等於null。
執行如下程式碼:
Entry<K,V>e=newEntry<>(key, value, parent);if (cmp<0)parent.left=e;//將元素放到左子樹elseparent.right=e;//將元素放到右子樹fixAfterInsertion(e);//進行反轉size++;modCount++;returnnull;
相關文章
- 傳智播客上海校區:你為什麼一定要學Python?Python
- 【傳智播客上海校區】java類和物件的關係的通俗理解Java物件
- 傳智播客上海校區受邀參與“2017華為雲技術私享會
- 傳智播客上海校區知識點解析:簡單模擬vue中的雙向資料繫結Vue
- 【傳智播客上海校區】PS製作牛仔布圖示-圖層樣式製造圖示
- 機器裝置維護工人在傳智播客上海校區找到改變命運的機會
- [原始碼解析] PyTorch 分散式(13) ----- DistributedDataParallel 之 反向傳播原始碼PyTorch分散式Parallel反向傳播
- [原始碼解析] PyTorch 分散式(12) ----- DistributedDataParallel 之 前向傳播原始碼PyTorch分散式Parallel
- 傳智播客:人工智慧學習線路概況人工智慧
- 最新傳智播客nodejs入門到精通(全套視訊)NodeJS
- jdk原始碼分析之TreeMapJDK原始碼
- [原始碼解析]PyTorch如何實現前向傳播(2) --- 基礎類(下)原始碼PyTorch
- [原始碼解析]PyTorch如何實現前向傳播(1) --- 基礎類(上)原始碼PyTorch
- java基礎:TreeMap — 原始碼分析Java原始碼
- Java集合原始碼分析(十四):TreeMapJava原始碼
- 傳智播客中JavaWeb程式設計任務教程的作業JavaWeb程式設計
- 死磕 java集合之TreeMap原始碼分析(四)——紅黑樹全解析Java原始碼
- 死磕 java集合之TreeMap原始碼分析(二)——紅黑樹全解析Java原始碼
- 死磕 java集合之TreeMap原始碼分析(三)——紅黑樹全解析Java原始碼
- 死磕 java集合之TreeMap原始碼分析(一)——紅黑樹全解析Java原始碼
- [原始碼解析] Pytorch 如何實現後向傳播 (2)---- 引擎靜態結構原始碼PyTorch
- [原始碼解析] Pytorch 如何實現後向傳播 (3)---- 引擎動態邏輯原始碼PyTorch
- [原始碼解析] PyTorch 如何實現後向傳播 (4)---- 具體演算法原始碼PyTorch演算法
- TreeMap原始碼分析,看了都說好原始碼
- JDK1.8 原始碼分析(十) -- TreeMapJDK原始碼
- 馬士兵Java和傳智播客Java系列視訊教程免費下載Java
- Mybati原始碼解析篇之六劍客!!!BAT原始碼
- TreeMap就這麼簡單【原始碼剖析】原始碼
- [原始碼解析] NVIDIA HugeCTR,GPU 版本引數伺服器---(7) ---Distributed Hash之前向傳播原始碼GPU伺服器
- 傳智播客黑馬.NET+Unity3D 遊戲開發視訊教程Unity3D遊戲開發
- 《球球大作戰》原始碼解析(8):訊息廣播原始碼
- [原始碼解析] NVIDIA HugeCTR,GPU 版本引數伺服器---(8) ---Distributed Hash之後向傳播原始碼GPU伺服器
- 傳智播客PHP2015-XML視訊教程 XML-01-xml介紹 筆記PHPXML筆記
- 開發者播客-模板原始碼已經貼出來啦!原始碼
- TreeMap 還能排序?分析下原始碼就明白了排序原始碼
- JDK1.8原始碼(十一)——java.util.TreeMap類JDK原始碼Java
- 傳智播客_畢姥爺_2012年畢向東Java基礎教程_畢向東老師Java
- Spring Cloud系列(四):Eureka原始碼解析之客戶端SpringCloud原始碼客戶端