HashMap理解及源码探析1.8vs1.7

HashMap是一种使用拉链法避免哈希冲突的哈希表,使用数组加链表来进行实现。

一、初步了解

1、首先看一下HashMap它的结构应该是这样的。如下图:

使用数组加链表实现:每一个数组元素就是一个Entry对象,每一个Entry对象都是一个链表节点含有指向下一个节点的引用next,我们可以将每一个数组元素对应的链表称作为一个桶。

2、源码初探

public class HashMap<k v=""> extends AbstractMap<k v=""> implements Map<k v="">, Cloneable, Serializable { } </k></k></k>

源码头的部分我们可以看到继承了AbstractMap<K,V> ,实现了Map<K,V>

下面看一下这两部分的源码头部分:

AbstractMap<K,V>:

public abstract class AbstractMap<k v> implements Map<k v> { /*省略部分代码*/ }

Map<K,V>:

public interface Map<k v> { /*省略部分代码*/ }

Map<K,V>源码如下:

public interface Map<k v="">{int size();
//在AbstractMap中实现
        boolean isEmpty();
// 在AbstractMap中实现
        boolean containsKey(Object key);
// 在AbstractMap中实现
        boolean containsValue(Object value);
// 在AbstractMap中实现 
        V get(Object key);
// 在AbstractMap中实现 
        V put(K key,V value);
// 通过抛异常的方式让子类来实现。 
        V remove(Object key);
// 在AbstractMap中实现 
        void putAll(Map<?extends K, ?extends V> m);
// 在AbstractMap中实现 
        void clear();
// 在AbstractMap中实现 
        Set<k> keySet();
// 在AbstractMap中实现 
        Collection<v> values();
// 在AbstractMap中实现 
        Set<Map.Entry<k v="">>entrySet();

interface Entry<k v="">{K getKey();
        V getValue();V setValue(V value);
        boolean equals(Object o);
        int hashCode();
        }
        boolean equals(Object o);
// 在AbstractMap中实现 
        int hashCode();
// 在AbstractMap中实现 
        }

AbstractMap<K,V>源码如下:

package java.util;
import java.util.Map.Entry;
public abstract class AbstractMap<K,V> implements Map<K,V> {
    protected AbstractMap() {
    }
    public int size() {
        return entrySet().size();
    }
    public boolean isEmpty() {
        return size() == 0;
    }
    public boolean containsValue(Object value) {
        Iterator<Entry<K,V>> i = entrySet().iterator();
        if (value==null) {
            while (i.hasNext()) {
                Entry<K,V> e = i.next();
                if (e.getValue()==null)
                    return true;
            }
        } else {
            while (i.hasNext()) {
                Entry<K,V> e = i.next();
                if (value.equals(e.getValue()))
                    return true;
            }
        }
        return false;
    }
    public boolean containsKey(Object key) {
        Iterator<Map.Entry<K,V>> i = entrySet().iterator();
        if (key==null) {
            while (i.hasNext()) {
                Entry<K,V> e = i.next();
                if (e.getKey()==null)
                    return true;
            }
        } else {
            while (i.hasNext()) {
                Entry<K,V> e = i.next();
                if (key.equals(e.getKey()))
                    return true;
            }
        }
        return false;
    }
    public V get(Object key) {
        Iterator<Entry<K,V>> i = entrySet().iterator();
        if (key==null) {
            while (i.hasNext()) {
                Entry<K,V> e = i.next();
                if (e.getKey()==null)
                    return e.getValue();
            }
        } else {
            while (i.hasNext()) {
                Entry<K,V> e = i.next();
                if (key.equals(e.getKey()))
                    return e.getValue();
            }
        }
        return null;
    }
    public V put(K key, V value) {
        throw new UnsupportedOperationException();
    }
    public V remove(Object key) {
        Iterator<Entry<K,V>> i = entrySet().iterator();
        Entry<K,V> correctEntry = null;
        if (key==null) {
            while (correctEntry==null && i.hasNext()) {
                Entry<K,V> e = i.next();
                if (e.getKey()==null)
                    correctEntry = e;
            }
        } else {
            while (correctEntry==null && i.hasNext()) {
                Entry<K,V> e = i.next();
                if (key.equals(e.getKey()))
                    correctEntry = e;
            }
        }

        V oldValue = null;
        if (correctEntry !=null) {
            oldValue = correctEntry.getValue();
            i.remove();
        }
        return oldValue;
    }
    public void putAll(Map<? extends K, ? extends V> m) {
        for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
            put(e.getKey(), e.getValue());
    }
    public void clear() {
        entrySet().clear();
    }
    transient Set<K>        keySet;
    transient Collection<V> values;

