jdk1.8 HashMap源码分析(put函数)

来源:互联网 发布:熟悉掌握办公软件 编辑:程序博客网 时间:2024/05/29 11:00
/**   将一个key和一个value在这个map中关联起来。 * Associates the specified value with the specified key in this map.   如果这个map中已经存在该key则会被替换。 * If the map previously contained a mapping for the key, the old * value is replaced. * * @param key key with which the specified value is to be associated * @param value value to be associated with the specified key * @return the previous value associated with <tt>key</tt>, or *         <tt>null</tt> if there was no mapping for <tt>key</tt>. *         (A <tt>null</tt> return can also indicate that the map *         previously associated <tt>null</tt> with <tt>key</tt>.) */public V put(K key, V value) {    //会对key进行再一次的hash    return putVal(hash(key), key, value, false, true);}/** * Computes key.hashCode() and spreads (XORs) higher bits of hash * to lower.  Because the table uses power-of-two masking, sets of * hashes that vary only in bits above the current mask will * always collide. (Among known examples are sets of Float keys * holding consecutive whole numbers in small tables.)  So we * apply a transform that spreads the impact of higher bits * downward. There is a tradeoff between speed, utility, and * quality of bit-spreading. Because many common sets of hashes * are already reasonably distributed (so don't benefit from * spreading), and because we use trees to handle large sets of * collisions in bins, we just XOR some shifted bits in the * cheapest possible way to reduce systematic lossage, as well as * to incorporate impact of the highest bits that would otherwise * never be used in index calculations because of table bounds. */static final int hash(Object key) {    int h;    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}/** * Implements Map.put and related methods * * @param hash hash for key * @param key the key * @param value the value to put * @param onlyIfAbsent if true, don't change existing value * @param evict if false, the table is in creation mode. * @return previous value, or null if none */final V putVal(int hash, K key, V value, boolean onlyIfAbsent,               boolean evict) {    Node<K,V>[] tab; Node<K,V> p; int n, i;    //如果是第一次put,那么将会进行resize,n则为新容量,    //如果不是第一次put,那么n = tab.length将得到原始的容量    if ((tab = table) == null || (n = tab.length) == 0)        n = (tab = resize()).length;    //i = (n - 1) & hash将会得到该元素在tab数组中的位置并赋值给i    //此时p的值为tab[i]的元素    //如果tab[i]没有值,那么直接放置    if ((p = tab[i = (n - 1) & hash]) == null)        tab[i] = newNode(hash, key, value, null);    else { //如果存在值,那么就要考虑是不是hash冲突了        Node<K,V> e; K k;        /*            如果两个冲突的元素hash值相等,并且key也一样,            将p赋值给e,后面会将value赋值给e.value,            这样就完成了相同key则使用新值覆盖原来的值,            这不是hash冲突,是因为用户put了多次相同的key        */        if (p.hash == hash &&            ((k = p.key) == key || (key != null && key.equals(k))))            e = p;        //后面的判断情况:        //1、put的key和map中的key是不相同的,需要处理hash冲突        //2、key存在于p节点的链表中,那么将其value替换为上面传入的参数value        else if (p instanceof TreeNode)            //如果已经转化为红黑树了            //(hash冲突比较严重就会转化为红黑树),那么值直接添加节点            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);                    //如果此时这个bin中的元素超过了TREEIFY_THRESHOLD(默认为8)                    //那么需要转化为红黑树                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st                        treeifyBin(tab, hash);                    break;                }                //如果key存在于p节点的链表中,                //那么将其value替换为上面传入的参数value                if (e.hash == hash &&                    ((k = e.key) == key || (key != null && key.equals(k))))                    break;                p = e;            }        }        //这里就是替换value的工作        if (e != null) { // existing mapping for key            V oldValue = e.value;            if (!onlyIfAbsent || oldValue == null)                e.value = value;            afterNodeAccess(e);            return oldValue;        }    }    ++modCount;    //增加元素结束之后,size加一,并判断是否超过了需要扩容的临界值    //这里是HashMap一个小的瑕疵,总是在添加完元素之后再判断扩容,    //如果扩容完之后再也不添加元素了,那么就会造成相当大的空间浪费    if (++size > threshold)        resize();    afterNodeInsertion(evict);    return null;}