国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Java容器類研究8:HashMap

zhichangterry / 625人閱讀

摘要:的值是在上述方法中處理過的值,通過與當前容量進行,直接獲取到哈希表的位置。策略二,如果已經很大了,擴容已經不可取,那么就采用紅黑樹結構轉化鏈表。紅黑樹的創建不再詳述。紅黑樹的根就是中第一個節點。

java.util.Map

Map中的自我引用

需要小心用易變的對象作為Map的key,這會導致Map的行為無法預測。Map也不可以將自己作為key,可以作為value,但是會導致equals和hashCode方法不是well defined.

Map中有些操作涉及遞歸的遍歷,如果Map自我引用,則有可能出現異常。這些方法包括:clone,equals,hashCode和toString。

Map可以將null作為key和value

這是Map接口自己默認實現的一個獲取指定key的value,當沒有該key時可以獲取一個默認值得方法。這就給用戶提供了更靈活的選擇來處理map中沒有存這個key的情況。這里比較有意思的一點是在用v = get(key)判斷一次后,為什么又用containsKey(key)再判斷一次,因為有的map中是允許存null作為value的,所以有key在Map中,但是value為null的情況。

    default V getOrDefault(Object key, V defaultValue) {
        V v;
        return (((v = get(key)) != null) || containsKey(key))
            ? v
            : defaultValue;
    }
HashMap 如何求int型數的最近2次冪

HashMap中,用戶可以初始化容量,但是HashMap中容量皆為2的次冪,所以會把用戶預設的值先轉為最近的2的次冪。tableSizeFor方法求出的結果總是大于或等于cap,且最接近cap的一個2的次冪。當然,對于大于2^30的數,會返回-2147483648,所以這里會判斷n為負數的情況,同時,設置了HashMap的最大容量應該為2^30(MAXIMUM_CAPACITY)。

有意思的是經過如下幾步就能求得一個2的次冪,下面方法所做的是先求得一個2^n-1,然后+1。獲得一個2^n-1的過程也很巧妙,就是不停的拷貝1。n |= n >>> x;將前x位的1拷貝到了接著的x位。通過幾步位操作就獲得了目標值,不得不贊嘆開發者的聰明。

    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;
    }
HashMap的table

HashMap的table就是一個Node的數組,大小一致保持2的次冪。Node就是HashMap中存儲的元素,它有哈希值、key、value和存儲下一個Node的next屬性。處理沖突的方法是閉哈希方法,也就是有相同的hash值的Node會用鏈表串起來。

HashMap中的hash值如何計算
    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

將key的hashCode的高位和低位部分通過異或合并,使得低位和高位的值都能在hash時發揮作用。因為哈希表會用一個掩碼來取hashCode的后面一段,如不采用上述方法,前面一部分的hashCode就被忽略了。如果一組key恰巧只在前段的hash值有所差異,而后段都是相同的,則會出現大量的沖突,這種情況在實際中是很可能存在的。

如何將Node哈希到table中

公式如:table[node.hash & (capacity - 1)] = node。node的hash值是key在上述hash()方法中處理過的值,通過與當前容量-1進行mask,直接獲取到哈希表的位置。

HashMap的table的擴張

如上所述,table大小保持2的次冪,擴張的步驟:

申請一個容量乘以2的新Node數組。

遍歷table,將原來的元素依次copy到新數組上,這里原來的鏈表會進行分裂。因為table的擴大,Node的hash值會被多mask一位,所以Node被Hash到的位置也會變化,Node要么保持原位置,要么就是在相對于原位置有一個舊容量大小的偏移。

這里不會將原來的元素重新進行一次hash的過程,重建原來的hash table,這樣代價是比較大的。這里就利用了Node位置變化的規律,直接將原來的鏈表分裂為兩個。

雖然已經進行了優化,但是該過程代價還是比較高的,時間復雜度為原table大小+元素數量,

table中鏈表太長如何處理

當Node發生多次沖突,在一個hash值下建了一個很長的鏈表,這會導致查詢的代價越來越大,這里采用了樹結構來減輕這種問題。