    public Set<K> keySet() {
        Set<K> ks = keySet;
        if (ks == null) {
            ks = new AbstractSet<K>() {
                public Iterator<K> iterator() {
                    return new Iterator<K>() {
                        private Iterator<Entry<K,V>> i = entrySet().iterator();

                        public boolean hasNext() {
                            return i.hasNext();
                        }

                        public K next() {
                            return i.next().getKey();
                        }

                        public void remove() {
                            i.remove();
                        }
                    };
                }

                public int size() {
                    return AbstractMap.this.size();
                }

                public boolean isEmpty() {
                    return AbstractMap.this.isEmpty();
                }

                public void clear() {
                    AbstractMap.this.clear();
                }

                public boolean contains(Object k) {
                    return AbstractMap.this.containsKey(k);
                }
            };
            keySet = ks;
        }
        return ks;
    }
    public Collection<V> values() {
        Collection<V> vals = values;
        if (vals == null) {
            vals = new AbstractCollection<V>() {
                public Iterator<V> iterator() {
                    return new Iterator<V>() {
                        private Iterator<Entry<K,V>> i = entrySet().iterator();

                        public boolean hasNext() {
                            return i.hasNext();
                        }

                        public V next() {
                            return i.next().getValue();
                        }

                        public void remove() {
                            i.remove();
                        }
                    };
                }

                public int size() {
                    return AbstractMap.this.size();
                }

                public boolean isEmpty() {
                    return AbstractMap.this.isEmpty();
                }

                public void clear() {
                    AbstractMap.this.clear();
                }

                public boolean contains(Object v) {
                    return AbstractMap.this.containsValue(v);
                }
            };
            values = vals;
        }
        return vals;
    }

    public abstract Set<Entry<K,V>> entrySet();
    public boolean equals(Object o) {
        if (o == this)
            return true;

        if (!(o instanceof Map))
            return false;
        Map<?,?> m = (Map<?,?>) o;
        if (m.size() != size())
            return false;

        try {
            Iterator<Entry<K,V>> i = entrySet().iterator();
            while (i.hasNext()) {
                Entry<K,V> e = i.next();
                K key = e.getKey();
                V value = e.getValue();
                if (value == null) {
                    if (!(m.get(key)==null && m.containsKey(key)))
                        return false;
                } else {
                    if (!value.equals(m.get(key)))
                        return false;
                }
            }
        } catch (ClassCastException unused) {
            return false;
        } catch (NullPointerException unused) {
            return false;
        }

        return true;
    }
    //省略........


}

对比这两部分的代码我们可以知道对于put方法是HashMap自己实现的。

二、HashMap源码分析

