数据结构Map-----HashMap源码解析

Map另一个基本的集合接口

接口中定义的所有方法:

public static interface Entry<K,V> {        
        public boolean equals(Object object);       
        public K getKey();
        public V getValue();      
        public int hashCode();       
        public V setValue(V object);
};
    public void clear();
    public boolean containsKey(Object key);  
    public boolean containsValue(Object value);  
    public Set<Map.Entry<K,V>> entrySet();   
    public boolean equals(Object object);    
    public V get(Object key);   
    public int hashCode();    
    public boolean isEmpty();    
    public Set<K> keySet();    
    public V put(K key, V value);    
    public void putAll(Map<? extends K,? extends V> map);    
    public V remove(Object key);    
    public int size();   
    public Collection<V> values();

HashMap:重要的成员变量以及构造方法

public class HashMap<K, V> extends AbstractMap<K, V> implements Cloneable, Serializable {

    /**
     * 常量,最小容量capacity(这个单词不陌生吧)
     */
    private static final int MINIMUM_CAPACITY = 4;

    /**
     * 常量,最大容量,移位运算,2的30次方-----1073741824(这么大一个值)
     * int的取值范围想必大家都记得(-2的31次方 到 2的31次方-1),这里取2的30次方,是一个很有深意的值
     * 简单透露一下就是HashMap扩容后数组长度都保证是2的n次方,这个后面遇到我会说
     */
    private static final int MAXIMUM_CAPACITY = 1 << 30;

    /**
     * Entry是一个接口,HashMapEntry是它的实现类,后面有写,MINIMUM_CAPACITY >>> 1 就是2,也就
     * 说一开始创建了一个长度为2的HashMapEntry[]数组对象并赋值给一个Entry[]数组实例EMPTY_TABLE。
     * 
     */
    private static final Entry[] EMPTY_TABLE
            = new HashMapEntry[MINIMUM_CAPACITY >>> 1];

    /**
     *默认负载因子,一个很重要的概念,和扩容相关 .75F的意思大概数组长度到达了总长度的百分之七十五也就是
     *四分之三就开始进行扩容了,后面我们会描述
     */
    static final float DEFAULT_LOAD_FACTOR = .75F;

    /**
     * HashMap集合其实就是保存了HashMapEntry<K, V>对象,这里定义一个table数组实例。
     */
    transient HashMapEntry<K, V>[] table;

    /**
     * HashMap集合允许Key为null,entryForNullKey就是用来存放这个的
     */
    transient HashMapEntry<K, V> entryForNullKey;

    /**
     * 集合数据元素数量
     */
    transient int size;

    /**
     * 计数器,相信看过我前面文章的都见过多次了,但是估计做用是什么都不是很了解,这是一种fail-fast机制
     * 当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件,比如你在for循环遍历集合的过
     * 程中,其他线程在对集合进行添加和删除操作,这样的话就会发生异常,很多函数中都要先进行计数器的比较
     * 从而保证不出现异常,一旦发现异常就抛出ConcurrentModificationException这个异常
     */
    transient int modCount;

    /**
     *阈(yu四声)值 哈哈,也是和扩容相关的,很重要的一个属性
     */
    private transient int threshold;

    // Views - lazily initialized
    //key集合,使用的是Set 这也就是键唯一原因
    private transient Set<K> keySet;
    private transient Set<Entry<K, V>> entrySet;
    //values集合,使用的是Collection 所以值是可以重复相同的
    private transient Collection<V> values;

    /**
     * 无参构造,将EMPTY_TABLE赋值给了table ,这两个属性上文都可以找到
     * threshold 阈值默认值-1(因为HashMap使用的是懒加载,为什么到put方法的时候我会说)
     * 还有这里我们可以发现table的长度是2,也就是2的1次方,还记得我说过关于容量的事吗
     */
    @SuppressWarnings("unchecked")
    public HashMap() {
        table = (HashMapEntry<K, V>[]) EMPTY_TABLE;
        threshold = -1; // Forces first put invocation to replace EMPTY_TABLE
    }

