圖解Java常用數(shù)據(jù)結(jié)構(gòu)

最近在整理數(shù)據(jù)結(jié)構(gòu)方面的知識(shí), 系統(tǒng)化看了下Java中常用數(shù)據(jù)結(jié)構(gòu), 突發(fā)奇想用動(dòng)畫來繪制數(shù)據(jù)流轉(zhuǎn)過程.

主要基于jdk8, 可能會(huì)有些特性與jdk7之前不相同, 例如LinkedList LinkedHashMap中的雙向列表不再是回環(huán)的.

HashMap中的單鏈表是尾插, 而不是頭插入等等, 后文不再贅敘這些差異, 本文目錄結(jié)構(gòu)如下:

image

LinkedList

經(jīng)典的雙鏈表結(jié)構(gòu), 適用于亂序插入, 刪除. 指定序列操作則性能不如ArrayList, 這也是其數(shù)據(jù)結(jié)構(gòu)決定的.

add(E) / addLast(E)

image

add(index, E)

這邊有個(gè)小的優(yōu)化, 他會(huì)先判斷index是靠近隊(duì)頭還是隊(duì)尾, 來確定從哪個(gè)方向遍歷鏈入.

if (index < (size >> 1)) {
   Node<E> x = first;
  for (int i = 0; i < index; i++)
  x = x.next;
  return x;6         
} else {
  Node<E> x = last;
  for (int i = size - 1; i > index; i--)
  x = x.prev;
  return x;
}
image

靠隊(duì)尾

image

get(index)

也是會(huì)先判斷index, 不過性能依然不好, 這也是為什么不推薦用for(int i = 0; i < lengh; i++)的方式遍歷linkedlist, 而是使用iterator的方式遍歷.

image
image

remove(E)

image

ArrayList

底層就是一個(gè)數(shù)組, 因此按序查找快, 亂序插入, 刪除因?yàn)樯婕暗胶竺嬖匾莆凰孕阅苈?

add(index, E)

image

擴(kuò)容

一般默認(rèn)容量是10, 擴(kuò)容后, 會(huì)length*1.5.

image

remove(E)

循環(huán)遍歷數(shù)組, 判斷E是否equals當(dāng)前元素, 刪除性能不如LinkedList.

image

Stack

經(jīng)典的數(shù)據(jù)結(jié)構(gòu), 底層也是數(shù)組, 繼承自Vector, 先進(jìn)后出FILO, 默認(rèn)new Stack()容量為10, 超出自動(dòng)擴(kuò)容.

push(E)

image

pop()

image

后綴表達(dá)式

Stack的一個(gè)典型應(yīng)用就是計(jì)算表達(dá)式如 9 + (3 - 1) * 3 + 10 / 2, 計(jì)算機(jī)將中綴表達(dá)式轉(zhuǎn)為后綴表達(dá)式, 再對后綴表達(dá)式進(jìn)行計(jì)算.

中綴轉(zhuǎn)后綴

  • 數(shù)字直接輸出

  • 棧為空時(shí)愧旦,遇到運(yùn)算符,直接入棧

  • 遇到左括號(hào), 將其入棧

  • 遇到右括號(hào), 執(zhí)行出棧操作肢藐,并將出棧的元素輸出彼绷,直到彈出棧的是左括號(hào)乌妙,左括號(hào)不輸出袄膏。

  • 遇到運(yùn)算符(加減乘除):彈出所有優(yōu)先級(jí)大于或者等于該運(yùn)算符的棧頂元素布轿,然后將該運(yùn)算符入棧

  • 最終將棧中的元素依次出棧,輸出朝聋。

image

計(jì)算后綴表達(dá)

  • 遇到數(shù)字時(shí)嗡午,將數(shù)字壓入堆棧

  • 遇到運(yùn)算符時(shí),彈出棧頂?shù)膬蓚€(gè)數(shù)玖翅,用運(yùn)算符對它們做相應(yīng)的計(jì)算, 并將結(jié)果入棧

  • 重復(fù)上述過程直到表達(dá)式最右端

  • 運(yùn)算得出的值即為表達(dá)式的結(jié)果

image

隊(duì)列

與Stack的區(qū)別在于, Stack的刪除與添加都在隊(duì)尾進(jìn)行, 而Queue刪除在隊(duì)頭, 添加在隊(duì)尾.

ArrayBlockingQueue

生產(chǎn)消費(fèi)者中常用的阻塞有界隊(duì)列, FIFO.

put(E)

image

put(E) 隊(duì)列滿了

    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == items.length)
        notFull.await();
        enqueue(e);
    } finally {
        lock.unlock();
    }
image

take()

當(dāng)元素被取出后, 并沒有對數(shù)組后面的元素位移, 而是更新takeIndex來指向下一個(gè)元素.

