日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java基础源码

發(fā)布時間:2023/12/10 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java基础源码 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

HashMap

第一部分 基礎入門

1. 數(shù)組的優(yōu)勢、劣勢

內存地址連續(xù),可以通過下標常數(shù)時間復雜度O(1)獲取元素,但是增加和刪除元素時間復雜度為O(n)。

數(shù)組長度大小固定,如果需要擴容,需要重新申請一個數(shù)組,將原數(shù)組元素拷貝到新數(shù)組中,不夠靈活

2. 鏈表的優(yōu)勢、劣勢

內存地址不連續(xù),增加和刪除元素時間復雜度為O(1),鏈表沒有index,查詢元素只能沿著鏈表一個一個往下找,時間復雜度為O(n)。

鏈表長度大小不固定,可以動態(tài)增加和減小鏈表的長度,比較靈活

3. 散列表

結構:數(shù)組+鏈表

整合了數(shù)組的快速索引,又整合了鏈表可以動態(tài)擴容

4. 散列表有什么特點?

5. 什么是hash?

核心理論:Hash也稱散列、哈希,對應的英文都是Hash。基本原理就是把任意長度的輸入,通過Hash算法變成固定長度的輸出。

這個映射的規(guī)則就是對應的Hash算法,而原始數(shù)據(jù)映射后的二進制串就是哈希值。

Hash的特點:

1.從hash值不可以反向推導出原始的數(shù)據(jù)

2.輸入數(shù)據(jù)的微小變化會得到完全不同的hash值,相同的數(shù)據(jù)會得到相同的值

3.哈希算法的執(zhí)行效率要高效,長的文本也能快速地計算出哈希值

4.hash算法的沖突概率要小

由于hash的原理是將輸入空間的值映射成hash空間內,而hash值的空間遠小于輸入的空間。

根據(jù)抽屜原理,一定會存在不同的輸入被映射成相同輸出的情況。

抽屜原理:桌上有十個蘋果,要把這十個蘋果放到九個抽屜里,無論怎樣放,我們會發(fā)現(xiàn)至少會有一個抽屜里面放不少于兩個蘋果。

這一現(xiàn)象就是我們所說的“抽屜原理”。

第二部分 HashMap原理

1. HashMap的繼承體系是什么樣的?

2. Node數(shù)據(jù)結構分析

static class Node<K,V> implements Map.Entry<K,V> {final int hash;final K key;V value;Node<K,V> next;Node(int hash, K key, V value, Node<K,V> next) {this.hash = hash;this.key = key;this.value = value;this.next = next;}public final K getKey() { return key; }public final V getValue() { return value; }public final String toString() { return key + "=" + value; }public final int hashCode() {return Objects.hashCode(key) ^ Objects.hashCode(value);}public final V setValue(V newValue) {V oldValue = value;value = newValue;return oldValue;}public final boolean equals(Object o) {if (o == this)return true;if (o instanceof Map.Entry) {Map.Entry<?,?> e = (Map.Entry<?,?>)o;if (Objects.equals(key, e.getKey()) &&Objects.equals(value, e.getValue()))return true;}return false;}}

Node節(jié)點實現(xiàn)了Map中的Entry接口

節(jié)點中包含hash,key,value,next指針四個屬性

3. 底層存儲結構介紹

HashMap底層數(shù)據(jù)結構為 數(shù)組+鏈表+紅黑樹

鏈表長度超過8,并且當前結構里面元素達到64的時候,鏈表結構會升級為紅黑樹

4. put數(shù)據(jù)原理分析

5. 什么是Hash碰撞

我們常把數(shù)組中的每一個節(jié)點稱為一個桶。

當向桶中添加一個鍵值對時,首先計算鍵值對中key的hash值,以此確定插入數(shù)組中的位置,但是可能存在同一hash值的元素已經被放在數(shù)組同一位置了,這種現(xiàn)象稱為Hash碰撞

6. 什么是鏈化

發(fā)生Hash碰撞后,按照尾插法(jdk1.7及以前為頭插法)的方式添加key-value到同一hash值的元素的后面,鏈表就這樣形成了。當鏈表長度超過8(TREEIFY_THRESHOLD)時,鏈表就轉換為紅黑樹。

7. jdk8為什么引入紅黑樹

為了解決鏈化問題,提高查找效率

鏈表如果很長,那么查找效率會很低,鏈表查找元素的時間復雜度為O(n)

紅黑樹是一棵自平衡的二叉查找樹,紅黑樹查找元素的時間復雜度為Olog(n)

8. HashMap擴容機制

擴容是為了提高查找效率,以空間換時間

第三部分 源碼分析

1.HashMap核心屬性分析(threshold, loadFactory, size, modCount)

常量(共6個)