package java.util; import java.io.*; public class HashMap<k v=""> extends AbstractMap<k v=""> implements Map<k v="">, Cloneable, Serializable { // 默认初始容量,必须是2的n次幂 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 // 默认最大容量 static final int MAXIMUM_CAPACITY = 1 << 30; // 默认负载因子,可在构造函数中指定 static final float DEFAULT_LOAD_FACTOR = 0.75f; //当表为扩展时的共享空表实例。 static final Entry<?,?>[] EMPTY_TABLE = {}; //需要调整表的大小但是必须是2的n次幂 transient Entry<k v="">[] table = (Entry<k v="">[]) EMPTY_TABLE; //当前键值对的数量,也就是当前的size transient int size; // 阈值 int threshold; // 哈希表的负载因子 final float loadFactor; // 用于确保使用迭代器的时候,HashMap并未进行更改 transient int modCount; static final int ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer.MAX_VALUE; private static class Holder { /** * Table capacity above which to switch to use alternative hashing. */ static final int ALTERNATIVE_HASHING_THRESHOLD; static { String altThreshold = java.security.AccessController.doPrivileged( new sun.security.action.GetPropertyAction( "jdk.map.althashing.threshold")); int threshold; try { threshold = (null != altThreshold) ? Integer.parseInt(altThreshold) : ALTERNATIVE_HASHING_THRESHOLD_DEFAULT; // disable alternative hashing if -1 if (threshold == -1) { 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; } } transient int hashSeed = 0; // 构造一个带指定初始容量和加载因子的空 HashMap。 public HashMap(int initialCapacity, float loadFactor) { // 如果指定初始容量小于0,抛出异常 if (initialCapacity < 0) throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); // 如果初始容量大于系统默认最大容量,则初始容量为最大容量 if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; // 如果loadFactor小于0,或loadFactor是NaN,抛出异常 if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal load factor: " + loadFactor); this.loadFactor = loadFactor; threshold = initialCapacity; init(); } // 构造一个带指定初始容量和默认加载因子 (0.75) 的空 HashMap。 public HashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); } // 构造一个具有默认初始容量 (16) 和默认加载因子 (0.75) 的空 HashMap。 public HashMap() { this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR); } // 构造一个映射关系与指定 Map 相同的新 HashMap。 public HashMap(Map<? extends K, ? extends V> m) { this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR); inflateTable(threshold); putAllForCreate(m); } private static int roundUpToPowerOf2(int number) { // assert number >= 0 : "number must be non-negative"; int rounded = number >= MAXIMUM_CAPACITY ? MAXIMUM_CAPACITY : (rounded = Integer.highestOneBit(number)) != 0 ? (Integer.bitCount(number) > 1) ? rounded << 1 : rounded : 1; return rounded; } private void inflateTable(int toSize) { // Find a power of 2 >= toSize int capacity = roundUpToPowerOf2(toSize); threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1); table = new Entry[capacity]; initHashSeedAsNeeded(capacity); } void init() { } 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; } // 预处理hash值,避免较差的离散hash序列,导致桶没有充分利用 final int hash(Object k) { int h = hashSeed; if (0 != h && k instanceof String) { return sun.misc.Hashing.stringHash32((String) k); } h ^= k.hashCode(); // This function ensures that hashCodes that differ only by // constant multiples at each bit position have a bounded // number of collisions (approximately 8 at default load factor). h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); } // 返回对应hash值得索引 static int indexFor(int h, int length) { // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2"; return h & (length-1); } // 返回当前map的key-value键值对数量,也就是当前size public int size() { return size; } // 该HashMap是否是空的,如果size为0,则为空 public boolean isEmpty() { return size == 0; } // 返回指定键所映射的值;如果对于该键来说,此映射不包含任何映射关系,则返回 null public V get(Object key) { // 如果key为null if (key == null) return getForNullKey(); //获取Entry对象 //未找到就返回null,否则返回键所对应的值 Entry<k v=""> entry = getEntry(key); return null == entry ? null : entry.getValue(); } //getForNullKey方法就是在数组0索引位上的链表去寻找null键: private V getForNullKey() { //如果size为0返回null if (size == 0) { return null; } for (Entry<k v=""> e = table[0]; e != null; e = e.next) { if (e.key == null) return e.value; } return null; } // 如果此映射包含对于指定键的映射关系,则返回true public boolean containsKey(Object key) { return getEntry(key) != null; } // 通过key获取一个value final Entry<k v=""> getEntry(Object key) { if (size == 0) { return null; } // 如果key为null,则hash为0,否则用hash函数预处理 int hash = (key == null) ? 0 : hash(key); // 得到对应的hash值的桶,如果这个桶不是,就通过next获取下一个桶 for (Entry<k v=""> e = table[indexFor(hash, table.length)]; e != null; e = e.next) { Object k; // 如果hash值相等,并且key相等则证明这个桶里的东西是我们想要的 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } // 所有桶都找遍了,没找到想要的,所以返回null return null; } //很重要的put方法 public V put(K key, V value) { if (table == EMPTY_TABLE) { inflateTable(threshold); } //如果键为空,则调用putForNullKey if (key == null) return putForNullKey(value); //否则根据key生成一个hash索引值 int hash = hash(key); //在根据索引值找到插入位置 int i = indexFor(hash, table.length); //循环遍历指定位置的Entry链表,若找到一个键与当前键完全一致的Entry,那么覆盖原来的键所对应的值,并返回原值 for (Entry<k v=""> e = table[i]; e != null; e = e.next) { Object k; //hash值相同且键相同 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怎么放value private V putForNullKey(V value) { // 遍历table[0]的所有桶 for (Entry<k v=""> e = table[0]; e != null; e = e.next) { // 如果key是null if (e.key == null) { // 取出oldValue,并存入value V oldValue = e.value; e.value = value; e.recordAccess(this); // 返回oldValue return oldValue; } } modCount++; addEntry(0, null, value, 0); return null; } // 判断是否需要创建新的桶 private void putForCreate(K key, V value) { // 如果key为null,则定义hash为0,否则用hash函数预处理 int hash = null == key ? 0 : hash(key); // 获取对应的索引 int i = indexFor(hash, table.length); // 遍历所有桶 for (Entry<k v=""> e = table[i]; e != null; e = e.next) { Object k; // 如果有hash相同,且key相同,那么则不需要创建新的桶,退出 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { e.value = value; return; } } // 否则需要创建新的桶 createEntry(hash, key, value, i); } // 根据Map创建所有对应的桶 private void putAllForCreate(Map<? extends K, ? extends V> m) { for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) putForCreate(e.getKey(), e.getValue()); } // 更具新的容量来resize这个HashMap void resize(int newCapacity) { // 保存oldTable Entry[] oldTable = table; // 保存旧的容量 int oldCapacity = oldTable.length; // 如果旧的容量已经是系统默认最大容量了,那么将阈值设置成整形的最大值,退出 if (oldCapacity == MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return; } // 根据新的容量新建一个table Entry[] newTable = new Entry[newCapacity]; // 将table转换成newTable transfer(newTable, initHashSeedAsNeeded(newCapacity)); // 将table设置newTable table = newTable; // 设置阈值 threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1); } // 将所有格子里的桶都放到新的table中 void transfer(Entry[] newTable, boolean rehash) { // 得到新的容量 int newCapacity = newTable.length; // 遍历src里面的所有格子 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; } } } // 将指定映射的所有映射关系复制到此映射中,这些映射关系将替换此映射目前针对指定映射中所有键的所有映射关系 public void putAll(Map<? extends K, ? extends V> m) { // 看看需要复制多少个映射关系 int numKeysToBeAdded = m.size(); if (numKeysToBeAdded == 0) return; // 如果要复制的映射关系比阈值还要多 if (table == EMPTY_TABLE) { inflateTable((int) Math.max(numKeysToBeAdded * loadFactor, threshold)); } // 重新计算新的容量先resize if (numKeysToBeAdded > threshold) { int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1); if (targetCapacity > MAXIMUM_CAPACITY) targetCapacity = MAXIMUM_CAPACITY; int newCapacity = table.length; while (newCapacity < targetCapacity) newCapacity <<= 1; if (newCapacity > table.length) resize(newCapacity); } // 迭代将key-value映射放进该HashMap for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) put(e.getKey(), e.getValue()); } // 从此映射中移除指定键的映射关系(如果存在) public V remove(Object key) { Entry<k v=""> e = removeEntryForKey(key); return (e == null ? null : e.value); } // 根据key删除桶,并返回对应value final Entry<k v=""> removeEntryForKey(Object key) { if (size == 0) { return null; } int hash = (key == null) ? 0 : hash(key); 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; } final Entry<k v=""> removeMapping(Object o) { if (size == 0 || !(o instanceof Map.Entry)) return null; Map.Entry<k v=""> entry = (Map.Entry<k v="">) o; Object key = entry.getKey(); int hash = (key == null) ? 0 : hash(key); 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; if (e.hash == hash && e.equals(entry)) { modCount++; size--; if (prev == e) table[i] = next; else prev.next = next; e.recordRemoval(this); return e; } prev = e; e = next; } return e; } public void clear() { modCount++; Arrays.fill(table, null); size = 0; } public boolean containsValue(Object value) { if (value == null) return containsNullValue(); Entry[] tab = table; 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; } private boolean containsNullValue() { Entry[] tab = table; for (int i = 0; i < tab.length ; i++) for (Entry e = tab[i] ; e != null ; e = e.next) if (e.value == null) return true; return false; } public Object clone() { HashMap<k v=""> result = null; try { result = (HashMap<k v="">)super.clone(); } catch (CloneNotSupportedException e) { // assert false; } if (result.table != EMPTY_TABLE) { result.inflateTable(Math.min( (int) Math.min( size * Math.min(1 / loadFactor, 4.0f), // we have limits... HashMap.MAXIMUM_CAPACITY), table.length)); } result.entrySet = null; result.modCount = 0; result.size = 0; result.init(); result.putAllForCreate(this); return result; } // 内置class输入对象,也就是我们说的桶 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; } public final K getKey() { return key; } public final V getValue() { return value; } public final V setValue(V newValue) { V oldValue = value; value = newValue; return oldValue; } public final boolean equals(Object o) { 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() { return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue()); } public final String toString() { return getKey() + "=" + getValue(); } void recordAccess(HashMap<k v=""> m) { } void recordRemoval(HashMap<k v=""> m) { } } 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++; } private abstract class HashIterator<e> implements Iterator<e> { Entry<k v=""> next; // next entry to return int expectedModCount; // For fast-fail int index; // current slot Entry<k v=""> current; // 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; } } private final class ValueIterator extends HashIterator<v> { public V next() { return nextEntry().value; } } private final class KeyIterator extends HashIterator<k> { public K next() { return nextEntry().getKey(); } } private final class EntryIterator extends HashIterator<Map.Entry<k v="">> { public Map.Entry<k v=""> next() { return nextEntry(); } } // Subclass overrides these to alter behavior of views' iterator() method Iterator<k> newKeyIterator() { return new KeyIterator(); } Iterator<v> newValueIterator() { return new ValueIterator(); } Iterator<Map.Entry<k v="">> newEntryIterator() { return new EntryIterator(); } // Views private transient Set<Map.Entry<k v="">> entrySet = null; public Set<k> keySet() { Set<k> ks = keySet; return (ks != null ? ks : (keySet = new KeySet())); } private final class KeySet extends AbstractSet<k> { public Iterator<k> iterator() { return newKeyIterator(); } public int size() { return size; } public boolean contains(Object o) { return containsKey(o); } public boolean remove(Object o) { return HashMap.this.removeEntryForKey(o) != null; } public void clear() { HashMap.this.clear(); } } public Collection<v> values() { Collection<v> vs = values; return (vs != null ? vs : (values = new Values())); } private final class Values extends AbstractCollection<v> { public Iterator<v> iterator() { return newValueIterator(); } public int size() { return size; } public boolean contains(Object o) { return containsValue(o); } public void clear() { HashMap.this.clear(); } } public Set<Map.Entry<k v="">> entrySet() { return entrySet0(); } private Set<Map.Entry<k v="">> entrySet0() { Set<Map.Entry<k v="">> es = entrySet; return es != null ? es : (entrySet = new EntrySet()); } private final class EntrySet extends AbstractSet<Map.Entry<k v="">> { public Iterator<Map.Entry<k v="">> iterator() { return newEntryIterator(); } public boolean contains(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry<k v=""> e = (Map.Entry<k v="">) o; Entry<k v=""> candidate = getEntry(e.getKey()); return candidate != null && candidate.equals(e); } public boolean remove(Object o) { return removeMapping(o) != null; } public int size() { return size; } public void clear() { HashMap.this.clear(); } } private void writeObject(java.io.ObjectOutputStream s) throws IOException { // Write out the threshold, loadfactor, and any hidden stuff s.defaultWriteObject(); // Write out number of buckets if (table==EMPTY_TABLE) { s.writeInt(roundUpToPowerOf2(threshold)); } else { s.writeInt(table.length); } // Write out size (number of Mappings) s.writeInt(size); // Write out keys and values (alternating) if (size > 0) { for(Map.Entry<k v=""> e : entrySet0()) { s.writeObject(e.getKey()); s.writeObject(e.getValue()); } } } private static final long serialVersionUID = 362498820763181265L; private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { // Read in the threshold (ignored), loadfactor, and any hidden stuff s.defaultReadObject(); if (loadFactor <= 0 || Float.isNaN(loadFactor)) { throw new InvalidObjectException("Illegal load factor: " + loadFactor); } // set other fields that need values table = (Entry<k v="">[]) EMPTY_TABLE; // Read in number of buckets s.readInt(); // ignored. // Read number of mappings int mappings = s.readInt(); if (mappings < 0) throw new InvalidObjectException("Illegal mappings count: " + mappings); // capacity chosen by number of mappings and desired load (if >= 0.25) int capacity = (int) Math.min( mappings * Math.min(1 / loadFactor, 4.0f), // we have limits... HashMap.MAXIMUM_CAPACITY); // allocate the bucket array; if (mappings > 0) { inflateTable(capacity); } else { threshold = capacity; } init(); // Give subclass a chance to do its thing. // Read the keys and values, and put the mappings in the HashMap for (int i = 0; i < mappings; i++) { K key = (K) s.readObject(); V value = (V) s.readObject(); putForCreate(key, value); } } // These methods are used when serializing HashSets int capacity() { return table.length; } float loadFactor() { return loadFactor; } }

最后说一下为什么要是2的n次幂

以及indexFor(int h, int length) 函数中为什么使用return h & (length-1);

通过如下代码进行测试:

package com.yang.review; 
/** 
 * @author 阳
 */ 
public class Test {
    /** 
     * @param args 
     */ 
    public static void main(String[] args) {
        int a = 1<<6; 
        int h = 68;
        int length = 15; 
        System.out.println(a); 
        System.out.println("h & (length-1)" +( h & length)); 
        System.out.println("h% (length-1)" + h%(length+1)); 
    }

}

我们发现其实就是取模,但是计算机中对于位运算的效率极高,所以使用位运算的方式代替直接取余。

------------------------------------------------------------------------------------------------------------------------------------------------------------------------

以上是Java8之前的。

java8在hashMap中引入了红黑树,因为之前版本的话如果数组+链表的方式 那么当某个数组的位置中的bucket长度过大时,而我们查询的元素如果出现在bucket的最后一个位置,那么性能就由原来的O(1)--->O(n)是很影响效率的,在Java8中进行了优化使用红黑树当bucket中的元素数量达到阈值时 TREEIFY_THRESHOLD,该值为8 那么就将链表bucket树化为红黑树,这样可以使得之前最坏情况下的性能由原来的O(n)变为O(logn).

源码部分:

之前的HashMap里面使用的是Entry对象进行存储,现在引入了红黑树,所以改成了Node进行存储,之前的get方法现在改成了调用getNode()方法,之前的put方法现在改成了里面调用putVal()方法。

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

putVal()方法内部代码:

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;
    }
