zoukankan      html  css  js  c++  java
  • HashMap和ConcurrentHashMap的区别,HashMap的底层源码

    HashMap和ConcurrentHashMap的区别:

    1. HashMap不是线程安全的,而ConcurrentHashMap是线程安全的。
    2. ConcurrentHashMap采用锁分段技术,将整个Hash桶进行了分段segment,也就是将这个大的数组分成了几个小的片段segment,而且每个小的片段segment上面都有锁存在,那么在插入元素的时候就需要先找到应该插入到哪一个片段segment,然后再在这个片段上面进行插入,而且这里还需要获取segment锁。

    3. ConcurrentHashMap让锁的粒度更精细一些,并发性能更好。

    HashMap的底层源码:

    一. 数据结构

          Map将实际数据存储在Entry类的数组中。

    transient Entry[] table;    //HashMap的成员变量,存放数据  
      
    static class Entry<K,V> implements Map.Entry<K,V> {    //内部类Entry  
        final K key;  
        V value;  
        Entry<K,V> next;     //指向下一个数据  
        final int hash;  
      
        /** 
         * Creates new entry. 
         */  
        Entry(int h, K k, V v, Entry<K,V> n) {  
            value = v;  
            next = n;  
            key = k;  
            hash = h;  
        }  

           执行put方法时,根据key的hash值来计算放到table数组的下标,如果hash有相同的下标,则新put进去的元素放到Entry链的头部。

    二. 属性和构造方法

    属性:

    1 static final int DEFAULT_INITIAL_CAPACITY = 16;  //默认的初始大小,如果执行无参的构造方法,则默认初始大小为16  
    2 static final int MAXIMUM_CAPACITY = 1 << 30;  //最大容量,1073741824  
    3 static final float DEFAULT_LOAD_FACTOR = 0.75f;  //默认的负载因子,如果没有通过构造方法传入负载因子,则使用0.75。  
    4 transient Entry[] table;   //存放具体键值对的Entry数组  
    5 transient int size;   //HashMap的大小  
    6 int threshold;    //阀值  threshold = (int)(capacity * loadFactor); 即容量*负载因子,执行put方法时如果size大于threshold则进行扩容,后面put方法将会看到  
    7 final float loadFactor;   //用户设置的负载因子  
    8 transient volatile int modCount;    //HashMap实例被改变的次数,这个同ArrayList  

    构造方法一:

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

    使用了默认的容量和默认的负载因子。

    构造方法二:

    1 public HashMap(int initialCapacity) {  
    2     this(initialCapacity, DEFAULT_LOAD_FACTOR);  
    3 } 

    使用了用户设置的初始容量和默认的负载因子。

    构造方法三:

     1 public HashMap(int initialCapacity, float loadFactor) {  
     2     if (initialCapacity < 0)  
     3         throw new IllegalArgumentException("Illegal initial capacity: " +  
     4                                            initialCapacity);  
     5     if (initialCapacity > MAXIMUM_CAPACITY)  
     6         initialCapacity = MAXIMUM_CAPACITY;  
     7     if (loadFactor <= 0 || Float.isNaN(loadFactor))  
     8         throw new IllegalArgumentException("Illegal load factor: " +  
     9                                            loadFactor);  
    10   
    11     // Find a power of 2 >= initialCapacity  
    12     int capacity = 1;  
    13     while (capacity < initialCapacity)  
    14         capacity <<= 1;  
    15   
    16     this.loadFactor = loadFactor;  
    17     threshold = (int)(capacity * loadFactor);  
    18     table = new Entry[capacity];  
    19     init();  
    20 }  

    用户传入了初始容量和负载因子,这两个值是HashMap性能优化的关键,涉及到了HashMap的扩容问题。

    HashMap的容量永远是2的倍数,如果传入的不是2的指数则被调整为大于传入值的最近的2的指数,例如如果传入130,则capacity计算后是256。是这段代码起的作用:

    1 while (capacity < initialCapacity)  
    2         capacity <<= 1; 

    构造方法四:

     1 public HashMap(Map<? extends K, ? extends V> m) {  
     2     this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,  
     3                   DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);//计算Map的大小  
     4     putAllForCreate(m);//初始化  
     5 }  
     6   
     7 private void putAllForCreate(Map<? extends K, ? extends V> m) {  
     8     for (Iterator<? extends Map.Entry<? extends K, ? extends V>> i = m.entrySet().iterator(); i.hasNext(); ) {//通过entryset进行遍历  
     9         Map.Entry<? extends K, ? extends V> e = i.next();  
    10         putForCreate(e.getKey(), e.getValue());  
    11     }  
    12 }  

    根据传入的map进行初始化。

    三. 关键方法

    1) put方法

     1 public V put(K key, V value) {  
     2     if (key == null)  
     3         return putForNullKey(value);  //单独处理,总是放到table[0]中  
     4     int hash = hash(key.hashCode());  //计算key的hash值,后面介绍性能的时候再说这个hash方法。  
     5     int i = indexFor(hash, table.length);  //将hash和length-1取&来得到数组的下表  
     6     for (Entry<K,V> e = table[i]; e != null; e = e.next) {  
     7         Object k;  
     8         if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {  
     9             V oldValue = e.value;  
    10             e.value = value;  
    11             e.recordAccess(this);  
    12             return oldValue;  
    13         }  
    14     }  //如果这个key值,原来已经则替换后直接返回。  
    15     modCount++;  
    16     addEntry(hash, key, value, i);  
    17     return null;  
    18 }  
    19 void addEntry(int hash, K key, V value, int bucketIndex) {  
    20 Entry<K,V> e = table[bucketIndex];  
    21        table[bucketIndex] = new Entry<K,V>(hash, key, value, e);  //如果table[bucketIndex]中已经存在Entry则放到头部。  
    22        if (size++ >= threshold)  //如果大于了阀值,则扩容到原来大小的2倍。  
    23            resize(2 * table.length);  
    24    }  
    25   
    26 void resize(int newCapacity) {  
    27     Entry[] oldTable = table;  
    28     int oldCapacity = oldTable.length;  
    29     if (oldCapacity == MAXIMUM_CAPACITY) {  
    30         threshold = Integer.MAX_VALUE;  
    31         return;  
    32     }  
    33   
    34     Entry[] newTable = new Entry[newCapacity];  
    35     transfer(newTable);  //赋值到新的table中,注意转移后会重新hash,所以位置可能会跟之前不同,目的是均匀分不到新的table中。  
    36     table = newTable;  
    37     threshold = (int)(newCapacity * loadFactor);  
    38 } 

    2) get方法

     1 public V get(Object key) {  
     2     if (key == null)  
     3         return getForNullKey();  
     4     int hash = hash(key.hashCode());  
     5     for (Entry<K,V> e = table[indexFor(hash, table.length)];//找到数组的下表,进行遍历  
     6          e != null;  
     7          e = e.next) {  
     8         Object k;  
     9         if (e.hash == hash && ((k = e.key) == key || key.equals(k)))  
    10             return e.value;//找到则返回  
    11     }  
    12     return null;//否则,返回null  
    13 }  

    3) remove方法

     1 public V remove(Object key) {  
     2     Entry<K,V> e = removeEntryForKey(key);  
     3     return (e == null ? null : e.value);  
     4 }  
     5   
     6 final Entry<K,V> removeEntryForKey(Object key) {  
     7     int hash = (key == null) ? 0 : hash(key.hashCode());  
     8     int i = indexFor(hash, table.length);  
     9     Entry<K,V> prev = table[i];  
    10     Entry<K,V> e = prev;  
    11   
    12     while (e != null) {//Entry链未遍历完则一直遍历  
    13         Entry<K,V> next = e.next;  
    14         Object k;  
    15         if (e.hash == hash &&  
    16             ((k = e.key) == key || (key != null && key.equals(k)))) {  
    17             modCount++;  
    18             size--;  
    19             if (prev == e)//如果是第一个,则将table[i]执行e.next  
    20                 table[i] = next;  
    21             else //否则将前一个的next指向e.next  
    22                 prev.next = next;   
    23             e.recordRemoval(this);  
    24             return e;  
    25         }  
    26         prev = e;//未找到则继续往后遍历  
    27         e = next;  
    28     }  
    29   
    30     return e;  
    31 }  

    4) HashMap的遍历方法

     1 Map map = new HashMap();  
     2 map.put("key1","value1");  
     3 map.put("key2","value2");  
     4 map.put("key3", "value3");  
     5 for(Iterator it = map.entrySet().iterator();it.hasNext();){  
     6     Map.Entry e = (Map.Entry)it.next();  
     7     System.out.println(e.getKey() + "=" + e.getValue());  
     8 }  
     9 System.out.println("-----------------------------------------");  
    10 for(Iterator it = map.keySet().iterator();it.hasNext();){  
    11     Object key = it.next();  
    12     Object value = map.get(key);  
    13     System.out.println(key+"="+value);  
    14 }  
    15 System.out.println("-----------------------------------------");  
    16 System.out.println(map.values());  
    17   
    18 输出为:  
    19 key3=value3  
    20 key2=value2  
    21 key1=value1  
    22 -----------------------------------------  
    23 key3=value3  
    24 key2=value2  
    25 key1=value1  
    26 -----------------------------------------  
    27 [value3, value2, value1]  

                                                          原文地址: http://frank1234.iteye.com/blog/2162490

  • 相关阅读:
    Java实现 LeetCode 50 Pow(x,n)
    Java实现 LeetCode 50 Pow(x,n)
    Java实现 LeetCode 49 字母异位词分组
    Java实现 LeetCode 49 字母异位词分组
    Java实现 LeetCode 49 字母异位词分组
    Java实现 LeetCode 48 旋转图像
    Java实现 LeetCode 48 旋转图像
    Java实现 LeetCode 48 旋转图像
    Java实现 LeetCode 47 全排列 II(二)
    Java实现 LeetCode 47 全排列 II(二)
  • 原文地址:https://www.cnblogs.com/jiqianqian/p/6561201.html
Copyright © 2011-2022 走看看