//table數(shù)組默認初始化容量大小,必須為2的冪次方 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 //table數(shù)組最大容量 static final int MAXIMUM_CAPACITY = 1 << 30; //默認負載因子 0.75f static final float DEFAULT_LOAD_FACTOR = 0.75f; //樹化閾值8 static final int TREEIFY_THRESHOLD = 8; //樹降級為鏈表閾值6 static final int UNTREEIFY_THRESHOLD = 6; //最小樹化容量,樹化的另一個參數(shù),當哈希表中的所有元素個數(shù)超過64時,才會允許樹化 static final int MIN_TREEIFY_CAPACITY = 64;

屬性(此處5個)

//哈希表中的數(shù)組 transient Node<K,V>[] table; //當前哈希表中元素個數(shù) transient int size; //當前哈希表結構修改次數(shù) transient int modCount; //擴容閾值。當你的哈希表中的元素個數(shù)超過閾值時,觸發(fā)擴容 int threshold; //負載因子,用來計算 threshold = loadFactor * capacity final float loadFactor;

2.構造方法分析(4個)

public HashMap(int initialCapacity, float loadFactor) {//其實就是做了一些校驗//capacity必須是大于0.最大值也就是MAXIMUM_CAPACITYif (initialCapacity < 0)throw new IllegalArgumentException("Illegal initial capacity: " +initialCapacity);if (initialCapacity > MAXIMUM_CAPACITY)initialCapacity = MAXIMUM_CAPACITY;//loadFactor必須大于0if (loadFactor <= 0 || Float.isNaN(loadFactor))throw new IllegalArgumentException("Illegal load factor: " +loadFactor);this.loadFactor = loadFactor;this.threshold = tableSizeFor(initialCapacity);}//作用,返回一個大于等于當前值cap的一個數(shù)字,并且這個數(shù)字一定是2的次方數(shù)static final int tableSizeFor(int cap) {int n = cap - 1;n |= n >>> 1;n |= n >>> 2;n |= n >>> 4;n |= n >>> 8;n |= n >>> 16;return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;}public HashMap(int initialCapacity) {this(initialCapacity, DEFAULT_LOAD_FACTOR);}public HashMap() {this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted}public HashMap(Map<? extends K, ? extends V> m) {this.loadFactor = DEFAULT_LOAD_FACTOR;putMapEntries(m, false);}

3.HashMap put 方法分析 => putVal方法分析

public V put(K key, V value) {return putVal(hash(key), key, value, false, true);}//作用:讓key的hash值的高16位也參與路由運算static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {//tab:引用當前hashMap的散列表//p:表示當前散列表的元素//n:表示散列表元素的長度//i:表示路由尋址結果、地址,下標Node<K,V>[] tab; Node<K,V> p; int n, i;//延遲初始化邏輯,第一次調用putVal方法時,會初始化hashMap對象中的最耗費內存的散列表if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length;//最簡單的一種情況:尋址找到的桶位 剛好是null,這個時候,直接將當前k-v=>node 扔進去就可以了if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);else {//e:不為null的話,找到了一個與當前要插入的k-v一致的key的元素//k:表示臨時的一個keyNode<K,V> e; K k;//表示桶位中的該元素,與你當前插入的元素的key完全一致,表示后續(xù)需要進行替換操作if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))e = p;//紅黑樹的情況,而且紅黑樹的頭元素與我們要插入的key不一致else if (p instanceof TreeNode)e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);//鏈表的情況,而且鏈表的頭元素與我們要插入的key不一致else {for (int binCount = 0; ; ++binCount) {//條件成立的話,說明迭代到最后一個元素了,也沒有找到一個與你要插入的key一致的node//說明需要加入到當前鏈表的末尾if ((e = p.next) == null) {p.next = newNode(hash, key, value, null);//條件成立的話,說明當前鏈表的長度,達到樹化標準了,需要進行樹化if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st//樹化操作treeifyBin(tab, hash);break;}//條件成立的話,說明找到了相同key的node元素,需要進行替換操作if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))break;p = e;}}//e不等于null,條件成立說明,找到了一個與你插入元素key完全一致的數(shù)據(jù),需要進行替換if (e != null) { // existing mapping for keyV oldValue = e.value;if (!onlyIfAbsent || oldValue == null)e.value = value;afterNodeAccess(e);return oldValue;}}//modCount:表示散列表結構被修改的次數(shù),替換node元素的value不計數(shù)++modCount;//插入新元素,size自增,如果自增后的值,大于擴容閾值,則觸發(fā)擴容if (++size > threshold)resize();afterNodeInsertion(evict);return null;}

4.HashMap resize 擴容方法分析(核心)

//為什么需要擴容? //為了解決哈希沖突導致的鏈化影響查詢效率的問題,擴容會緩解該問題。 final Node<K,V>[] resize() {//oldTab:引用擴容前的哈希表Node<K,V>[] oldTab = table;//oldCap:表示擴容之前table數(shù)組的長度int oldCap = (oldTab == null) ? 0 : oldTab.length;//oldThr:表示擴容之前的擴容閾值,觸發(fā)本次擴容的閾值int oldThr = threshold;//newCap:擴容之后table數(shù)組的大小//newThr:擴容之后,下次再次觸發(fā)擴容的條件int newCap, newThr = 0;//條件如果成立說明 hashMap中的散列表已經初始化過了,這是一次正常擴容if (oldCap > 0) {//擴容之前的table數(shù)組大小已經達到 最大閾值后,則不擴容,且設置擴容條件為 int 最大值。if (oldCap >= MAXIMUM_CAPACITY) {threshold = Integer.MAX_VALUE;return oldTab;}//oldCap左移一位實現(xiàn)數(shù)值翻倍,并且賦值給newCap, newCap 小于數(shù)組最大值限制 且 擴容之前的閾值 >= 16//這種情況下,則 下一次擴容的閾值 等于當前閾值翻倍else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&oldCap >= DEFAULT_INITIAL_CAPACITY)newThr = oldThr << 1; // double threshold}//oldCap == 0,說明hashMap中的散列表是null//1.new HashMap(initCap, loadFactor);//2.new HashMap(initCap);//3.new HashMap(map); 并且這個map有數(shù)據(jù)else if (oldThr > 0) // initial capacity was placed in thresholdnewCap = oldThr;//oldCap == 0,oldThr == 0//new HashMap();else { // zero initial threshold signifies using defaultsnewCap = DEFAULT_INITIAL_CAPACITY;//16newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);//12}//newThr為零時,通過newCap和loadFactor計算出一個newThrif (newThr == 0) {float ft = (float)newCap * loadFactor;newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?(int)ft : Integer.MAX_VALUE);}threshold = newThr;//創(chuàng)建出一個更長 更大的數(shù)組@SuppressWarnings({"rawtypes","unchecked"})Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];table = newTab;//說明,hashMap本次擴容之前,table不為nullif (oldTab != null) {for (int j = 0; j < oldCap; ++j) {//當前node節(jié)點Node<K,V> e;//說明當前桶位中有數(shù)據(jù),但是數(shù)據(jù)具體是 單個數(shù)據(jù),還是鏈表 還是 紅黑樹 并不知道if ((e = oldTab[j]) != null) {//方便JVM GC時回收內存oldTab[j] = null;//第一種情況:當前桶位只有一個元素,從未發(fā)生過碰撞,這情況 直接計算出當前元素應存放在 新數(shù)組中的位置,然后//扔進去就可以了if (e.next == null)newTab[e.hash & (newCap - 1)] = e;//第二種情況:當前節(jié)點已經樹化,本期先不講,下一期講,紅黑樹。QQ群:865-373-238else if (e instanceof TreeNode)((TreeNode<K,V>)e).split(this, newTab, j, oldCap);else { // preserve order//第三種情況:桶位已經形成鏈表//低位鏈表:存放在擴容之后的數(shù)組的下標位置,與當前數(shù)組的下標位置一致。Node<K,V> loHead = null, loTail = null;//高位鏈表:存放在擴容之后的數(shù)組的下表位置為 當前數(shù)組下標位置 + 擴容之前數(shù)組的長度Node<K,V> hiHead = null, hiTail = null;Node<K,V> next;do {next = e.next;//hash-> .... 1 1111//hash-> .... 0 1111// 0b 10000if ((e.hash & oldCap) == 0) {if (loTail == null)loHead = e;elseloTail.next = e;loTail = e;}else {if (hiTail == null)hiHead = e;elsehiTail.next = e;hiTail = e;}} while ((e = next) != null);if (loTail != null) {loTail.next = null;newTab[j] = loHead;}if (hiTail != null) {hiTail.next = null;newTab[j + oldCap] = hiHead;}}}}}return newTab; }

5.HashMap get 方法分析

public V get(Object key) {Node<K,V> e;return (e = getNode(hash(key), key)) == null ? null : e.value; } final Node<K,V> getNode(int hash, Object key) {//tab:引用當前hashMap的散列表//first:桶位中的頭元素//e:臨時node元素//n:table數(shù)組長度Node<K,V>[] tab; Node<K,V> first, e; int n; K k;if ((tab = table) != null && (n = tab.length) > 0 &&(first = tab[(n - 1) & hash]) != null) {//第一種情況:定位出來的桶位元素 即為咱們要get的數(shù)據(jù)if (first.hash == hash && // always check first node((k = first.key) == key || (key != null && key.equals(k))))return first;//說明當前桶位不止一個元素,可能 是鏈表 也可能是 紅黑樹if ((e = first.next) != null) {//第二種情況:桶位升級成了 紅黑樹if (first instanceof TreeNode)//下一期說return ((TreeNode<K,V>)first).getTreeNode(hash, key);//第三種情況:桶位形成鏈表do {if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))return e;} while ((e = e.next) != null);}}return null; }

6.HashMap remove 方法分析

public V remove(Object key) {Node<K,V> e;return (e = removeNode(hash(key), key, null, false, true)) == null ?null : e.value; } final Node<K,V> removeNode(int hash, Object key, Object value,boolean matchValue, boolean movable) {//tab:引用當前hashMap中的散列表//p:當前node元素//n:表示散列表數(shù)組長度//index:表示尋址結果Node<K,V>[] tab; Node<K,V> p; int n, index;if ((tab = table) != null && (n = tab.length) > 0 &&(p = tab[index = (n - 1) & hash]) != null) {//說明路由的桶位是有數(shù)據(jù)的,需要進行查找操作,并且刪除//node:查找到的結果//e:當前Node的下一個元素Node<K,V> node = null, e; K k; V v;//第一種情況:當前桶位中的元素 即為 你要刪除的元素if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))node = p;else if ((e = p.next) != null) {//說明,當前桶位 要么是 鏈表 要么 是紅黑樹if (p instanceof TreeNode)//判斷當前桶位是否升級為 紅黑樹了//第二種情況//紅黑樹查找操作,下一期再說node = ((TreeNode<K,V>)p).getTreeNode(hash, key);else {//第三種情況//鏈表的情況do {if (e.hash == hash &&((k = e.key) == key ||(key != null && key.equals(k)))) {node = e;break;}p = e;} while ((e = e.next) != null);}}//判斷node不為空的話,說明按照key查找到需要刪除的數(shù)據(jù)了if (node != null && (!matchValue || (v = node.value) == value ||(value != null && value.equals(v)))) {//第一種情況:node是樹節(jié)點,說明需要進行樹節(jié)點移除操作if (node instanceof TreeNode)((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);//第二種情況:桶位元素即為查找結果,則將該元素的下一個元素放至桶位中else if (node == p)tab[index] = node.next;else//第三種情況:將當前元素p的下一個元素 設置成 要刪除元素的 下一個元素。p.next = node.next;++modCount;--size;afterNodeRemoval(node);return node;}}return null; }

7.HashMap replace 方法分析

@Overridepublic boolean replace(K key, V oldValue, V newValue) {Node<K,V> e; V v;if ((e = getNode(hash(key), key)) != null &&((v = e.value) == oldValue || (v != null && v.equals(oldValue)))) {e.value = newValue;afterNodeAccess(e);return true;}return false;}@Overridepublic V replace(K key, V value) {Node<K,V> e;if ((e = getNode(hash(key), key)) != null) {V oldValue = e.value;e.value = value;afterNodeAccess(e);return oldValue;}return null;}

紅黑樹

紅黑樹原理

紅黑樹的性質:

紅黑樹的性質

紅黑樹示例圖

性質1:每個節(jié)點要么是黑色,要么是紅色。

性質2:根節(jié)點是黑色。

性質3:每個葉子節(jié)點(NIL)是黑色。

性質4:每個紅色節(jié)點的兩個子節(jié)點一定都是黑色。

不能有兩個紅色節(jié)點相連。

性質5:任意一節(jié)點到每個葉子節(jié)點的路徑都包含數(shù)量相同的黑結點。俗稱:黑高!

從性質5又可以推出:

性質5.1:如果一個節(jié)點存在黑子節(jié)點,那么該結點肯定有兩個子節(jié)點

紅黑樹并不是一個完美平衡二叉查找樹,從圖上可以看到,根結點P的左子樹顯然比右子樹高,

但左子樹和右子樹的黑結點的層數(shù)是相等的,也即任意一個結點到到每個葉子結點的路徑都包含數(shù)量相同的黑結點(性質5)。

所以我們叫紅黑樹這種平衡為黑色完美平衡。

紅黑樹的性質講完了,只要這棵樹滿足以上性質,這棵樹就是趨近與平衡狀態(tài)的,

不要問為什么,發(fā)明紅黑樹的科學家就是這么牛逼!

前面講到紅黑樹能自平衡,它靠的是什么?三種操作:左旋、右旋和變色。

1.變色:結點的顏色由紅變黑或由黑變紅。

2.左旋:以某個結點作為支點(旋轉結點),其右子結點變?yōu)樾D結點的父結點,右子結點的左子結點變?yōu)樾D結點的右子結點,左子結點保持不變。

3.右旋:以某個結點作為支點(旋轉結點),其左子結點變?yōu)樾D結點的父結點,左子結點的右子結點變?yōu)樾D結點的左子結點,右子結點保持不變