源码大致如下:
首先如果数组为空的话使用resize进行初始化,
(resize也可以进行扩容判断再该部分代码的后半段),
然后判断是否发生碰撞如果没有就是新元素,直接newNode放在数组的该位置。
如果发生碰撞此时先判断传入进来的key是否和 当前位置的一样如果一样则直接替换原来的元素,
然后判断此时是否是树化后的,如果是 则将该值放入红黑树,如果不是则按照链表的方式进行添加元素,
同时判断链表的元素数量是否达到阈值,达到的话则调用treefyBin方法进行树化,
最后是如果发现插入的键位如果存在于hashMap中则新值替换旧值返回旧值。

get()方法变成getNode()

public V get(Object key) {
        Node<K,V> e;
        return (e = getNode(hash(key), key)) == null ? null : e.value;
    }

    /**
     * Implements Map.get and related methods
     *
     * @param hash hash for key
     * @param key the key
     * @return the node, or null if none
     */
    final Node<K,V> getNode(int hash, Object key) {
        Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {
            if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null && key.equals(k))))
                return first;
            if ((e = first.next) != null) {
                if (first instanceof TreeNode)
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);
            }
        }
        return null;
    }

就不多说了。

HashMap的resize方法会引起线程安全问题

           Java1.7及之前的是采用的头插法,比如我们插入a、b、c三个值,假设计算完hash后hash值相同,然后计算索引位置运算后,位置相同那么就是这个样子的一个链表 c - b - a ,首先我们要知道resize的时候会复制一个原来长度两倍的数组,int oldCap = (oldTab == null) ? 0 : oldTab.length; int oldThr = threshold;newCap = oldCap << 1 ;newThr = oldThr << 1; // double threshold

