HashMap源码解析

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

HashMap继承抽象类AbstractMap,实现了Map接口。

  • HashMap的属性
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;                 //x<<y=x*2的y次方,x>>y=x/2的y次方 1<<4=16  默认初始容量
    static final int MAXIMUM_CAPACITY = 1 << 30;                        //最大容量,2的30次方
    static final float DEFAULT_LOAD_FACTOR = 0.75f;                     //默认的装载因子,达到75%就扩容
    static final Entry<?,?>[] EMPTY_TABLE = {};                         //定义个Entry类型的空数组
    transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;          //HashMap用来维护内部的数据结构
    transient int size;                                                 //map中保存键值对的数量
    int threshold;                                                      //需要调整大小的极限值(容量*装载因子)
    final float loadFactor;                                             //装载因子
    transient int modCount;                                             //map结构改变的次数
    static final int ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer.MAX_VALUE;  //threshold的最大值
    transient int hashSeed = 0;                                          //计算hash值时用的,默认是0
  • 静态内部类
        static class Entry<K,V> implements Map.Entry<K,V> {
        final K key;
        V value;
        Entry<K,V> next;                                            //链表形式存储,指向下一个entry
        int hash;                                                   //entry的hash值

        Entry(int h, K k, V v, Entry<K,V> n) {                      //初始化
            value = v;
            next = n;
            key = k;
            hash = h;
        }

        public final K getKey() {                                   //获取key
            return key;
        }

        public final V getValue() {                                 //获取value
            return value;
        }

        public final V setValue(V newValue) {                       //设置新的value,返回旧的value
            V oldValue = value;
            value = newValue;
            return oldValue;
        }

        public final boolean equals(Object o) {                     //传入节点与当前节点的key,value比较,都相等则返回true
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry e = (Map.Entry)o;
            Object k1 = getKey();
            Object k2 = e.getKey();
            if (k1 == k2 || (k1 != null && k1.equals(k2))) {
                Object v1 = getValue();
                Object v2 = e.getValue();
                if (v1 == v2 || (v1 != null && v1.equals(v2)))
                    return true;
            }
            return false;
        }

        public final int hashCode() {                                          //根据key和value生成hashCode
            return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue());  //^异或运算符,就是说相异为1,相同为0,
        }                                                                        //1 ^ 1 = 0;1 ^ 0 = 1;0 ^ 0 =0                                                                     

        public final String toString() {
            return getKey() + "=" + getValue();
        }

        void recordAccess(HashMap<K,V> m) {                                         //LinkedHashMap中有实现
        }

        void recordRemoval(HashMap<K,V> m) {                                        //LinkedHashMap中有实现
        }
    }

    private static class Holder {
        static final int ALTERNATIVE_HASHING_THRESHOLD;                             //容量阈值,初始化hashSeed的时候会用到该值
        static {
            String altThreshold = java.security.AccessController.doPrivileged(
                new sun.security.action.GetPropertyAction(
                    "jdk.map.althashing.threshold"));                            //获取系统变量jdk.map.althashing.threshold
            int threshold;
            try {
                threshold = (null != altThreshold)                              //假如系统变量jdk.map.althashing.threshold为null或-1时
                        ? Integer.parseInt(altThreshold)
                        : ALTERNATIVE_HASHING_THRESHOLD_DEFAULT;
                if (threshold == -1) {
                    threshold = Integer.MAX_VALUE;                              //将容量阈值threshold设置为Integer.MAX_VALUE。
                }
                if (threshold < 0) {
                    throw new IllegalArgumentException("value must be positive integer.");
                }
            } catch(IllegalArgumentException failed) {
                throw new Error("Illegal value for 'jdk.map.althashing.threshold'", failed);
            }
            ALTERNATIVE_HASHING_THRESHOLD = threshold;
        }
    }
  • HashMap的构造方法
    public HashMap(int initialCapacity, float loadFactor) {                     //根据传入容量与装载因子,生成一个空HashMap
        if (initialCapacity < 0)                                                 //初始容量不能小于0
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)                                  //初始容量不能大于默认的最大容量
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))                     //装载因子不能小于0,且不能为“NaN” (Not a Number)
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);

        this.loadFactor = loadFactor;                                       //将传入的装载因子赋值给装载因子属性
        threshold = initialCapacity;                                        //初始容量作为下一次扩容的容量。
        init();                                                             //LinkedHashMap中有实现。
    }   
    public HashMap(int initialCapacity) {                                   // 传入初始容量,装载因子使用默认值,生成一个空hashmap。
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }
    public HashMap() {                                                      //初始容量和负载因子全部使用默认值,生成一个空hashmap。
        this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
    }
    public HashMap(Map<? extends K, ? extends V> m) {                       //根据map对象生成一个hashmap,初始容量与传入的map相关,负载因子使用默认值
        this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
                      DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
        inflateTable(threshold);                                            //使用threshold新建一个数组

        putAllForCreate(m);                                                 //将map添加到数组中去
    }
   
    private void putAllForCreate(Map<? extends K, ? extends V> m) {         //添加指定map里面的所有键值对
        for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
            putForCreate(e.getKey(), e.getValue());
    }
    private void putForCreate(K key, V value) {
        int hash = null == key ? 0 : hash(key);                             //如果key为null,则hash值为0,否则根据key计算hash值
        int i = indexFor(hash, table.length);                               //根据hash值和数组的长度找到:该key所属entry在table中的位置i
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {              //循环链表,假如key相等则替换value值跳出。
            Object k;
            if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k)))) {
                e.value = value;
                return;
            }
        }
        createEntry(hash, key, value, i);                                   //如果没找到,将键值对与他的hash值作为一个entry
    }                                                                       //插入table的指定下标中的链表头中
    final int hash(Object k) {
        int h = hashSeed;
        if (0 != h && k instanceof String) {                                //如果key是String类型,stringHash32来生成hash值
            return sun.misc.Hashing.stringHash32((String) k);
        }
        h ^= k.hashCode();                                                  //一次散列
        h ^= (h >>> 20) ^ (h >>> 12);                                       //二次散列
        return h ^ (h >>> 7) ^ (h >>> 4);
    }
     static int indexFor(int h, int length) {
        return h & (length-1);                                              //除模取余,相当于hash % length,
    }                                                                       //经笔者数万次循环测试,&比%运算效率会高很多,次数越多,效率约明显
     void createEntry(int hash, K key, V value, int bucketIndex) {
        Entry<K,V> e = table[bucketIndex];
        table[bucketIndex] = new Entry<>(hash, key, value, e);
        size++;
    }