左旋圖示

右旋圖示

紅黑樹查找:

紅黑樹插入:

插入操作包括兩部分工作:

1.查找插入的位置

2.插入后自平衡

注意:插入節(jié)點,必須為紅色,理由很簡單,紅色在父節(jié)點(如果存在)為黑色節(jié)點時,紅黑樹的黑色平衡沒被破壞,不需要做自平衡操作。

但如果插入結點是黑色,那么插入位置所在的子樹黑色結點總是多1,必須做自平衡。

在開始每個情景的講解前,我們還是先來約定下:

紅黑樹插入節(jié)點情景分析

情景1:紅黑樹為空樹

最簡單的一種情景,直接把插入結點作為根結點就行

注意:根據(jù)紅黑樹性質2:根節(jié)點是黑色。還需要把插入結點設為黑色。

情景2:插入結點的Key已存在

處理:更新當前節(jié)點的值,為插入節(jié)點的值

情景3:插入結點的父結點為黑結點

由于插入的結點是紅色的,當插入結點的黑色時,并不會影響紅黑樹的平衡,直接插入即可,無需做自平衡。

情景4:插入節(jié)點的父節(jié)點為紅色

再次回想下紅黑樹的性質2:根結點是黑色。如果插入節(jié)點的父結點為紅結點,那么該父結點不可能為根結點,所以插入結點總是存在祖父結點。

