三、HashMap源码分析

来源:互联网 发布:战地2win7优化补丁 编辑:程序博客网 时间:2024/05/17 13:38

一、HashMap源码分析

HashMap源码分析:
HashMap 底层是数组加链表的形式

    //hashMap默认的容量大小为16 必须是2的幂次方    static final int DEFAULT_INITIAL_CAPACITY = 16;    //hashMap 2的幂次方容量 <= 1<<30    static final int MAXIMUM_CAPACITY = 1 << 30;    //默认负载系数 用于判断hashMap什么时候进扩容    static final float DEFAULT_LOAD_FACTOR = 0.75f    //hashMap底层是Entry数组    transient Entry<K,V>[] table    //容量大小    transient int size;    // threshold=容量*负载系数    int threshold;    // 负载系数    final float loadFactor;    // the HashMap fail-fast 集合在遍历时,不能进行修改,增加和删除    transient int modCount;

HashMap 构造函数

    //无参构造函数 容量为默认的16 负载系数为0.75    public HashMap() {        this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);    }    //指定容量大小的  负载系数为0.75    public HashMap(int initialCapacity) {        this(initialCapacity, DEFAULT_LOAD_FACTOR);    }    //指定容量和负载系数    public HashMap(int initialCapacity, float loadFactor) {        if (initialCapacity < 0)            throw new IllegalArgumentException("Illegal initial capacity: " +                                               initialCapacity);        if (initialCapacity > MAXIMUM_CAPACITY)            initialCapacity = MAXIMUM_CAPACITY;        if (loadFactor <= 0 || Float.isNaN(loadFactor))            throw new IllegalArgumentException("Illegal load factor: " +                                               loadFactor);        int capacity = 1;        //遍历 找到2的幂 >= 指定容量 确认hashMap的容量        while (capacity < initialCapacity)            capacity* <<= 1;        this.loadFactor = loadFactor;        //记录 capacity*loadFactor 的大小        threshold = (int)Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);        //创建长度为capacity 的数组        table = new Entry[capacity];        useAltHashing = sun.misc.VM.isBooted() &&                (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);        init();    }

hashMap 存入键值对

    //put    public V put(K key, V value) {        //如果存入的key为null        if (key == null)            return putForNullKey(value);        //确认hash值        int hash = hash(key);        //确认桶的位置        int i = indexFor(hash, table.length);        //遍历桶的位置上是否有相同的可以,有就替换,返回旧值        for (Entry<K,V> e = table[i]; e != null; e = e.next) {            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;    }    //存入key为null的值    private V putForNullKey(V value) {        //取第一个桶,如果也存在key为null值,则替换,返回旧值        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++;        //则hash值为0 ,放在第一个桶里 返回null        addEntry(0, null, value, 0);        return null;    }    //将添加元素放入到数组中    void addEntry(int hash, K key, V value, int bucketIndex) {        //如果当前集合的大小大于等于 threshold(当前容量*负载系数) 并且插入的数组位置不为空,则进行扩容        if ((size >= threshold) && (null != table[bucketIndex])) {            //扩容为原来的2倍            resize(2 * table.length);            //扩容后需要重新计算hash值            hash = (null != key) ? hash(key) : 0;            //根据hash值和数组的长度确认放入桶的位置            bucketIndex = indexFor(hash, table.length);        }        createEntry(hash, key, value, bucketIndex);    }    //扩容    void resize(int newCapacity) {        Entry[] oldTable = table;        int oldCapacity = oldTable.length;        if (oldCapacity == MAXIMUM_CAPACITY) {            threshold = Integer.MAX_VALUE;            return;        }        Entry[] newTable = new Entry[newCapacity];        boolean oldAltHashing = useAltHashing;        useAltHashing |= sun.misc.VM.isBooted() &&                (newCapacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);        boolean rehash = oldAltHashing ^ useAltHashing;        //将旧数组的元素转移到新数组中        transfer(newTable, rehash);        table = newTable;        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;            }        }    }    // 确认元素存入桶的位置    static int indexFor(int h, int length) {        return h & (length-1);    }    //添加元素进桶    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++;    }

hashMap根据key获取值

     public V get(Object key) {        if (key == null)            return getForNullKey();        Entry<K,V> entry = getEntry(key);        return null == entry ? null : entry.getValue();    }    //如果key为null则去第一个桶的,如果有在返回,没有就返回null    private V getForNullKey() {        for (Entry<K,V> e = table[0]; e != null; e = e.next) {            if (e.key == null)                return e.value;        }        return null;    }    //根据key获取Entry    final Entry<K,V> getEntry(Object key) {        //首先通过key获取hash值        int hash = (key == null) ? 0 : hash(key);        //然后找到元素存放的桶,遍历桶上的元素,没有返回null        for (Entry<K,V> e = table[indexFor(hash, table.length)];             e != null;             e = e.next) {            Object k;            if (e.hash == hash &&                ((k = e.key) == key || (key != null && key.equals(k))))                return e;        }        return null;    }

hashMap清空

 public void clear() {        modCount++;        Entry[] tab = table;        for (int i = 0; i < tab.length; i++)            tab[i] = null;        size = 0;    }

判断hashMap中是否包含value

    public boolean containsValue(Object value) {        if (value == null)            return containsNullValue();        Entry[] tab = table;        //遍历所有数组中的Entry        for (int i = 0; i < tab.length ; i++)            for (Entry e = tab[i] ; e != null ; e = e.next)                if (value.equals(e.value))                    return true;        return false;    }

hashMap 内部类 Entry

 static class Entry<K,V> implements Map.Entry<K,V> {        final K key;        V value;        Entry<K,V> next;        int hash;        /**         * Creates new entry.         */        Entry(int h, K k, V v, Entry<K,V> n) {            value = v;            next = n;            key = k;            hash = h;        }    }

总结:
1.hashMap底层是数组和加链表的形式,元素都是存放在一个一个桶里,以key的hash值和数组的大小确认
2.hashMap默认的容量大小为16,负载系数为0.75 也就是当容量大小为12是,就会进行扩容,容量为原来的2倍,这方面平时需要注意,尽量减少扩容
3.hashMap中的Entry分别存放了 key,value,本身,还有hash值

0 0