当遍历原来的链表 c-b-a 时 会出现a-b-c 也就是将原来链表逆序,但是由于多线程可能a-b的时候另外一个线程还是存储着b-a的关系,这样在resize时就会有可能产生a-b-a这样的一个环,所以Java1.7之前的HashMap在多线程环境下进行使用的时候时会有线程安全问题,当然了多线程环境下也不建议使用HashMap,而Java1.7之所以会产生链表的环就是因为头插,所以Java1.8订正了这个问题,改为尾插法,这样就不会改变链表元素的顺序,也就不会产生环,那么既然Java1.8的修改不会产生环而导致线程安全问题,那么是不是意味着我们就可以在多线程环境下使用HashMap了呢?我们注意到HashMap中的所有方法都么有进行加锁,尤其我们常用的put和get方法,那么既然没有加锁就会意味着这时存在线程安全问题的,比如我们put(a,1)然后另外一个线程put(a,2),这个时候我们进行get我们无法保证能够拿到1这个值,因为已经被另外一个线程给修改过了。所以Hash Map还是不能够用在多线程环境下的

Collections(使用mutex 锁对象-定义了一个成员变量作为锁)工具类保证线程安全和HashTable(直接在方法上使用synchronized加锁,锁粒度大)保证线程安全效率来讲是一样的。

