HASHMAP底层原理分析


概述

本文主要介绍hashmap的原理和说明

hashMap 部分源码说明

put 方法

    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                       boolean evict) {
            Node<K,V>[] tab; Node<K,V> p; int n, i;
            if ((tab = table) == null || (n = tab.length) == 0)
                n = (tab = resize()).length;
            if ((p = tab[i = (n - 1) & hash]) == null)
                tab[i] = newNode(hash, key, value, null);
            else {
                Node<K,V> e; K k;
                if (p.hash == hash &&
                    ((k = p.key) == key || (key != null && key.equals(k))))
                    e = p;
                else if (p instanceof TreeNode)
                    e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
                else {
                    for (int binCount = 0; ; ++binCount) {
                        if ((e = p.next) == null) {
                            p.next = newNode(hash, key, value, null);
                            if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st this will change linkList to red-black tree
                                treeifyBin(tab, hash);
                            break;
                        }
                        if (e.hash == hash &&
                            ((k = e.key) == key || (key != null && key.equals(k))))
                            break;
                        p = e;
                    }
                }
                if (e != null) { // existing mapping for key
                    V oldValue = e.value;
                    if (!onlyIfAbsent || oldValue == null)
                        e.value = value;
                    afterNodeAccess(e);
                    return oldValue;
                }
            }
            ++modCount;
            if (++size > threshold)
                resize();
            afterNodeInsertion(evict);
            return null;
        }

转为TreeNode过程

        /**
         * Replaces all linked nodes in bin at index for given hash unless
         * table is too small, in which case resizes instead.
         */
        final void treeifyBin(Node<K,V>[] tab, int hash) {
            int n, index; Node<K,V> e;
            if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
                resize();
            else if ((e = tab[index = (n - 1) & hash]) != null) {
                TreeNode<K,V> hd = null, tl = null;
                do {
                    TreeNode<K,V> p = replacementTreeNode(e, null);
                    if (tl == null)
                        hd = p;
                    else {
                        p.prev = tl;
                        tl.next = p;
                    }
                    tl = p;
                } while ((e = e.next) != null);
                if ((tab[index] = hd) != null)
                    hd.treeify(tab);
            }
        }

> TreeNode对象

```java

        /**
         * Entry for Tree bins. Extends LinkedHashMap.Entry (which in turn
         * extends Node) so can be used as extension of either regular or
         * linked node.
         */
        static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
            TreeNode<K,V> parent;  // red-black tree links
            TreeNode<K,V> left;
            TreeNode<K,V> right;
            TreeNode<K,V> prev;    // needed to unlink next upon deletion
            boolean red;
            TreeNode(int hash, K key, V val, Node<K,V> next) {
                super(hash, key, val, next);
            }

            /**
             * Returns root of tree containing this node.
             */
            final TreeNode<K,V> root() {
                for (TreeNode<K,V> r = this, p;;) {
                    if ((p = r.parent) == null)
                        return r;
                        r = p;
                }
            }
        }

可以看出在链表中容量大于等于8后,链表转为了红黑树以提高hashmap性能,此JDK为1.8。

HashMap和Hashtable的区别

Hashtable是基于陈旧的Dictionary类的,HashMap是java 1.2引进的Map接口的一个现实。

Hashtable是同步的,这个类中的一些方法保证了Hashtable中的对象是线程安全的

HashMap则是异步的,因此HashMap中的对象并不是线程安全的,因为同步的要求会影响执行的效率,所以如果你不需要线程安全的结合那么使用HashMap是一个很好的选择,这样可以避免由于同步带来的不必要的性能开销,从而提高效率,我们一般所编写的程序都是异步的,但如果是服务器端的代码除外。

HashMap可以将空值作为一个表的条目的key或value, Hashtable不能放入空值(null).

HashMap说明

HashMap 的底层实现是数组,这大大加快了其速率,hashMap的快来源于hash表,而hash表的主干就是数组.

hash表为何快呢,hashMap在查找和增加元素时会先通过KEY去hash表中找到其引用的实际存储地址,从而操作该数据,其数据其实是一个ENTRY对象.

hash冲突,当对某个元素通过hash计算得到的的存储地址,在插入时发现此地址已经被其他元素占用,则产生hash冲突.

哈希冲突的解决方案有多种:开放定址法(发生冲突,继续寻找下一块未被占用的存储地址),再散列函数法,链地址法,而HashMap即是采用了链地址法,也就是数组+链表的方式(JDK7之前),JDK8采用的是数组+链表/红黑树的方式。当同一个hash值的节点数大于8时,将不再以单链表的形式存储了,会被调整成一颗红黑树,这就是JDK7与JDK8中HashMap实现的最大区别。

简单来说,HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的,如果定位到的数组位置不含链表(当前entry的next指向null),那么对于查找,添加等操作很快,仅需一次寻址即可;

如果定位到的数组包含链表,对于添加操作,其时间复杂度为O(n),首先遍历链表,存在即覆盖,否则新增;对于查找操作来讲,仍需遍历链表,然后通过key对象(比如student)的equals方法逐一比对查找。so,性能考虑,HashMap中的链表出现越少,性能才会越好。

hashMap threshold 指当前map容量达到threshold%时对hashMap进行扩容,默认为0.75

如何减少hash冲突,使用final对象作为key,并且从写equal方法,hashcode方法,提高hashcode性能。

两个键的hashcode相同,如何获取值对象

如果两个键的hashcode相同,即找到bucket位置之后,我们通过key.equals()找到链表LinkedList中正确的节点,最终找到要找的值对象。

object.equal()方法只判断两个引用变量是否为同一个对象,无法真的判断对象在逻辑上是否相等.

所以尽量使用不可变的、声明作final的对象,并且采用合适的equals()和hashCode()方法,减少碰撞的发生,提高效率。不可变性使得能够缓存不同键的hashcode,这将提高整个获取对象的速度,使用String,Interger这样的wrapper类作为键是非常好的选择。

CocurrentHashMap为什么线程安全

CocurrentHashMap使用了分段锁技术:ConcurrentHashMap的主干是个Segment数组,ConcurrentHashMap相比HashTable而言解决的问题就是的它不是锁全部数据,而是锁一部分数据,这样多个线程访问的时候就不会出现竞争关系,不需要排队等待了。

ConcurrentHashMap是分段并发分段进行读取数据的,Segment 里面有一个Count字段,用来表示当前Segment中元素的个数它的类型是volatile变量。所有的操作到最后都会在最后一步更新count这个变量,由于volatile变量 happer-before的特性使得get方法能够几乎准确的获取最新的结构更新。

参考

HashMap实现原理

一盏灯, 一片昏黄; 一简书, 一杯淡茶。 守着那一份淡定, 品读属于自己的寂寞。 保持淡定, 才能欣赏到最美丽的风景! 保持淡定, 人生从此不再寂寞。



   Reprint policy


《HASHMAP底层原理分析》 by jackromer is licensed under a Creative Commons Attribution 4.0 International License
 Previous
lambda处理集合 lambda处理集合
java8 lambda表达式利用流处理各种集合 处理listpublic class Jdk8Main { public static void main(String[] args) { List<P
2019-08-27
Next 
JAVA-MONGODB简单使用 JAVA-MONGODB简单使用
概述 本文主要介绍MongoDB和java简单使用MongoDB,MongoDB作为流行的 NOT ONLY SQL 数据库,和关系型数据库mysql,oracle等的ACID不同,非关系型数据库为分布式系统和大数据的应用提供了良好的支持
2019-08-27
  目录