takeIndex是一個(gè)環(huán)形的增長, 當(dāng)移動(dòng)到隊(duì)列尾部時(shí), 會(huì)指向0, 再次循環(huán).

     private E dequeue() {
         // assert lock.getHoldCount() == 1;
         // assert items[takeIndex] != null;
         final Object[] items = this.items;
         @SuppressWarnings("unchecked")
         E x = (E) items[takeIndex];
         items[takeIndex] = null;
         if (++takeIndex == items.length)
             takeIndex = 0;
         count--;
        if (itrs != null)
            itrs.elementDequeued();
         notFull.signal();
        return x;
    }
image

HashMap

最常用的哈希表, 面試的童鞋必備知識(shí)了, 內(nèi)部通過數(shù)組 + 單鏈表的方式實(shí)現(xiàn). jdk8中引入了紅黑樹對長度 > 8的鏈表進(jìn)行優(yōu)化, 我們另外篇幅再講.

put(K, V****)

image

put(K, V) 相同hash值

image

resize 動(dòng)態(tài)擴(kuò)容

當(dāng)map中元素超出設(shè)定的閾值后, 會(huì)進(jìn)行resize (length * 2)操作, 擴(kuò)容過程中對元素一通操作, 并放置到新的位置.

具體操作如下:

  • 在jdk7中對所有元素直接rehash, 并放到新的位置.

  • 在jdk8中判斷元素原h(huán)ash值新增的bit位是0還是1, 0則索引不變, 1則索引變成"原索引 + oldTable.length".

     //定義兩條鏈
     //原來的hash值新增的bit為0的鏈翼馆,頭部和尾部
     Node<K,V> loHead = null, loTail = null;
     //原來的hash值新增的bit為1的鏈,頭部和尾部
     Node<K,V> hiHead = null, hiTail = null;
     Node<K,V> next;
     //循環(huán)遍歷出鏈條鏈
     do {
         next = e.next; 
        if ((e.hash & oldCap) == 0) {
             if (loTail == null)
                 loHead = e;
             else
                loTail.next = e;
             loTail = e;
         } else {
             if (hiTail == null)
                 hiHead = e;
             else
                 hiTail.next = e;
             hiTail = e;
         }
     } while ((e = next) != null);
     //擴(kuò)容前后位置不變的鏈
     if (loTail != null) {
         loTail.next = null;
         newTab[j] = loHead;
     }
     //擴(kuò)容后位置加上原數(shù)組長度的鏈
     if (hiTail != null) {
         hiTail.next = null;
         newTab[j + oldCap] = hiHead;
     }
image

LinkedHashMap

繼承自HashMap, 底層額外維護(hù)了一個(gè)雙向鏈表來維持?jǐn)?shù)據(jù)有序. 可以通過設(shè)置accessOrder來實(shí)現(xiàn)FIFO(插入有序)或者LRU(訪問有序)緩存.

put(K, V)

image

get(K)

accessOrder為false的時(shí)候, 直接返回元素就行了, 不需要調(diào)整位置.

accessOrder為true的時(shí)候, 需要將最近訪問的元素, 放置到隊(duì)尾.

image

removeEldestEntry 刪除最老的元素

image
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末金度,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子严沥,更是在濱河造成了極大的恐慌猜极,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件消玄,死亡現(xiàn)場離奇詭異跟伏,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)翩瓜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門受扳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人兔跌,你說我怎么就攤上這事勘高。” “怎么了坟桅?”我有些...
    開封第一講書人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵华望,是天一觀的道長。 經(jīng)常有香客問我仅乓,道長赖舟,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任夸楣,我火速辦了婚禮宾抓,結(jié)果婚禮上子漩,老公的妹妹穿的比我還像新娘。我一直安慰自己石洗,他們只是感情好幢泼,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著劲腿,像睡著了一般旭绒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上焦人,一...
    開封第一講書人閱讀 52,156評(píng)論 1 308
  • 那天挥吵,我揣著相機(jī)與錄音,去河邊找鬼花椭。 笑死忽匈,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的矿辽。 我是一名探鬼主播丹允,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼袋倔!你這毒婦竟也來了雕蔽?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬榮一對情侶失蹤宾娜,失蹤者是張志新(化名)和其女友劉穎批狐,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體前塔,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡嚣艇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了华弓。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片食零。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖寂屏,靈堂內(nèi)的尸體忽然破棺而出贰谣,到底是詐尸還是另有隱情,我是刑警寧澤凑保,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布冈爹,位于F島的核電站,受9級(jí)特大地震影響欧引,放射性物質(zhì)發(fā)生泄漏频伤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一芝此、第九天 我趴在偏房一處隱蔽的房頂上張望憋肖。 院中可真熱鬧因痛,春花似錦、人聲如沸岸更。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽怎炊。三九已至谭企,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間评肆,已是汗流浹背债查。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留瓜挽,地道東北人盹廷。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像久橙,于是被迫代替她去往敵國和親俄占。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容