put

    public V put(K key, V value) {
        if (table == EMPTY_TABLE) {                                 //如果table为空,则新建数组
            inflateTable(threshold);
        }
        if (key == null)                                            //假如key为null,则循环数组,找到key为null的则替换value
            return putForNullKey(value);                            //否则创建一个数组,放到第0位。
        int hash = hash(key);                                       //根据key生成hash值
        int i = indexFor(hash, table.length);                       //根据hash值和数组长度找到找到entry所在数组中的位置
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {      //如果发现存在key与传入key相等,则替换其value,返回原value
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
        addEntry(hash, key, value, i);                              //先判断是否需要扩容,然后添加该键值到指定下标的链表头中
        return null;
    }

     private void inflateTable(int toSize) {
        int capacity = roundUpToPowerOf2(toSize);                          //内部数组的大小必须是2的n次方,所以要找到大于toSize的最小的2的n次方。
        threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);    //下次扩容临界值
        table = new Entry[capacity];                                        
        initHashSeedAsNeeded(capacity);                                     //根据数组长度初始化hashseed
    }
     private static int roundUpToPowerOf2(int number) {                    //如传入100,则返回128,即2的6次方
        return number >= MAXIMUM_CAPACITY                                  //(num-1)<<1 = (100-1)*2的1次方 = 198
                ? MAXIMUM_CAPACITY                                         //Integer.highestOneBit(198) return 128 即返回2的6次方
                : (number > 1) ? Integer.highestOneBit((number - 1) << 1) : 1;  //2的7次方就是256了,大于198了,返回不大于198,最大的2的N次方
    }
    final boolean initHashSeedAsNeeded(int capacity) {
        boolean currentAltHashing = hashSeed != 0;
        boolean useAltHashing = sun.misc.VM.isBooted() &&
                (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
        boolean switching = currentAltHashing ^ useAltHashing;
        if (switching) {
            hashSeed = useAltHashing
                ? sun.misc.Hashing.randomHashSeed(this)
                : 0;
        }
        return switching;
    }
    private V putForNullKey(V value) {
        for (Entry<K,V> e = table[0]; e != null; e = e.next) {
            if (e.key == null) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        modCount++;
        addEntry(0, null, value, 0);
        return null;
    }
    void addEntry(int hash, K key, V value, int bucketIndex) {
        if ((size >= threshold) && (null != table[bucketIndex])) {
            resize(2 * table.length);
            hash = (null != key) ? hash(key) : 0;
            bucketIndex = indexFor(hash, table.length);
        }

        createEntry(hash, key, value, bucketIndex);
    }   
    void createEntry(int hash, K key, V value, int bucketIndex) {
        Entry<K,V> e = table[bucketIndex];
        table[bucketIndex] = new Entry<>(hash, key, value, e);
        size++;
    }
    void resize(int newCapacity) {                                          //扩容,当前数组的2倍大小
        Entry[] oldTable = table;
        int oldCapacity = oldTable.length;
        if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return;
        }

        Entry[] newTable = new Entry[newCapacity];                          //创建个新的数组,大小为原数组的2倍
        transfer(newTable, initHashSeedAsNeeded(newCapacity));              //把数据都复制到新数组里去
        table = newTable;                                                   //新数组的引用直接指向table
        threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
    }
    void transfer(Entry[] newTable, boolean rehash) {
        int newCapacity = newTable.length;
        for (Entry<K,V> e : table) {
            while(null != e) {
                Entry<K,V> next = e.next;
                if (rehash) {
                    e.hash = null == e.key ? 0 : hash(e.key);
                }
                int i = indexFor(e.hash, newCapacity);
                e.next = newTable[i];
                newTable[i] = e;
                e = next;
            }
        }
    }

eg:

    import java.util.HashMap;
public class TestMain {
    public static void main(String[] args) {
    	HashMap map = new HashMap();
    	map.put("英语", 1);
    	map.put("日语", 2);
    	map.put("微积分",3);
    	map.put("数学", 4);
    	map.put("语文", 5);
    	map.put("政治", 6);
    	map.put("体育", 7);
    	map.put("音乐", 8);
    	map.put("绘画", 9);
    	map.put("历史", 10);
    	map.put("物理", 11);
    	map.put("化学", 12);
    	map.put("生物", 13);
    	map.put("编程", 14);
    	map.put("哲学", 15);
    	map.put("经济", 16);
    	System.out.println(map.toString());
    }	
}
这里重点讲下indexFor方法,假设我们元这里有个包含16个元素的HashMap,有这么2个素,map.put("语文",1);map.put("物理",1);
这2个元素的key是语文和物理,通过debug测试,得到这2个key的hash值是语文:1073632,物理:964128, h & (length-1)
语文:1073632&(16-1) = 0,物理964128&(16-1) = 0,2个key得到的下标都是0,即数组的第一个位置,此时即可发生碰撞,产生链表结构。

数据结构

bucket entry entry
语文 物理
null
null
日语 绘画
音乐
编程
null
null
微积分 数学
null
null
null
null
null
政治 历史
null
null
null
null
null
英语
哲学
经济
体育
化学
null
null
null
null
生物
这里还拿语文的key举例,1073632&(16-1)二进制 100000110000111100000^1111 结果就是0,这里拿长度16减1,是为了让二进制的低位尽量是1,假如直接用2的N次方,比如16,二进制就是10000,和key的hash值异或,发生碰撞的机会将会大大加大,这样hashMap效率就很差了。

get

    public V get(Object key) {
        if (key == null)
            return getForNullKey();                             //如果key为null,则从table[0]中取value
        Entry<K,V> entry = getEntry(key);                       //先根据key,找到其entry

        return null == entry ? null : entry.getValue();         //返回entry节点里的value值
    }
    private V getForNullKey() {
        if (size == 0) {
            return null;
        }
        for (Entry<K,V> e = table[0]; e != null; e = e.next) {      //查找table[0]处的链表,如果找到entry的key为null,就返回其value
            if (e.key == null)
                return e.value;
        }
        return null;
    }
    final Entry<K,V> getEntry(Object key) {
        if (size == 0) {
            return null;
        }

        int hash = (key == null) ? 0 : hash(key);                           //key为null,hash值为0,否则计算hash值
        for (Entry<K,V> e = table[indexFor(hash, table.length)];            //根据hash值和table长度找到table下标,
             e != null;                                                     //迭代该下标中的链表里的每一个entry节点
             e = e.next) {
            Object k;
            if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k))))
                return e;
        }
        return null;
    }