這一點很關鍵,因為后續(xù)的旋轉操作肯定需要祖父結點的參與。

插入情景4.1:叔叔結點存在并且為紅結點

依據(jù)紅黑樹性質4可知,紅色節(jié)點不能相連 ==> 祖父結點肯定為黑結點;

因為不可以同時存在兩個相連的紅結點。那么此時該插入子樹的紅黑層數(shù)的情況是:黑紅紅。顯然最簡單的處理方式是把其改為:紅黑紅

處理:

1.將P和U節(jié)點改為黑色

2.將PP改為紅色

3.將PP設置為當前節(jié)點,進行后續(xù)處理

可以看到,我們把PP結點設為紅色了,如果PP的父結點是黑色,那么無需再做任何處理;

但如果PP的父結點是紅色,則違反紅黑樹性質了。所以需要將PP設置為當前節(jié)點,繼續(xù)做插入操作自平衡處理,直到平衡為止。

插入情景4.2:叔叔結點不存在或為黑結點,并且插入結點的父親結點是祖父結點的左子結點

注意:單純從插入前來看,叔叔節(jié)點非紅即空(NIL節(jié)點),否則的話破壞了紅黑樹性質5,此路徑會比其它路徑多一個黑色節(jié)點。

插入情景4.2.1:新插入節(jié)點,為其父節(jié)點的左子節(jié)點(LL紅色情況)

