hashMap,hashtable和ConcurrentMap的区别

1.HashTable

(1)继承于Dictionary,实现了Map,Cloneable,Java.io.Serializable接口

(2)底层数组+链表实现,无论key还是value都不能为null,同步线程安全,实现线程安全的方式是锁住整个hashtable,效率低,concurrentMap做了相关优化。

(3)初始容量为11 扩容:newsize=oldsize*2+1

(4)两个参数影响性能:初始容量,加载因子(默认0.75)

(5)计算index方法:index=(hash&0x7FFFFFFF)%tab.length 

hashtable的底层源码: 

1)HashTable继承于Dictionary,实现了Map,Cloneable,Java.io.Serializable接口
public class Hashtable<K,V>
    extends Dictionary<K,V>
    implements Map<K,V>, Cloneable, java.io.Serializable {}
2)HashTable两个参数影响性能:初始容量,加载因子(默认0.75)
 public Hashtable(int initialCapacity) {
        this(initialCapacity, 0.75f);
    }
3)HashTable初始容量为11 扩容:newsize=oldsize*2+1
// 调整Hashtable的长度,将长度变成原来的(2倍+1)
    // (01) 将“旧的Entry数组”赋值给一个临时变量。
    // (02) 创建一个“新的Entry数组”,并赋值给“旧的Entry数组”
    // (03) 将“Hashtable”中的全部元素依次添加到“新的Entry数组”中
    protected void rehash() {
        int oldCapacity = table.length;
        Hashtable.Entry<?,?>[] oldMap = table;

        // overflow-conscious code
        int newCapacity = (oldCapacity << 1) + 1;
        if (newCapacity - MAX_ARRAY_SIZE > 0) {
            if (oldCapacity == MAX_ARRAY_SIZE)
                // Keep running with MAX_ARRAY_SIZE buckets
                return;
            newCapacity = MAX_ARRAY_SIZE;
        }
        Hashtable.Entry<?,?>[] newMap = new Hashtable.Entry<?,?>[newCapacity];

        modCount++;
        threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
        table = newMap;

        for (int i = oldCapacity ; i-- > 0 ;) {
            for (Hashtable.Entry<K,V> old = (Hashtable.Entry<K,V>)oldMap[i]; old != null ; ) {
                Hashtable.Entry<K,V> e = old;
                old = old.next;

                int index = (e.hash & 0x7FFFFFFF) % newCapacity;
                e.next = (Hashtable.Entry<K,V>)newMap[index];
                newMap[index] = e;
            }
        }
    }
(4) 计算index方法:index=(hash&0x7FFFFFFF)%tab.length
 public synchronized V put(K key, V value) {
        // Make sure the value is not null
        if (value == null) {
            throw new NullPointerException();
        }

        // Makes sure the key is not already in the hashtable.
        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        Entry<K,V> entry = (Entry<K,V>)tab[index];
        for(; entry != null ; entry = entry.next) {
            if ((entry.hash == hash) && entry.key.equals(key)) {
                V old = entry.value;
                entry.value = value;
                return old;
            }
        }

        addEntry(hash, key, value, index);
        return null;
    }

2.HashMap

(1)底层数组+链表+红黑树实现,可以存在null键和null值,线程不安全

(2)初始size为16 扩容:newsize=oldsize*2,size一定为2的n次幂

(3)扩容针对整个map,每次和扩容时,原数组的元素重新计算存放位置,并重新插入。

(4)插入元素后才判断是否需要扩容,若再无插入,无效扩容

(5)加载因子:默认0.75

(6)计算index方法:index=hash&(tab.length-1)

(7)空间换时间:如果希望加快Key查找的时间,还可以进一步降低加载因子,加大初始大小,以降低哈希冲突的概率。

 注意:

(1)JDK1.8以后,hashmap的底层结构,由原来单纯的数组+链表,改为链表长度为8时,开始由链表转变为红黑树

(2)至于为什么要将链表在长度为8时转变为红黑树呢?   

原因是链表的时间复杂度是O(n),红黑树的时间复杂度O(logn),很显然,红黑树的复杂度是优于链表的

因为树节点所占空间是普通节点的两倍,所以只有当节点足够多的时候,才会使用树节点。也就是说,节点少的时候,尽管时间复杂度上,红黑树比链表好

一点,但是红黑树所占空间比较大,综合考虑,认为只能在节点太多的时候,红黑树占空间大这一劣势不太明显的时候,才会舍弃链表,使用红黑树。

也就是大部分情况下,hashmap还是使用的链表,如果是理想的均匀分布,节点数不到8,hashmap就自动扩容了

3.ConcurrentMap  

(1)底层采用分段的数组+链表实现,线程安全。

(2)通过把整个map分为N个Segment,可以提供相同的线程安全效率提升N倍,默认16倍。

(3)读操作不加锁,修改操作加分段锁,允许多个修改操作并行发生。

(4)扩容:段内扩容(段内元素超过该段对应的Entry数组的0.75,触发扩容,而不是整段扩容),插入前检测是否需要扩容,避免无效扩容。

(有些方法需要跨段,比如size()和containsValue(),它们可能需要锁定整个表而而不仅仅是某个段,这需要按顺序锁定所有段,操作完毕后,又按顺序释放所有段的锁)。

(5)存储结构中ConcurrentHashMap比HashMap多出了一个类Segment,而Segment是一个可重入锁。

(6)ConcurrentHashMap是使用了锁分段技术来保证线程安全的。

锁分段技术是什么呢?

      锁分段技术:首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。

ConcurrentHashMap和Hashtable的不同点:

(1)ConcurrentHashMap提供了与Hashtable和SynchronizedMap不同的锁机制。

(2)Hashtable中采用的锁机制是一次锁住整个hash表,从而在同一时刻只能由一个线程对其进行操作;而ConcurrentHashMap中则是一次锁住一个桶。

(3)ConcurrentHashMap默认将hash表分为16个桶,诸如get、put、remove等常用操作只锁住当前需要用到的桶。这样,原来只能一个线程进入,现在却能同时有

16个写线程执行,并发性能的提升是显而易见的。

 
 
posted @ 2020-05-31 20:07  南鸽  阅读(242)  评论(0编辑  收藏  举报