1. 程式人生 > >深入解析HashMap、ConcurrentHashMap丶HashTable丶ArrayList

深入解析HashMap、ConcurrentHashMap丶HashTable丶ArrayList

Java集合類是個非常重要的知識點,HashMap、HashTable、ConcurrentHashMap等算是集合類中的重點,可謂“重中之重”,首先來看個問題,如面試官問你:HashMap和HashTable有什麼區別,一個比較簡單的回答是: 
1、HashMap是非執行緒安全的,HashTable是執行緒安全的。 
2、HashMap的鍵和值都允許有null值存在,而HashTable則不行。 
3、因為執行緒安全的問題,HashMap效率比HashTable的要高

能答出上面的三點,簡單的面試,算是過了,但是如果再問:Java中的另一個執行緒安全的與HashMap極其類似的類是什麼?同樣是執行緒安全,它與HashTable線上程同步上有什麼不同?能把第二個問題完整的答出來,說明你的基礎算是不錯的了。帶著這個問題深入解HashMap和HashTable類應用而生

一、HashMap的內部儲存結構 
Java中資料儲存方式最底層的兩種結構,一種是陣列,另一種就是連結串列,陣列的特點:連續空間,定址迅速,但是在刪除或者新增元素的時候需要有較大幅度的移動,所以查詢速度快,增刪較慢。而連結串列正好相反,由於空間不連續,定址困難,增刪元素只需修改指標,所以查詢慢、增刪快。有沒有一種資料結構來綜合一下陣列和連結串列,以便發揮他們各自的優勢?答案是肯定的!就是:雜湊表。雜湊表具有較快(常量級)的查詢速度,及相對較快的增刪速度,所以很適合在海量資料的環境中使用。一般實現雜湊表的方法採用“拉鍊法”,我們可以這裡寫圖片描述理解為“連結串列的陣列”,如下圖: 
從上圖中,我們可以發現雜湊表是由陣列+連結串列組成的,一個長度為16的陣列中,每個元素儲存的是一個連結串列的頭結點。那麼這些元素是按照什麼樣的規則儲存到陣列中呢。一般情況是通過hash(key)%len獲得,也就是元素的key的雜湊值對陣列長度取模得到。比如上述雜湊表中,12%16=12,28%16=12,108%16=12,140%16=12。所以12、28、108以及140都儲存在陣列下標為12的位置。它的內部其實是用一個Entity陣列來實現的,屬性有key、value、next。接下來我會從初始化階段詳細的講解HashMap的內部結構。

1、初始化

首先來看三個常量: 
static final int DEFAULT_INITIAL_CAPACITY = 16; 初始容量:16 
static final int MAXIMUM_CAPACITY = 1 
<< 30; 最大容量:2的30次方:1073741824 
static final float DEFAULT_LOAD_FACTOR = 0.75f; 
裝載因子,後面再說它的作用 
來看個無參構造方法,也是我們最常用的:

[java] view plain copy
public HashMap() {  
        this.loadFactor = DEFAULT_LOAD_FACTOR;  
        threshold = (int
)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR); table = new Entry[DEFAULT_INITIAL_CAPACITY]; init(); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

loadFactor、threshold的值在此處沒有起到作用,不過他們在後面的擴容方面會用到,此處只需理解table=new Entry[DEFAULT_INITIAL_CAPACITY].說明,預設就是開闢16個大小的空間。另外一個重要的構造方法:

ublic HashMap(int initialCapacity, float loadFactor) {  
        if (initialCapacity < 0)  
            throw new IllegalArgumentException("Illegal initial capacity: " +  
                                               initialCapacity);  
        if (initialCapacity > MAXIMUM_CAPACITY)  
            initialCapacity = MAXIMUM_CAPACITY;  
        if (loadFactor <= 0 || Float.isNaN(loadFactor))  
            throw new IllegalArgumentException("Illegal load factor: " +  
                                               loadFactor);  

        // Find a power of 2 >= initialCapacity  
        int capacity = 1;  
        while (capacity < initialCapacity)  
            capacity <<= 1;  

        this.loadFactor = loadFactor;  
        threshold = (int)(capacity * loadFactor);  
        table = new Entry[capacity];  
        init();  
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

就是說傳入引數的構造方法,我們把重點放在:while (capacity <

initialCapacity)  
           capacity <<= 1; 
  • 1
  • 2

上面,該程式碼的意思是,實際的開闢的空間要大於傳入的第一個引數的值。舉個例子: 
new HashMap(7,0.8),loadFactor為0.8,capacity為7,通過上述程式碼後,capacity的值為:8.(1 << 2的結果是4,2 << 2的結果為8<此處感謝網友wego1234的指正>)。所以,最終capacity的值為8,最後通過new Entry[capacity]來建立大小為capacity的陣列,所以,這種方法最紅取決於capacity的大小。 
2、put(Object key,Object value)操作 
當呼叫put操作時,首先判斷key是否為null,如下程式碼1處:

<p>public V put(K key, V value) {  
        if (key == null)  
            return putForNullKey(value);  
        int hash = hash(key.hashCode());  
        int i = indexFor(hash, table.length);  
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {  
            Object k;  
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {  
                V oldValue = e.value;  
                e.value = value;  
                e.recordAccess(this);  
                return oldValue;  
            }  
        }</p><p>        modCount++;  
        addEntry(hash, key, value, i);  
        return null;  
    }</p>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

如果key是null,則呼叫如下程式碼:

private V putForNullKey(V value) {  
        for (Entry<K,V> e = table[0]; e != null; e = e.next) {  
            if (e.key == null) {  
                V oldValue = e.value;  
                e.value = value;  
                e.recordAccess(this);  
                return oldValue;  
            }  
        }  
        modCount++;  
        addEntry(0, null, value, 0);  
        return null;  
    }  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

就是說,獲取Entry的第一個元素table[0],並基於第一個元素的next屬性開始遍歷,直到找到key為null的Entry,將其value設定為新的value值。 
如果沒有找到key為null的元素,則呼叫如上述程式碼的addEntry(0, null, value, 0);增加一個新的entry,程式碼如下:

void addEntry(int hash, K key, V value, int bucketIndex) {  
    Entry<K,V> e = table[bucketIndex];  
        table[bucketIndex] = new Entry<K,V>(hash, key, value, e);  
        if (size++ >= threshold)  
            resize(2 * table.length);  
    } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

先獲取第一個元素table[bucketIndex],傳給e物件,新建一個entry,key為null,value為傳入的value值,next為獲取的e物件。如果容量大於threshold,容量擴大2倍。 
如果key不為null,這也是大多數的情況,重新看一下原始碼:

public V put(K key, V value) {  
        if (key == null)  
            return putForNullKey(value);  
        int hash = hash(key.hashCode());//---------------2---------------  
        int i = indexFor(hash, table.length);  
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {//--------------3-----------  
            Object k;  
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {  
                V oldValue = e.value;  
                e.value = value;  
                e.recordAccess(this);  
                return oldValue;  
            }  
        }//-------------------4------------------  
        modCount++;//----------------5----------  
        addEntry(hash, key, value, i);-------------6-----------  
        return null;  
    }  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

看原始碼中2處,首先會進行key.hashCode()操作,獲取key的雜湊值,hashCode()是Object類的一個方法,為本地方法,內部實現比較複雜,我們 
會在後面作單獨的關於Java中Native方法的分析中介紹。hash()的原始碼如下

static int hash(int h) {  
        // This function ensures that hashCodes that differ only by  
        // constant multiples at each bit position have a bounded  
        // number of collisions (approximately 8 at default load factor).  
        h ^= (h >>> 20) ^ (h >>> 12);  
        return h ^ (h >>> 7) ^ (h >>> 4);  
    } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

int i = indexFor(hash, table.length);的意思,相當於int i = hash % Entry[].length;得到i後,就是在Entry陣列中的位置,(上述程式碼5和6處是如果Entry陣列中不存在新要增加的元素,則執行5,6處的程式碼,如果存在,即Hash衝突,則執行 3-4處的程式碼,此處HashMap中採用鏈地址法解決Hash衝突。此處經網友bbycszh指正,發現上述陳述有些問題)。重新解釋:其實不管Entry陣列中i位置有無元素,都會去執行5-6處的程式碼,如果沒有,則直接新增,如果有,則將新元素設定為Entry[0],其next指標指向原有物件,即原有物件為Entry[1]。具體方法可以解釋為下面的這段文字:(3-4處的程式碼只是檢查在索引為i的這條鏈上有沒有key重複的,有則替換且返回原值,程式不再去執行5-6處的程式碼,無則無處理) 
上面我們提到過Entry類裡面有一個next屬性,作用是指向下一個Entry。如, 第一個鍵值對A進來,通過計算其key的hash得到的i=0,記做:Entry[0] = A。一會後又進來一個鍵值對B,通過計算其i也等於0,現在怎麼辦?HashMap會這樣做:B.next = A,Entry[0] = B,如果又進來C,i也等於0,那麼C.next = B,Entry[0] = C;這樣我們發現i=0的地方其實存取了A,B,C三個鍵值對,他們通過next這個屬性連結在一起,也就是說陣列中儲存的是最後插入的元素。 
到這裡為止,HashMap的大致實現,我們應該已經清楚了。當然HashMap裡面也包含一些優化方面的實現,這裡也說一下。比如:Entry[]的長度一定後,隨著map裡面資料的越來越長,這樣同一個i的鏈就會很長,會不會影響效能?HashMap裡面設定一個因素(也稱為因子),隨著map的size越來越大,Entry[]會以一定的規則加長長度。

2、get(Object key)操作 
get(Object key)操作時根據鍵來獲取值,如果瞭解了put操作,get操作容易理解,先來看看原始碼的實現:

public V get(Object key) {  
        if (key == null)  
            return getForNullKey();  
        int hash = hash(key.hashCode());  
        for (Entry<K,V> e = table[indexFor(hash, table.length)];  
             e != null;  
             e = e.next) {  
            Object k;  
            if (e.hash == hash && ((k = e.key) == key || key.equals(k)))//-------------------1----------------  
                return e.value;  
        }  
        return null;  
    }  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

意思就是:1、當key為null時,呼叫getForNullKey(),原始碼如下:

private V getForNullKey() {  
        for (Entry<K,V> e = table[0]; e != null; e = e.next) {  
            if (e.key == null)  
                return e.value;  
        }  
        return null;  
    } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

當key不為null時,先根據hash函式得到hash值,在更具indexFor()得到i的值,迴圈遍歷連結串列,如果有:key值等於已存在的key值,則返回其value。如上述get()程式碼1處判斷。 
總結下HashMap新增put和獲取get操作:

//儲存時:  
int hash = key.hashCode();  
int i = hash % Entry[].length;  
Entry[i] = value;  

//取值時:  
int hash = key.hashCode();  
int i = hash % Entry[].length;  
return Entry[i]; 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

理解了就比較簡單。 
此處附一個簡單的HashMap小演算法應用:

package com.xtfggef.hashmap;  

import java.util.HashMap;  
import java.util.Map;  
import java.util.Set;  

/** 
 * 列印在陣列中出現n/2以上的元素 
 * 利用一個HashMap來存放陣列元素及出現的次數 
 * @author erqing 
 * 
 */  
public class HashMapTest {  

    public static void main(String[] args) {  

        int [] a = {2,3,2,2,1,4,2,2,2,7,9,6,2,2,3,1,0};  

        Map<Integer, Integer> map = new HashMap<Integer,Integer>();  
        for(int i=0; i<a.length; i++){  
            if(map.containsKey(a[i])){  
                int tmp = map.get(a[i]);  
                tmp+=1;  
                map.put(a[i], tmp);  
            }else{  
                map.put(a[i], 1);  
            }  
        }  
        Set<Integer> set = map.keySet();//------------1------------  
        for (Integer s : set) {  
            if(map.get(s)>=a.length/2){  
                System.out.println(s);  
            }  
        }//--------------2---------------  
    }  
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

此處注意兩個地方,map.containsKey(), 
理解了HashMap的上面的操作,其它的大多數方法都很容易理解了。搞清楚它的內部儲存機制,一切OK!

二、HashTable的內部儲存結構 
HashTable和HashMap採用相同的儲存機制,二者的實現基本一致,不同的是: 
1、HashMap是非執行緒安全的,HashTable是執行緒安全的,內部的方法基本都是synchronized。 
2、HashTable不允許有null值的存在。 
在HashTable中呼叫put方法時,如果key為null,直接丟擲NullPointerException。其它細微的差別還有,比如初始化Entry陣列的大小等等,但基本思想和HashMap一樣。 
三、HashTable和ConcurrentHashMap的比較 
如我開篇所說一樣,ConcurrentHashMap是執行緒安全的HashMap的實現。同樣是執行緒安全的類,它與HashTable在同步方面有什麼不同呢? 
之前我們說,synchronized關鍵字加鎖的原理,其實是對物件加鎖,不論你是在方法前加synchronized還是語句塊前加,鎖住的都是物件整體,但是ConcurrentHashMap的同步機制和這個不同,它不是加synchronized關鍵字,而是基於lock操作的,這樣的目的是保證同步的時候,鎖住的不是整個物件。事實上,ConcurrentHashMap可以滿足concurrentLevel個執行緒併發無阻塞的操作集合物件。關於concurrentLevel稍後介紹。 
1、構造方法 
為了容易理解,我們先從建構函式說起。ConcurrentHashMap是基於一個叫Segment陣列的,其實和Entry類似,如下:

public ConcurrentHashMap()  
  {  
    this(16, 0.75F, 16);  
  } 
  • 1
  • 2
  • 3
  • 4

預設傳入值16,呼叫下面的方法:

public ConcurrentHashMap(int paramInt1, float paramFloat, int paramInt2)  
  {  
    if ((paramFloat <= 0F) || (paramInt1 < 0) || (paramInt2 <= 0))  
      throw new IllegalArgumentException();  

    if (paramInt2 > 65536) {  
      paramInt2 = 65536;  
    }  

    int i = 0;  
    int j = 1;  
    while (j < paramInt2) {  
      ++i;  
      j <<= 1;  
    }  
    this.segmentShift = (32 - i);  
    this.segmentMask = (j - 1);  
    this.segments = Segment.newArray(j);  

    if (paramInt1 > 1073741824)  
      paramInt1 = 1073741824;  
    int k = paramInt1 / j;  
    if (k * j < paramInt1)  
      ++k;  
    int l = 1;  
    while (l < k)  
      l <<= 1;  

    for (int i1 = 0; i1 < this.segments.length; ++i1)  
      this.segments[i1] = new Segment(l, paramFloat);  
  }  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

你會發現比HashMap的建構函式多一個引數,paramInt1就是我們之前談過的initialCapacity,就是陣列的初始化大小,paramfloat為loadFactor(裝載因子),而paramInt2則是我們所要說的concurrentLevel,這三個值分別被初始化為16,0.75,16,經過:

while (j < paramInt2) {  
      ++i;  
      j <<= 1;  
    }  
  • 1
  • 2
  • 3
  • 4

後,j就是我們最終要開闢的陣列的size值,當paramInt1為16時,計算出來的size值就是16.通過: 
this.segments = Segment.newArray(j)後,我們看出了,最終稿建立的Segment陣列的大小為16.最終建立Segment物件時:

this.segments[i1] = new Segment(cap, paramFloat);  
  • 1

需要cap值,而cap值來源於:

int k = paramInt1 / j;  
  if (k * j < paramInt1)  
    ++k;  
  int cap = 1;  
  while (cap < k)  
    cap <<= 1;  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
public V put(K paramK, V paramV)  
  {  
    if (paramV == null)  
      throw new NullPointerException();  
    int i = hash(paramK.hashCode());  
    return segmentFor(i).put(paramK, i, paramV, false);  
  }  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

與HashMap不同的是,如果key為null,直接丟擲NullPointer異常,之後,同樣先計算hashCode的值,再計算hash值,不過此處hash函式和HashMap中的不一樣:

private static int hash(int paramInt)  
  {  
    paramInt += (paramInt << 15 ^ 0xFFFFCD7D);  
    paramInt ^= paramInt >>> 10;  
    paramInt += (paramInt << 3);  
    paramInt ^= paramInt >>> 6;  
    paramInt += (paramInt << 2) + (paramInt << 14);  
    return (paramInt ^ paramInt >>> 16);  
  }  


[java] view plain copy
final Segment<K, V> segmentFor(int paramInt)  
  {  
    return this.segments[(paramInt >>> this.segmentShift & this.segmentMask)];  
  } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

根據上述程式碼找到Segment物件後,呼叫put來操作:

V put(K paramK, int paramInt, V paramV, boolean paramBoolean)  
{  
  lock();  
  try {  
    Object localObject1;  
    Object localObject2;  
    int i = this.count;  
    if (i++ > this.threshold)  
      rehash();  
    ConcurrentHashMap.HashEntry[] arrayOfHashEntry = this.table;  
    int j = paramInt & arrayOfHashEntry.length - 1;  
    ConcurrentHashMap.HashEntry localHashEntry1 = arrayOfHashEntry[j];  
    ConcurrentHashMap.HashEntry localHashEntry2 = localHashEntry1;  
    while ((localHashEntry2 != null) && (((localHashEntry2.hash != paramInt) || (!(paramK.equals(localHashEntry2.key)))))) {  
      localHashEntry2 = localHashEntry2.next;  
    }  

    if (localHashEntry2 != null) {  
      localObject1 = localHashEntry2.value;  
      if (!(paramBoolean))  
        localHashEntry2.value = paramV;  
    }  
    else {  
      localObject1 = null;  
      this.modCount += 1;  
      arrayOfHashEntry[j] = new ConcurrentHashMap.HashEntry(paramK, paramInt, localHashEntry1, paramV);  
      this.count = i;  
    }  
    return localObject1;  
  } finally {  
    unlock();  
  }  
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

先呼叫lock(),lock是ReentrantLock類的一個方法,用當前儲存的個數+1來和threshold比較,如果大於threshold,則進行rehash,將當前的容量擴大2倍,重新進行hash。之後對hash的值和陣列大小-1進行按位於操作後,得到當前的key需要放入的位置,從這兒開始,和HashMap一樣。 
從上述的分析看出,ConcurrentHashMap基於concurrentLevel劃分出了多個Segment來對key-value進行儲存,從而避免每次鎖定整個陣列,在預設的情況下,允許16個執行緒併發無阻塞的操作集合物件,儘可能地減少併發時的阻塞現象。 
在多執行緒的環境中,相對於HashTable,ConcurrentHashMap會帶來很大的效能提升! 
ArrayList工作原理

ArrayList工作原理其實很簡單,底層是動態陣列,每次建立一個ArrayList例項時會分配一個初始容量(如果指定了初始容量的話),以add方法為例,如果沒有指定初始容量,當執行add方法,先判斷當前陣列是否為空,如果為空則給儲存物件的陣列分配一個最小容量,這裡為10。當新增大容量元素額時候,會先增加陣列的大小,以提高新增的效率。 
把ArrayList理解為一個數組就好了 先看看增大容量的方法

privatevoidgrow(int minCapacity) {
       // overflow-conscious code
       int oldCapacity = elementData.length;
       int newCapacity = oldCapacity + (oldCapacity >> 1);
       if (newCapacity - minCapacity < 0)
           newCapacity = minCapacity;
       
            
           

相關推薦

深入解析HashMapConcurrentHashMapHashTableArrayList

Java集合類是個非常重要的知識點,HashMap、HashTable、ConcurrentHashMap等算是集合類中的重點,可謂“重中之重”,首先來看個問題,如面試官問你:HashMap和HashTable有什麼區別,一個比較簡單的回答是:  1、HashMap是非執

HashTableHashMapConcurrentHashMap的原理與區別

希望各位小夥伴能帶著如下幾個問題來進行閱讀,這樣收穫會更大。 1.HashTable、HashMap、ConcurrentHashMap的區別? 2.HashMap執行緒不安全的出現場景? 3.HashMap put方法存放資料時是怎麼判斷是否重複的? 4.JD

深入解析HashMap

原文: http://www.iteye.com/topic/539465     Hashmap是一種非常常用的、應用廣泛的資料型別,最近研究到相關的內容,就正好複習一下。網上關於hashmap的文章很多,但到底是自己學習的總結,就發出來跟大家一起分享,一起討論。

HashMapConcurrentHashMapHashTable的底層原理與剖析

    HashMap  可以允許key為null,value為null,但HashMap的是執行緒不安全的  HashMap 底層是陣列 + 連結串列的資料結構 在jdk 1.7 中 map集合中的每一項都是一個 entry   在jdk 1.8 中

HashMapConcurrentHashMap實現原理及原始碼分析

HashMap:https://www.cnblogs.com/chengxiao/p/6059914.html ConcurrentHashMap:https://blog.csdn.net/dingjianmin/article/details/79776646   遺留問

HashMapTreeMap和HashTable的區別

Map介面有三個比較重要的實現類,分別是HashMap、TreeMap和HashTable。 TreeMap是有序的,HashMap和HashTable是無序的。 Hashtable的方法是同步的,HashMap的方法不是同步的。這是兩者最主要的區別。 這就意味著Hashtable是執行緒安全的,HashMa

HashMapConcurrentHashMap物件put(K key, V value)方法實現

以下原始碼版本為java8,與java7版本的HashMap原始碼有所差異,請區分。 HashMap物件put(K key, V value)原始碼 public V put(K key, V value) { return putVal(hash(key), key

Java中關於Map的使用(HashMapConcurrentHashMap

get java 多線程 tree ict per The name user 在日常開發中Map可能是Java集合框架中最常用的一個類了,當我們常規使用HashMap時可能會經常看到以下這種代碼: Map<Integer, String> hashMap =

HashMapTreeMapHashtableHashSet和ConcurrentHashMap區別

擴展性 navig shc .net ica fin details blank table 一、HashMap和TreeMap區別 1、HashMap是基於散列表實現的,時間復雜度平均能達到O(1)。 TreeMap基於紅黑樹(一種自平衡二叉查找樹)實現的,時

HashTableHashMapConCurrentHashMap源碼OA現金盤平臺出租解讀

cti 那不 累加 map集合 版本 結點 精神 plain objects HashMap 的數據結構? hashMap 初始的數據結構如下圖所示OA現金盤平臺出租QQ2952777280【話仙源碼論壇】hxforum.com【木瓜源碼論壇】papayabbs.com,內

HashMapHashtableConcurrentHashMap的原理與區別

另一個 cnblogs 需要 構造器 新的 底層 bsp 哈希 fas HashTable 底層數組+鏈表實現,無論key還是value都不能為null,線程安全,實現線程安全的方式是在修改數據時鎖住整個HashTable,效率低,ConcurrentHashMap做了相

hashmap資料結構詳解(五)之HashMapHashTableConcurrentHashMap 的區別

【hashmap 與 hashtable】   hashmap資料結構詳解(一)之基礎知識奠基 hashmap資料結構詳解(二)之走進JDK原始碼 hashmap資料結構詳解(三)之hashcode例項及大小是2的冪次方解釋 hashmap資料結構詳解(四)之has

多執行緒(九): HashTableHashMapConcurrentHashMap

public class HashTest { static Map<String, Integer> map = new HashMap<String, Integer>(); // static Map<String, Integer&g

HashMapHashtableConcurrentHashMap對比

1.執行緒不安全的HashMap 因為多執行緒環境下,使用Hashmap進行put操作會引起死迴圈,導致CPU利用率接近100%,所以在併發情況下不能使用HashMap。 2.效率低下的HashTable容器 HashTable容器使用synchronized來保證執行緒安全,但線

hashMap,hashTable,concurrentHashmap的區別 面試必備:HashMapHashtableConcurrentHashMap的原理與區別

轉自  面試必備:HashMap、Hashtable、ConcurrentHashMap的原理與區別 果你去面試,面試官不問你這個問題,你來找我^_^ 下面直接來乾貨,先說這三個Map的區別: HashTable 底層陣列+連結串列實現,無論key還是value都不能為null,執行

Java原始碼分析——java.util工具包解析(三)——HashMapTreeMapLinkedHashMapHashtable解析

    Map,中文名字對映,它儲存了鍵-值對的一對一的關係形式,並用雜湊值來作為存貯的索引依據,在查詢、插入以及刪除時的時間複雜度都為O(1),是一種在程式中用的最多的幾種資料結構。Java在java.util工具包中實現了Map介面,來作為各大

各種集合框架的總結ArrayListLinkedListVectorHashMapHashTableHashSetLinkedHaSetTreeSetConcurrentHashMap

這幾道Java集合框架面試題在面試中幾乎必問  1.Arraylist 與 LinkedList 異同 1. 執行緒安全: ArrayList 和 LinkedList 都是執行緒不安全的; 2. 資料結構: Arraylist 底層使用的是Object陣列;Linked

Java集合——HashMapHashTable以及ConCurrentHashMap異同比較

0. 前言 HashMap和HashTable的區別一種比較簡單的回答是: (1)HashMap是非執行緒安全的,HashTable是執行緒安全的。 (2)HashMap的鍵和值都允許有null存在,而HashTable則都不行。 (3)因為執行緒安全、雜湊效率的

面試必備:HashMapHashtableConcurrentHashMap的原理與區別

如果你去面試,面試官不問你這個問題,你來找我^_^ 下面直接來乾貨,先說這三個Map的區別: HashTable 底層陣列+連結串列實現,無論key還是value都不能為null,執行緒安全,實現執行緒安全的方式是在修改資料時鎖住整個HashTable,效率低,Conc

HashMapHashTableConcurrentHashMap區別及工作原理

前言 第一次寫部落格,水平有限可能有理解不到位或理解錯的地方。歡迎各位大神參與討論或指正。 Map在工作中的使用頻率較高,HashMap相關的問題在面試中也經常被問到。所以抽空在網上找資料對它們進行了系統的學習,作出以下幾點總結: HashMap、Hash