處理:

1.變顏色:將P設置為黑色,將PP設置為紅色

2.對PP節(jié)點進行右旋

插入情景4.2.2:新插入節(jié)點,為其父節(jié)點的右子節(jié)點(LR紅色情況)

處理:

1.對P進行左旋

2.將P設置為當前節(jié)點,得到LL紅色情況

3.按照LL紅色情況處理(1.變顏色 2.右旋PP)

插入情景4.3:叔叔結點不存在或為黑結點,并且插入結點的父親結點是祖父結點的右子結點

該情景對應情景4.2,只是方向反轉,直接看圖。

插入情景4.3.1:新插入節(jié)點,為其父節(jié)點的右子節(jié)點(RR紅色情況)

處理:

1.變顏色:將P設置為黑色,將PP設置為紅色

2.對PP節(jié)點進行左旋

插入情景4.3.2:新插入節(jié)點,為其父節(jié)點的左子節(jié)點(RL紅色情況)

處理:

1.對P進行右旋

2.將P設置為當前節(jié)點,得到RR紅色情況

3.按照RR紅色情況處理(1.變顏色 2.左旋PP)

hashMap中紅黑樹源碼

平衡插入(balanceInsertion)

主要有四種情況

情況1,如果新節(jié)點X的父節(jié)點XP為null,則不需要調整 情況2,如果父節(jié)點為黑色,或者祖父節(jié)點XPP為空,則不需要調整 情況3,父節(jié)點XP為祖父節(jié)點XPP的左節(jié)點(父節(jié)點XP為紅色) 情況4,父節(jié)點XP為祖父節(jié)點XPP的右節(jié)點(父節(jié)點XP為紅色) //進行平衡的調整,X為新節(jié)點 static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,TreeNode<K,V> x) {//設置新插入的節(jié)點為紅色x.red = true;for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {//情況1,如果新節(jié)點X的父節(jié)點XP為null,則不需要調整if ((xp = x.parent) == null) {x.red = false;return x;}//情況2,如果父節(jié)點為黑色,或者祖父節(jié)點XPP為空,則不需要調整else if (!xp.red || (xpp = xp.parent) == null)return root;//情況3,父節(jié)點XP為祖父節(jié)點XPP的左節(jié)點(父節(jié)點XP為紅色)if (xp == (xppl = xpp.left)) {//右叔叔節(jié)點不為null,且右叔叔節(jié)點為紅色if ((xppr = xpp.right) != null && xppr.red) {xppr.red = false;xp.red = false;xpp.red = true;x = xpp;}//右叔叔節(jié)點為null,或右叔叔節(jié)點為黑色,右旋else {//如果新節(jié)點X為父節(jié)點的右節(jié)點,需要先進行左旋if (x == xp.right) {root = rotateLeft(root, x = xp);xpp = (xp = x.parent) == null ? null : xp.parent;}if (xp != null) {xp.red = false;if (xpp != null) {xpp.red = true;root = rotateRight(root, xpp);}}}}//情況4,父節(jié)點XP為祖父節(jié)點XPP的右節(jié)點(父節(jié)點XP為紅色)else {//左叔叔節(jié)點不為null,且左叔叔節(jié)點為紅色if (xppl != null && xppl.red) {xppl.red = false;xp.red = false;xpp.red = true;x = xpp;}//左叔叔節(jié)點為null,或者左叔叔節(jié)點為黑色,左旋else {//如果新節(jié)點X為父節(jié)點的左節(jié)點,需要先進行右旋if (x == xp.left) {root = rotateRight(root, x = xp);xpp = (xp = x.parent) == null ? null : xp.parent;}if (xp != null) {xp.red = false;if (xpp != null) {xpp.red = true;root = rotateLeft(root, xpp);}}}}} }