具體過程是,當某個hash值下的鏈表超過了閾值,則會采取策略對其長度進行消解。

如果table還不算大,那么直接對table擴容,鏈表自然會被分裂到兩地。

策略二,如果table已經很大了,擴容table已經不可取,那么就采用紅黑樹結構轉化鏈表。

紅黑樹的創建不再詳述。需要知道,這里采用的是二叉搜索樹,進行比較的是每個hash值,不是key的直接比較。紅黑樹的根就是table中第一個節點。原始鏈表會先變成雙向鏈表,以保存前后關系,然后再變成樹結構。雖然由鏈表轉化成了樹結構,但是每個節點仍然保存了鏈表的前后關系,所以可以迅速的從樹結構退化為鏈表結構。從TreeNode的屬性中可以看出其既有樹結構的left、right、parent關系,又有prev、next(繼承)的雙向鏈表關系。

        TreeNode parent;  // red-black tree links
        TreeNode left;
        TreeNode right;
        TreeNode prev;    // needed to unlink next upon deletion
HashMap的KeySet、Values和EntrySet

HashMap用KeySet來提供一個key的集合視角,KeySet并沒有再申請一個空間來存儲這些key,而是將所有的方法建立于HashMap的方法之上。KeySet提供了刪除key的操作,該操作會映射到其HashMap之上,最后就是在HashMap中刪除了該元素。KeySet沒有提供添加元素的方法,因為HashMap需要的是key和value,而KeySet只能添加key,方法不能映射到HashMap上。

同樣,HashMap提供了value的集合視角Values,EntrySet提供了Node集合的視角,原理類似。

HashMap的Spliterator

在HashMap中,分為key、value和node三種Spliterator,實現原理都是類似的。HashMap的Spliterator的劃分不是針對元素的直接劃分,而是對table這個數組的劃分,這樣更為簡單。劃分的策略也很簡單,采用的二分法。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/67894.html

相關文章

  • Java容器研究9:其它Map

    摘要:和的區別和的區別是,在操作的方法上加入關鍵字,使得線程安全。使用進行比較,或者傳入的比較器?;?,它自己的任務主要是維護保持順序的雙向鏈表。和的區別提供了一個高效的線程安全的訪問和更新的方式。在中的過程和類似。 HashTable和HashMap的區別 HashTable和HashMap的區別是,HashTable在操作table的方法上加入synchronized關鍵字,使得線程安全...

    zilu 評論0 收藏0
  • Java容器研究7:Set

    摘要:繼承自,繼承接口,接口繼承自。的也是使用的的的。中和方法理論上是常數時間的復雜度,前提是元素在中分散比較均勻。并且這些元素在結構中,根據進行了排序,所以用遍歷時可以按照遞增遞減順序。遍歷訪問元素的操作比更為高效,時間由元素數量決定。 java.util.Set HashSet繼承自AbstractSet,繼承接口Set,Set接口繼承自Collection。 null是否可以放在Set...

    RdouTyping 評論0 收藏0
  • Java容器研究1:Collection

    摘要:集合類關系是和的父接口。相等必須是對稱的,約定只能和其它相等,亦然。和接口在中引入,這個單詞是和的合成,用來分割集合以給并行處理提供方便。這些并不立即執行,而是等到最后一個函數,統一執行。 集合類關系: Collection ├List │├LinkedList │├ArrayList │└Vector │ └Stack └Set Map ...

    AprilJ 評論0 收藏0
  • 記一次慘痛的面試經歷

    摘要:把內存分成兩種,一種叫做棧內存,一種叫做堆內存在函數中定義的一些基本類型的變量和對象的引用變量都是在函數的棧內存中分配。堆內存用于存放由創建的對象和數組。 一次慘痛的阿里技術面 就在昨天,有幸接到了阿里的面試通知,本來我以為自己的簡歷應該不會的到面試的機會了,然而機會卻這么來了,我卻沒有做好準備,被面試官大大一通血虐。因此,我想寫點東西紀念一下這次的經歷,也當一次教訓了。其實面試官大大...

    CoorChice 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<