remove

    public V remove(Object key) {
        Entry<K,V> e = removeEntryForKey(key);                      //根据key来删除entry节点
        return (e == null ? null : e.value);
    }
    final Entry<K,V> removeEntryForKey(Object key) {
        if (size == 0) {
            return null;
        }
        int hash = (key == null) ? 0 : hash(key);                   //计算key的hash值
        int i = indexFor(hash, table.length);                       //计算下标
        Entry<K,V> prev = table[i];                                 //找到下标对应链表的头结点
        Entry<K,V> e = prev;

        while (e != null) {                                         //迭代单链表删除节点
            Entry<K,V> next = e.next;
            Object k;
            if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k)))) {
                modCount++;
                size--;
                if (prev == e)
                    table[i] = next;
                else
                    prev.next = next;
                e.recordRemoval(this);
                return e;
            }
            prev = e;
            e = next;
        }

        return e;
    }

Iteator

    private abstract class HashIterator<E> implements Iterator<E> {
        Entry<K,V> next;                                                //指向下个节点
        int expectedModCount;                                           //fast-fail机制
        int index;                                                      // 当前table下标
        Entry<K,V> current;                                             // 当前entry节点

        HashIterator() {
            expectedModCount = modCount;
            if (size > 0) { // advance to first entry
                Entry[] t = table;
                while (index < t.length && (next = t[index++]) == null)
                    ;
            }
        }

        public final boolean hasNext() {
            return next != null;
        }

        final Entry<K,V> nextEntry() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            Entry<K,V> e = next;
            if (e == null)
                throw new NoSuchElementException();

            if ((next = e.next) == null) {
                Entry[] t = table;
                while (index < t.length && (next = t[index++]) == null)
                    ;
            }
            current = e;
            return e;
        }

        public void remove() {
            if (current == null)
                throw new IllegalStateException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            Object k = current.key;
            current = null;
            HashMap.this.removeEntryForKey(k);
            expectedModCount = modCount;
        }
    }

总结

HashMap由数组和链表组成,以key-value存在。key和value都允许为空,key重复会覆盖,value可以重复,无序,非线程安全。
hashmap中查找一个值,需要两次定位,先找到元素在数组的位置的链表上,然后在链表上查找,在HashMap中的第一次定位是由hash值确定的,第二次定位由key和hash值确定。

posted @ 2018-03-22 20:18  Ch1nYK  阅读(195)  评论(0编辑  收藏  举报