?左旋

//P為旋轉的中心節(jié)點 static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,TreeNode<K,V> p) {TreeNode<K,V> r, pp, rl;//P不為空,且P的右節(jié)點R不為空,才能進行左旋if (p != null && (r = p.right) != null) {if ((rl = p.right = r.left) != null)rl.parent = p;if ((pp = r.parent = p.parent) == null)(root = r).red = false;else if (pp.left == p)pp.left = r;elsepp.right = r;r.left = p;p.parent = r;}return root; }

右旋

static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root,TreeNode<K,V> p) {TreeNode<K,V> l, pp, lr;if (p != null && (l = p.left) != null) {if ((lr = p.left = l.right) != null)lr.parent = p;if ((pp = l.parent = p.parent) == null)(root = l).red = false;else if (pp.right == p)pp.right = l;elsepp.left = l;l.right = p;p.parent = l;}return root; }

split方法?

final void split(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit) {TreeNode<K,V> b = this;// Relink into lo and hi lists, preserving orderTreeNode<K,V> loHead = null, loTail = null;TreeNode<K,V> hiHead = null, hiTail = null;int lc = 0, hc = 0;for (TreeNode<K,V> e = b, next; e != null; e = next) {next = (TreeNode<K,V>)e.next;e.next = null;if ((e.hash & bit) == 0) {if ((e.prev = loTail) == null)loHead = e;elseloTail.next = e;loTail = e;++lc;}else {if ((e.prev = hiTail) == null)hiHead = e;elsehiTail.next = e;hiTail = e;++hc;}}if (loHead != null) {if (lc <= UNTREEIFY_THRESHOLD)tab[index] = loHead.untreeify(map);else {tab[index] = loHead;if (hiHead != null) // (else is already treeified)loHead.treeify(tab);}}if (hiHead != null) {if (hc <= UNTREEIFY_THRESHOLD)tab[index + bit] = hiHead.untreeify(map);else {tab[index + bit] = hiHead;if (loHead != null)hiHead.treeify(tab);}} }

put

final TreeNode<K,V> putTreeVal(HashMap<K,V> map, Node<K,V>[] tab,int h, K k, V v) {Class<?> kc = null;boolean searched = false;TreeNode<K,V> root = (parent != null) ? root() : this;for (TreeNode<K,V> p = root;;) {int dir, ph; K pk;if ((ph = p.hash) > h)dir = -1;else if (ph < h)dir = 1;else if ((pk = p.key) == k || (k != null && k.equals(pk)))return p;else if ((kc == null &&(kc = comparableClassFor(k)) == null) ||(dir = compareComparables(kc, k, pk)) == 0) {if (!searched) {TreeNode<K,V> q, ch;searched = true;if (((ch = p.left) != null &&(q = ch.find(h, k, kc)) != null) ||((ch = p.right) != null &&(q = ch.find(h, k, kc)) != null))return q;}dir = tieBreakOrder(k, pk);}TreeNode<K,V> xp = p;if ((p = (dir <= 0) ? p.left : p.right) == null) {Node<K,V> xpn = xp.next;TreeNode<K,V> x = map.newTreeNode(h, k, v, xpn);if (dir <= 0)xp.left = x;elsexp.right = x;xp.next = x;x.parent = x.prev = xp;if (xpn != null)((TreeNode<K,V>)xpn).prev = x;moveRootToFront(tab, balanceInsertion(root, x));return null;}} } final void treeify(Node<K,V>[] tab) {TreeNode<K,V> root = null;for (TreeNode<K,V> x = this, next; x != null; x = next) {next = (TreeNode<K,V>)x.next;x.left = x.right = null;if (root == null) {x.parent = null;x.red = false;root = x;}else {K k = x.key;int h = x.hash;Class<?> kc = null;for (TreeNode<K,V> p = root;;) {int dir, ph;K pk = p.key;if ((ph = p.hash) > h)dir = -1;else if (ph < h)dir = 1;else if ((kc == null &&(kc = comparableClassFor(k)) == null) ||(dir = compareComparables(kc, k, pk)) == 0)dir = tieBreakOrder(k, pk);TreeNode<K,V> xp = p;if ((p = (dir <= 0) ? p.left : p.right) == null) {x.parent = xp;if (dir <= 0)xp.left = x;elsexp.right = x;root = balanceInsertion(root, x);break;}}}}moveRootToFront(tab, root); }

FutureTask

實現(xiàn)RunnableFuture接口,通過submit()方法去提交到線程池中的任務,在被執(zhí)行之前,這些Runnable或者Callable都會被封裝成FutureTask提交到線程池里面,線程池中的線程會去執(zhí)行你所提交的任務

FutureTask中的屬性

//--------表示當前task狀態(tài)--------- private volatile int state; //當前任務尚未執(zhí)行 private static final int NEW = 0; //當前任務正在結束,還沒有完全結束,一種臨界狀態(tài) private static final int COMPLETING = 1; //當前任務正常結束 private static final int NORMAL = 2; //當前任務執(zhí)行過程中,發(fā)生了異常。內部封裝的callable.run()向上拋出異常 private static final int EXCEPTIONAL = 3; //當前任務被取消 private static final int CANCELLED = 4; //當前任務中斷中 private static final int INTERRUPTING = 5; //當前任務已中斷 private static final int INTERRUPTED = 6;//submit(runnable/callable) runnable 使用 裝飾者模式 偽裝成 callable了 private Callable<V> callable; //正常情況下:任務正常執(zhí)行結束,outcome保存執(zhí)行結果。保存callable的返回值 //非正常情況:callable向上拋出異常,outcome保存異常 private Object outcome; //當前任務被線程執(zhí)行期間,保存當前執(zhí)行任務的線程對象引用 private volatile Thread runner; //因為會有很多線程去get當前線程的結果。所以這里使用了一種數(shù)據(jù)結構 stack 頭插 頭取 一個隊列 private volatile WaitNode waiters; static final class WaitNode {volatile Thread thread;volatile FutureTask.WaitNode next;WaitNode() { thread = Thread.currentThread(); } }// Unsafe mechanicsprivate static final sun.misc.Unsafe UNSAFE;private static final long stateOffset;private static final long runnerOffset;private static final long waitersOffset;static {try {UNSAFE = sun.misc.Unsafe.getUnsafe();Class<?> k = FutureTask.class;stateOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("state"));runnerOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("runner"));waitersOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("waiters"));} catch (Exception e) {throw new Error(e);}}

FutureTask中的構造器

共有兩個構造器,分別是傳入callable和runnable,如果傳入runnable,會將其轉換成callable

public class FutureTask<V> implements RunnableFuture<V> {//傳入callablepublic FutureTask(Callable<V> callable) {if (callable == null)throw new NullPointerException();//callable就是程序員自己實現(xiàn)的業(yè)務類this.callable = callable;//設置當前任務狀態(tài)為NEWthis.state = NEW;}//傳入Runnable,將其轉換成callable,裝飾者模式public FutureTask(Runnable runnable, V result) {//使用裝飾者模式將runnable轉換成了callable接口,外部線程通過get獲取//當前任務執(zhí)行結果時,結果可能為null,也可能為傳進來的值this.callable = Executors.callable(runnable, result);//設置當前任務狀態(tài)為NEWthis.state = NEW;} }public class Executors {public static <T> Callable<T> callable(Runnable task, T result) {if (task == null)throw new NullPointerException();return new Executors.RunnableAdapter<T>(task, result);}static final class RunnableAdapter<T> implements Callable<T> {final Runnable task;final T result;RunnableAdapter(Runnable task, T result) {this.task = task;this.result = result;}public T call() {task.run();return result;}} } FutureTask中的run方法,任務執(zhí)行的入口 public class FutureTask<V> implements RunnableFuture<V> {//任務執(zhí)行入口public void run() {//條件一:state != NEW 條件成立,說明當前task已經被執(zhí)行過了 或者 被cancel了,總之非NEW狀態(tài)的任務,線程就不處理了//條件二:!UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread())// 條件成立,CAS失敗,當前任務被其他線程搶占了if (state != NEW ||!UNSAFE.compareAndSwapObject(this, runnerOffset,null, Thread.currentThread()))return;//執(zhí)行到這里,當前task一定是NEW狀態(tài),而且當前線程也搶占task成功try {//callable 就是程序員自己封裝邏輯的callable 或者 裝飾后的runnableCallable<V> c = callable;//條件一:c != null 防止空指針異常//條件二:state == NEW 防止外部線程 cancel掉當前任務if (c != null && state == NEW) {//結果引用V result;//true 表示callable.call 代碼塊執(zhí)行成功 未拋出異常//false 表示callable.call 代碼塊執(zhí)行失敗,拋出異常boolean ran;try {//調用程序員自己實現(xiàn)的callable 或者 裝飾后的runnableresult = c.call();ran = true;} catch (Throwable ex) {result = null;ran = false;setException(ex);}if (ran)//說明當前c.call正常執(zhí)行結束//set就是設置結果到outcomeset(result);}} finally {runner = null;int s = state;if (s >= INTERRUPTING)handlePossibleCancellationInterrupt(s);}}//正常情況下設置結果到outcomeprotected void set(V v) {//使用CAS方式,設置當前任務狀態(tài)為 完成中...//有沒有可能失敗?外部線程等不及了,直接在set執(zhí)行CAS之前,將task取消了。很小的概率if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {outcome = v;//將結果賦值給 outcome之后,馬上會將當前線程狀態(tài)修改為 NORMAL 正常結束狀態(tài)。UNSAFE.putOrderedInt(this, stateOffset, NORMAL);finishCompletion();}}//異常情況下設置結果到outcomeprotected void setException(Throwable t) {//使用CAS方式,設置當前任務狀態(tài)為 完成中...//有沒有可能失敗?外部線程等不及了,直接在set執(zhí)行CAS之前,將task取消了。很小的概率if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {//引用的是 callable 向上層拋出來的異常outcome = t;//將結果賦值給 outcome之后,馬上會將當前線程狀態(tài)修改為 EXCEPTIONAL 異常結束狀態(tài)。UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL);finishCompletion();}} } FutureTask中的get方法 public class FutureTask<V> implements RunnableFuture<V> {//場景:多個線程等待當前任務執(zhí)行完成后的結果public V get() throws InterruptedException, ExecutionException {//獲取當前任務狀態(tài)int s = state;//條件成立:未執(zhí)行、正在執(zhí)行、正在完成。調用get的外部線程會被阻塞在get方法上if (s <= COMPLETING)s = awaitDone(false, 0L);return report(s);}private int awaitDone(boolean timed, long nanos)throws InterruptedException {//0 不帶超時final long deadline = timed ? System.nanoTime() + nanos : 0L;//引用當前線程 封裝成WaitNode對象WaitNode q = null;//表示當前線程waitNode對象 有沒有 入隊/出隊boolean queued = false;//自旋for (;;) {//條件成立:說明當前線程喚醒 是被其他線程使用中斷這種方式喚醒的。//interrupted():返回true,后會將Thread的中斷標記重置回falseif (Thread.interrupted()) {//當前線程node出隊removeWaiter(q);//get方法拋出中斷異常throw new InterruptedException();}//假設當前線程是被其他線程 使用unpark(thread)喚醒的話。會正常自旋,走下面邏輯。//獲取當前任務最新狀態(tài)int s = state;//條件成立:說明當前任務 已經有結果了..可能是好 也可能是壞..if (s > COMPLETING) {//條件成立:說明已經為當前線程創(chuàng)建過node了。此時需要將node.thread=null helpGCif (q != null)q.thread = null;//直接返回當前狀態(tài)return s;}//條件成立:說明當前任務已經接近完成狀態(tài)...這里讓當前線程再釋放CPU,進行下一次搶占CPU。else if (s == COMPLETING) // cannot time out yetThread.yield();//條件成立:第一次自旋。當前線程還未創(chuàng)建WaitNode對象。此時為當前線程創(chuàng)建WaitNode對象else if (q == null)q = new WaitNode();//條件成立:第二次自旋,當前線程已經創(chuàng)建WaitNode對象了,但是node對象還未入隊else if (!queued)//q.next = waiters 當前線程node節(jié)點next指向原隊列頭結點 waiters一直指向隊列的頭//CAS方式設置waiters引用指向當前線程node,成功的話, queued=true,否則,可能有其他線程先你一步入隊了。queued = UNSAFE.compareAndSwapObject(this, waitersOffset,q.next = waiters, q);else if (timed) {nanos = deadline - System.nanoTime();if (nanos <= 0L) {removeWaiter(q);return state;}LockSupport.parkNanos(this, nanos);}else//當前get操作的線程會被park了。線程狀態(tài)會變?yōu)閃AITING狀態(tài),相當于休眠了..//除非有其他線程將你喚醒 或者 將當前線程中斷LockSupport.park(this);}}private void removeWaiter(WaitNode node) {if (node != null) {node.thread = null;retry:for (;;) { // restart on removeWaiter racefor (WaitNode pred = null, q = waiters, s; q != null; q = s) {s = q.next;if (q.thread != null)pred = q;else if (pred != null) {pred.next = s;if (pred.thread == null) // check for racecontinue retry;}else if (!UNSAFE.compareAndSwapObject(this, waitersOffset,q, s))continue retry;}break;}}} }

?

總結

以上是生活随笔為你收集整理的java基础源码的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。