HashMap深入理解

来源:互联网 发布:淘宝助理怎么复制宝贝 编辑:程序博客网 时间:2024/06/05 21:01

HashMap深入理解

HashMap基于哈希表的 Map 接口的实现。允许使用 null 值和 null 键。不保证映射的顺序,特别是它不保证该顺序恒久不变。
HashMap的底层主要是基于数组和链表来实现的,它之所以有相当快的查询速度主要是因为它是通过计算散列码来决定存储的位置。HashMap中主要是通过key的hashCode来计算hash值的,只要hashCode相同,计算出来的hash值就一样。如果存储的对象对多了,就有可能不同的对象所算出来的hash值是相同的,这就出现了hash冲突,HashMap底层是通过链表来解决hash冲突的。
我们使用put(key, value)存储对象到HashMap中,使用get(key)从HashMap中获取对象。当我们给put()方法传递键和值时,我们先对键调用hashCode()方法,返回的hashCode用于找到应保存在数组中的索引,计算方法是h&(length-1),这样实现了均匀的散列,而且效率要高很多,找到的bucket位置用来储存Entry对象
当hashcode相同,所以它们的bucket位置相同,‘碰撞’会发生。因为HashMap使用链表存储对象,这个Entry(包含有键值对的Map.Entry对象)会存储在链表中
当我们调用get()方法,HashMap会使用键对象的hashcode找到bucket位置,然后会调用keys.equals()方法遍历链表找到链表中正确的节点,最终找到要找的值对象。
如果HashMap的大小超过了数组大小*loadFactor负载因子(load factor)定义的容量,将会创建原来HashMap大小的两倍的bucket数组,来重新调整map的大小,并将原来的对象放入新的bucket数组中。这个过程叫作rehashing,因为它调用hash方法找到新的bucket位置
HashMap不是线程安全的,首先如果多个线程同时使用put方法添加元素,而且假设正好存在两个put的key发生了碰撞(hash值一样),那么根据HashMap的实现,这两个key会添加到数组的同一个位置,这样最终就会发生其中一个线程的put的数据被覆盖。第二就是如果多个线程同时检测到元素个数超过数组大小*loadFactor,这样就会发生多个线程同时对Node数组进行扩容,会引起死循环。
如果想要线程安全的HashMap,可以通过Collections类的静态方法synchronizedMap获得线程安全的HashMap。
HashTable源码中是使用synchronized来保证线程安全的,当一个线程访问HashTable的同步方法时,其他线程如果也要访问同步方法,会被阻塞住,如当一个线程使用put方法时,另一个线程不但不可以使用put方法,连get方法都不可以,效率很低。
ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术。它使用了多个锁来控制对hash表的不同部分进行的修改。ConcurrentHashMap内部使用段(Segment)来表示这些不同的部分,每个段其实就是一个小的hash table,它们有自己的锁。只要多个修改操作发生在不同的段上,它们就可以并发进行。
有些方法需要跨段,比如size()和containsValue(),它们可能需要锁定整个表而而不仅仅是某个段,这需要按顺序锁定所有段,操作完毕后,又按顺序释放所有段的锁。

原创粉丝点击