HashMap允许null key  null  value  但是 HashTable  和ConccurenthashMap都是不允许的

           Hashtable使用的是安全失败机制(fail-safe),这种机制会使你此次读到的数据不一定是最新的数据。如果你使用null值,就会使得其无法判断对应的key是不存在还是为空,因为你无法再调用一次contain(key)来对key是否存在进行判断,ConcurrentHashMap同理。HashMap和HashTable的迭代器不同:HashMap 中的 Iterator 迭代器是 fail-fast 的,而 Hashtable 的 Enumerator 不是 fail-fast 的。快速失败(fail—fast)是java集合中的一种机制, 在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增加、删除、修改),则会抛出Concurrent Modification Exception。所以,当其他线程改变了HashMap 的结构,如:增加、删除元素,将会抛出ConcurrentModificationException 异常,而 Hashtable 则不会。迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。集合在被遍历期间如果内容发生变化,就会改变modCount的值。每当迭代器使用hashNext()/next()遍历下一个元素之前,都会检测modCount变量是否为expectedmodCount值,是的话就返回遍历;否则抛出异常,终止遍历。这里异常的抛出条件是检测到 modCount!=expectedmodCount 这个条件。如果集合发生变化时修改modCount值刚好又设置为了expectedmodCount值,则异常不会抛出。因此,不能依赖于这个异常是否抛出而进行并发操作的编程,这个异常只建议用于检测并发修改的bug。java.util包下的集合类都是快速失败的,不能在多线程下发生并发修改(迭代过程中被修改)算是一种安全机制吧。安全失败(fail—safe)大家也可以了解下,java.util.concurrent包下的容器都是安全失败,可以在多线程下并发使用,并发修改。

            那么还有ConccurentHashMap 之前也就是早期的是使用segmen分段锁来实现线程安全的因为我们在创建hashMap时默认初始化数组大小是16 所以之前的思想是分成16个分段锁,这样访问没一个桶都可以不会因为锁粒度的问题导致无法访问,现在的思想是使用cas+synchronize 而且也用到了红黑树。思想是一样的也是先hash然后确认数组的位置,找到应该存放的索引位置,使用cas进行插入bucket的头结点。(cas是乐观锁的实现,普通的cas会出现ABA问题,解决办法就是使用版本号)。如果不是新元素(也就是说发生了hash碰撞)那么我们就使用synchronized关键字锁住链表或者红黑树的头结点。

ConccurentHashMap put方法逻辑

1、判断Node[]数组是否初始化,没有初始化 那么就进行初始化操作。

2、通过hash定位数组的索引坐标,是否有Node节点,如果没有则使用cas进行添加(链表的节点),添加失败则进入下次循环。

3、检查到内部正在扩容,就协助扩容。

4、如果 f != null 则使用synchronized 锁住f 元素(f元素是链表/红黑二叉树的头元素)

      如果是Node(链表结构)则执行链表的添加操作。

      如果是TreeNode(树型结构)则执行树添加操作。

5、判断链表长度已经达到临界值8,8是默认值,当节点数超过这个值就需要把链表转换成树结构。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值