HashMap存储过程分析

1:hashMap的实现原理

  1.1 hsahMap在jdk1.8的时候做了一个改进,在jdk1.7的时候hahsMap是基于哈希表(数组+链表)实现的,在1.8之后又加了一个叫二叉树的一个实现,在二叉树里边用了一个叫红黑树,红黑树是二叉树里边的一种,它主要是用来保证树的平衡性,因为二叉树有的时候节点太长,有的时候节点太短,太长的话就不便于遍历,所以说,红黑树的这个目的是来标记(红,黑)的这个算法,主要是保证这棵树的左右两端的平衡,这样遍历的话平均的这个性能就会很好,接下来看一下源码

2:源码分析

    2.1首先我们看一下他的默认构造方法   

    public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }

  我们把DEFAULT_LOAD_FACTOR点开,我们可以看到,他默认赋了一个0.75的一个值,这个值是加载因子,还有他的初始化容量数组是16,指的是数组的百分之75都被存了值,就表示这个空间的数据已经存满了,如果满的话这个数组就有可能会去重新创建,就是说这个数组要重新扩充了,这个0.75就是标准的红线,他的内部回去计算这个0.75,他是这样计算的,初始化容量数组默认是16,因子是百分之75,当数组到12的时候就已经存满了,当然了它还提供了别的构造方法,在初始化的时候我们也可以去修改这个值

    /**
     * The default initial capacity - MUST be a power of two. 初始化数组值默认为16
     */
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

        /**
     * The load factor used when none specified in constructor. 加载因子默认为0.75
     */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

   2.2:如何把对象存储到哈希表中,他的存储方法叫PUT,PUT方法的时候我们传入一个key,一个value,调用的是一个putvalue的方法,putvalue里边还调用了一个hash(key)的一个方法,是通过key来计算 hash的值

    

    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

  他是先拿到Object的一个对象去求(key.hashCode)对象的值,h就拿到了Object key值,h右移16位在和hashCode疑惑最后得出一个整数,这个整数代表的是这个hash 的值

    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

  得到整个整数以后,他就传到了putval这个方法,我们捡重要的去看一下,首先看一下,下边的一个table,这个table就是Node的这个数组,table其实就是node的一个对象数组,node就是hash里边的一个静态类,里边hash就是我们求的值,next是一个链表,然后把key和value,他把key和value存在了Node对象里边了

transient Node<K,V>[] table;
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;

我们可以看到他判断了一下,如果table=null或table=0那么table就等于resize()方法,里边进行了扩容还有一些判断如果他们都等于空,他会把默认的,他会把默认的数组长度16赋值给他,那么n就等于16,我们看一下他存储的位置,(n-1)=15 ,15&hash赋值给i,tab接收作为数组的下标进行存储到Node,Node是一个链表,存储的对象很多,如果又一个对象存储进来了求的值跟他一样就是形成一个链表,jdk1.8之前就是用这个链表处理的,但是1.8之后,出现了红黑树,来限制这种链表,他这个判读是这样写的(binCount>=TREEIFY_THRESHOLD-1)那么(THREEIFY_THRESHOLD)就等于7,下一个数据就是等于8,当大于8的时候就转换成了红黑树

 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
                            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;
    }

3.存储过程

 3.1 刚才进行源码分析,我想大家也了解到了他的一个存储过程,在这里我在总结一下:

    把key通过通过hash()方法计算hash的值,然后这个hash的值对数组进行求余数(默认16),来决定KEY对象在数组中的位置,当这个对象有多个值时,以链表方式进行存储,jdk8以后当链表长度大于8时,链表将转换成红黑树结构存储,这样的目的,是为了取值更快,存储的数量越多性能的表现就越明显。

猜你喜欢

转载自www.cnblogs.com/zhaoyuwei/p/9286665.html