    /**
     *这是一个参数的构造函数,传入一个int值作为容量,你可以发现无论你传入的值是多少它都会把你传入的值变
     *成一个2的n次方的值,这个n的大小有一定的计算方式。
     */
    public HashMap(int capacity) {
        if (capacity < 0) {
            throw new IllegalArgumentException("Capacity: " + capacity);
        }
        //传0,就相当于使用无参构造
        if (capacity == 0) {
            @SuppressWarnings("unchecked")
            HashMapEntry<K, V>[] tab = (HashMapEntry<K, V>[]) EMPTY_TABLE;
            table = tab;
            threshold = -1; // Forces first put() to replace EMPTY_TABLE
            return;
        }
        //传小于MINIMUM_CAPACITY 就当你传的是MINIMUM_CAPACITY 2的2次方
        if (capacity < MINIMUM_CAPACITY) {
            capacity = MINIMUM_CAPACITY;
        //传大于MAXIMUM_CAPACITY 就当你传的是MAXIMUM_CAPACITY 2的30次方
        } else if (capacity > MAXIMUM_CAPACITY) {
            capacity = MAXIMUM_CAPACITY;
        /**
         *通过Collections.roundUpToPowerOfTwo(capacity);方法计算capacity ,放心还是一个2的
         *n次方的值,气不气?该方法表示的就是找到一个比capacity大的2的n次方的最小值,可能不是太明白,
         *如果capacity是3就返回4,因为2的1次方比3小,不合适,所以是2的2次方,同样如果  
         *capacity是9则返回16,因为2的3次方是8比9小,所以返回2的4次方16,- -!好气哦
         */
        } else {
            capacity = Collections.roundUpToPowerOfTwo(capacity);
        }
        //构建table
        makeTable(capacity);
    }

    private HashMapEntry<K, V>[] makeTable(int newCapacity) {
        @SuppressWarnings("unchecked") HashMapEntry<K, V>[] newTable
                = (HashMapEntry<K, V>[]) new HashMapEntry[newCapacity];
        table = newTable;
        /**
         *这里给threshold阈值弄一个新值,假如newCapacity 是16 16>>1就是8 加上 16>>2就是4
         *8+4=12 12是16的0.75也就是四分之三(只有2的n次方才可以完美的演绎四分之三)
         */
        threshold = (newCapacity >> 1) + (newCapacity >> 2); // 3/4 capacity
        return newTable;
    }

    //HashMapEntry HashMap就是存储这个类的对象的
    static class HashMapEntry<K, V> implements Entry<K, V> {
        final K key;
        V value;
        final int hash;
        HashMapEntry<K, V> next;

        HashMapEntry(K key, V value, int hash, HashMapEntry<K, V> next) {
            this.key = key;
            this.value = value;
            this.hash = hash;
            this.next = next;//看到next是不是想起了链表,没错hashMap就是一个散列链表
        }

        public final K getKey() {
            return key;
        }

        public final V getValue() {
            return value;
        }

        public final V setValue(V value) {
            V oldValue = this.value;
            this.value = value;
            return oldValue;
        }

        @Override public final boolean equals(Object o) {
            if (!(o instanceof Entry)) {
                return false;
            }
            Entry<?, ?> e = (Entry<?, ?>) o;
            return Objects.equal(e.getKey(), key)
                    && Objects.equal(e.getValue(), value);
        }

        @Override public final int hashCode() {
            return (key == null ? 0 : key.hashCode()) ^
                    (value == null ? 0 : value.hashCode());
        }

        @Override public final String toString() {
            return key + "=" + value;
        }
    }
}
  • 增加
@Override 
public V put(K key, V value) {
    //如果key为null,这里是显示出了HashMap的key运行为null值
    if (key == null) {
        return putValueForNullKey(value);
    }
    //对key进行哈希算法(进行了两次哦,看名字就知道了)拿到key的hashCode然后调用secondaryHash再玩一遍
    int hash = Collections.secondaryHash(key);
    //这种出于安全性的内部变量不多说了
    HashMapEntry<K, V>[] tab = table;
    /**
     *得出index值,是通过上面的hash值和HashMapEntry[]数组长度-1,进行与运算(位运算),最后index
     *值肯定是在0~tab.length - 1之间
     */
    int index = hash & (tab.length - 1);
    //链表循环遍历
    for (HashMapEntry<K, V> e = tab[index]; e != null; e = e.next) {
        //哈希值相同key也相同那么就是替换喽
        if (e.hash == hash && key.equals(e.key)) {
            //★用来给子类重写使用的★
            preModify(e);
            V oldValue = e.value;
            e.value = value;
            return oldValue;
        }
    }

    // No entry for (non-null) key is present; create one
    modCount++;
    //扩容啊!之前说过HashMap懒加载,因为使用无参构造threshold默认值是-1啊,那么一进行put肯定if成立啊
    if (size++ > threshold) {
        //doubleCapacity() 看名字就知道扩容了多少
        tab = doubleCapacity();
        index = hash & (tab.length - 1);
    }
    //★添加新元素★
    addNewEntry(key, value, hash, index);
    return null;
}

/**
 * Give LinkedHashMap a chance to take action when we modify an existing
 * entry.
 *
 * @param e the entry we're about to modify.
 */
void preModify(HashMapEntry<K, V> e) { }


---------------哈希部分-------------------

