HashMap的数据结构是什么?如何实现的。和HashTable,ConcurrentHashMap的区别

HashMap和HashTable的区别一种比较简单的回答是:

(1)HashMap是非线程安全的,HashTable是线程安全的。

(2)HashMap的键和值都允许有null存在,而HashTable则都不行。

(3)因为线程安全、哈希效率的问题,HashMap效率比HashTable的要高。

JAVA的数据结构存储:

1、数组:查询快,增删慢;连续空间寻址快。

2、链表:增删快,查询慢;空间不连续,增删只需修改指针。

Hash表结构:

从下图中,我们可以发现哈希表是由数组+链表组成的,一个长度为16的数组中,每个元素存储的是一个链表的头结点,通过功能类似于hash(key.hashCode())%len的操作,获得要添加的元素所要存放的的数组位置。

HashMap的哈希算法实际操作是通过位运算,比取模运算效率更高,同样能达到使其分布均匀的目的,后面会介绍。

键值对所存放的数据结构其实是HashMap中定义的一个Entity内部类,数组来实现的,属性有key、value和指向下一个Entity的next。

《HashMap的数据结构是什么?如何实现的。和HashTable,ConcurrentHashMap的区别》

HashMap的实现重点需要注意的在两个方面,一个是链表结构,一个是table的resize()扩容;

HashMap和HashTable的对比:

HashTable和HashMap采用相同的存储机制,二者的实现基本一致,不同的是:

(1)HashMap是非线程安全的,HashTable是线程安全的,内部的方法基本都经过synchronized修饰。

(2)因为同步、哈希性能等原因,性能肯定是HashMap更佳,因此HashTable已被淘汰。

(3) HashMap允许有null值的存在,而在HashTable中put进的键值只要有一个null,直接抛出NullPointerException。

(4)HashMap默认初始化数组的大小为16,HashTable为11。前者扩容时乘2,使用位运算取得哈希,效率高于取模。而后者为乘2加1,都是素数和奇数,这样取模哈希结果更均匀。

言归正传,看下两种集合的hash算法。看源码也不难理解。

//HashMap的散列函数,这里传入参数为键值对的key  

static final int hash(Object key) {  

int h;  

return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);  

}   

//返回hash值的索引,h & (length-1)操作等价于 hash % length操作, 但&操作性能更优  

static int indexFor(int h, int length) {  

// length must be a non-zero power of 2  

return h & (length-1);  

}  

//HashTable的散列函数直接在put方法里实现了  

int hash = key.hashCode();  

int index = (hash & 0x7FFFFFFF) % tab.length;

ConcurrentHashMap原理:

《HashMap的数据结构是什么?如何实现的。和HashTable,ConcurrentHashMap的区别》

ConcurrentHashMap引入了分割(Segment),上面代码中的最后一行其实就可以理解为把一个大的Map拆分成N个小的HashTable,在put方法中,会根据hash(paramK.hashCode())来决定具体存放进哪个Segment,如果查看Segment的put操作,我们会发现内部使用的同步机制是基于lock操作的,这样就可以对Map的一部分(Segment)进行上锁,这样影响的只是将要放入同一个Segment的元素的put操作,保证同步的时候,锁住的不是整个Map(HashTable就是这么做的),相对于HashTable提高了多线程环境下的性能,因此HashTable已经被淘汰了。

HashMap和ConCurrentHashMap的对比

最后对这俩兄弟做个区别总结吧:

(1)经过4.2的分析,我们知道ConcurrentHashMap对整个桶数组进行了分割分段(Segment),然后在每一个分段上都用lock锁进行保护,相对于HashTable的syn关键字锁的粒度更精细了一些,并发性能更好,而HashMap没有锁机制,不是线程安全的。

(2)HashMap的键值对允许有null,但是ConCurrentHashMap都不允许。

所以 HashMap 与 ConcurrentHashMap 区别为 CHM部分方法具备原子性,CHM原子性 基于CAS实现,CAS是乐观锁实现同步的一种方式

《HashMap的数据结构是什么?如何实现的。和HashTable,ConcurrentHashMap的区别》

使用concurrentHashMap随便操作是原子性,但是放一起可能违反happen-before规则,chm是弱一致,要想强一致必须使用全局锁

《HashMap的数据结构是什么?如何实现的。和HashTable,ConcurrentHashMap的区别》

建议使用以下代码

    Map map = Collections.synchronizedMap(newHashMap());

    String getString(String name) { 

        synchronized(map){//可保证该同步块内的所有代码对map是一个原子操作。

            String x = map.get(name); 

            if(x == null) { 

                x = newString(); 

                map.put(name, x); 

            } 

            returnx;

        } 

    } 

    原文作者:小七_8d72
    原文地址: https://www.jianshu.com/p/45afd8c2fa3a
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