public int hashCode() {
    int lockWord = shadow$_monitor_;
    final int lockWordStateMask = 0xC0000000;  // Top 2 bits.
    final int lockWordStateHash = 0x80000000;  // Top 2 bits are value 2 (kStateHash).
    final int lockWordHashMask = 0x0FFFFFFF;  // Low 28 bits.
    if ((lockWord & lockWordStateMask) == lockWordStateHash) {
        return lockWord & lockWordHashMask;
    }
    return System.identityHashCode(this);
}

public static int secondaryHash(Object key) {
    return secondaryHash(key.hashCode());
}

private static int secondaryHash(int h) {
    // Spread bits to regularize both segment and index locations,
    // using variant of single-word Wang/Jenkins hash.
    h += (h <<  15) ^ 0xffffcd7d;
    h ^= (h >>> 10);
    h += (h <<   3);
    h ^= (h >>>  6);
    h += (h <<   2) + (h << 14);
    return h ^ (h >>> 16);
}

---------------put相关-------------------

private V putValueForNullKey(V value) {
    HashMapEntry<K, V> entry = entryForNullKey;
    //说明之前没有key为null的值添加过,首次添加key为null的值
    if (entry == null) {
        //★添加null key元素★
        addNewEntryForNullKey(value);
        size++;
        modCount++;
        return null;
    //说明之前有key为null的值添加过,进行vaule的替换
    } else {
        preModify(entry);
        V oldValue = entry.value;
        entry.value = value;
        return oldValue;
    }
}
//形成链表的重要函数
void addNewEntry(K key, V value, int hash, int index) {
    table[index] = new HashMapEntry<K, V>(key, value, hash, table[index]);
}
//创建一个NullKey的HashMapEntry对象
void addNewEntryForNullKey(V value) {
   entryForNullKey = new HashMapEntry<K, V>(null, value, 0, null);
}

看完put函数可能大家还有很多疑问,我们很好理解List这种结构也很好理解LinkedList这种链表结构,那么HashMap是一种怎样的散列链表呢,或者基于哈希表的Map接口实现呢

我们在put方法中可以看到,对传入的Key进行了二次哈希得到hash值,并将这个hash值与tab.length-1进行了&运算,那么大家可能发现如果2个值hash值相同,那么&运算后得到的index一定是相同,还有即使两个key的hash值不同,但是&运算还有可以导致index相同,在传统的Object[]中index相同可以就直接替换,那么这里是以一种什么方式来存在呢,其实HashMap这种散列链表结构类似于我们手机通讯录,A~Z
就是indext,但是A中会有多个数据,我们画图来表示一下大家就看的很明白了

这里写图片描述

是不是很清晰,如果index相同就加入到链表中,用next来记录下一个元素的引用,注意每次新添加元素调用put方法都会执行

//形成链表的重要函数
void addNewEntry(K key, V value, int hash, int index) {
    table[index] = new HashMapEntry<K, V>(key, value, hash, table[index]);
}

我们可以发现新添加的元素都是放到链表的头部的,这个是需要大家注意的,将之前的table[index]作为新的table[index]的next。(函数是先计算等号右边哦)

  • 删除
@Override 
public V remove(Object key) {
    if (key == null) {
        return removeNullKey();
    }
    int hash = Collections.secondaryHash(key);
    HashMapEntry<K, V>[] tab = table;
    int index = hash & (tab.length - 1);
    //相信上边这部分大家通过put方法都可以理解了
    //现在来看for循环
    for (HashMapEntry<K, V> e = tab[index], prev = null;
            e != null; prev = e, e = e.next) {
        //hash相同&&key值也相同,那么就说明找到了要删除的元素
        if (e.hash == hash && key.equals(e.key)) {
            //prev==null说明刚开始遍历就找到了元素,那么就说明元素是头结点
            if (prev == null) {
                //让头节点,变成当前元素的next元素
                tab[index] = e.next;
            } else {
                //prev 是 e的前一个元素,让e的前一个元素的next,指向e的next元素,就完成了删除
                prev.next = e.next;
            }
            modCount++;
            size--;
            //★子类进行重写★
            postRemove(e);
            return e.value;
        }
    }
    return null;
}
  • 查找
public V get(Object key) {
    if (key == null) {
        HashMapEntry<K, V> e = entryForNullKey;
        return e == null ? null : e.value;
    }

    int hash = Collections.secondaryHash(key);
    HashMapEntry<K, V>[] tab = table;
    for (HashMapEntry<K, V> e = tab[hash & (tab.length - 1)];
            e != null; e = e.next) {
        K eKey = e.key;
        //key== || hash值相同,key equals也相同
        if (eKey == key || (e.hash == hash && key.equals(eKey))) {
            return e.value;
        }
    }
    return null;
}

小贴士:当我们使用new HashMap()对HashMap进行创建的时候,会同时为我们创建一个长度为2的1次方的HashEntry

猜你喜欢

转载自blog.csdn.net/omyrobin/article/details/78614460