HashMap源码解析

来源:互联网 发布:无基础学编程看什么书 编辑:程序博客网 时间:2024/05/22 17:47

疑问:.Set和Map之间是怎么匹配的,Map中包含Set

一、定义

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

扩展自AbstractMap,
重新实现Map
实现Cloneable,浅克隆
实现Serializable,序列化

Map定义了一些基本的map方法

public interface Map<K,V> {    //返回的set与原HashMap有映射关系,所以修改其中任意一个,另外一个也会改变    Set<K> keySet();    //返回的Collection与原HashMap有映射关系,所以修改其中任意一个,另外一个也会改变    Collection<V> values();    //返回的set与原HashMap有映射关系,所以修改其中任意一个,另外一个也会改变    Set<Map.Entry<K, V>> entrySet();    //接口中定义接口    interface Entry<K,V> {        K getKey();        V getValue();        V setValue(V value);        boolean equals(Object o);        int hashCode();    }}

AbstractMap抽象类

/** * 包括两个内部类,这两个类都是key-value类Entry的简单实现 * 其中SimpleEntry可以用于setValue值,SimpleImmutableEntry不可以 * 这两个都是为子类准备的 *  * 当用到Map类中的内部类Entry时,有的是调用Map.Entry,有的直接调用Entry *  * containsValue,get、remove等方法都是通过entrySet()的迭代器为基础进行操作的 *  * equals比较时不会比较排列顺序,在有顺序的子类Map中应该会覆盖此类方法 *  * 发现泛型中调用内部类Entry,直接调用或者通过Map.Entry都可以 */public abstract class AbstractMap<K,V> implements Map<K,V> {    /**     * Sole constructor.  (For invocation by subclass constructors, typically     * implicit.)     */    protected AbstractMap() {    }    // Query Operations    //返回entrySet().size()    public int size() {        return entrySet().size();    }    //此实现所需的时间与映射的大小呈线性关系    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();                //通过equals来比较                if (value.equals(e.getValue()))                    return true;            }        }        return false;    }     public V put(K key, V value) {        throw new UnsupportedOperationException();    }    public void putAll(Map<? extends K, ? extends V> m) {        for (Map.Entry<? extends K, ? extends V> e : m.entrySet())            put(e.getKey(), e.getValue());    }    // transient 自定义序列化    // volatile 线程相关,保证原子性    // 查看HashMap后,明白这两变量是用来存放keySet,values值    transient volatile Set<K>        keySet = null;    transient volatile Collection<V> values = null;    /**     * 返回的set与Map有映射关系,修改其中一方对另一方都会造成影响     * 返回的是一个set<key>只有key值,但set.iterator利用的却是set<key,value>,这样做的原因?     *      * 实际存储是以映射关系存储的,以Entry来封装,     *      * 方法中内部类size,contains等方法交给外部类处理     */    public Set<K> keySet() {        if (keySet == null) {            keySet = new AbstractSet<K>() {                public Iterator<K> iterator() {                    return new Iterator<K>() {                        //private 修饰                        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);                }            };        }        return keySet;    }    public abstract Set<Entry<K,V>> entrySet();    //没有比较元素之间的顺序    public boolean equals(Object o) {        if (o == this)            return true;        if (!(o instanceof Map))            return false;        Map<K,V> m = (Map<K,V>) o;        if (m.size() != size())            return false;        try {            Iterator<Entry<K,V>> i = entrySet().iterator();            while (i.hasNext()) {                //有的地方用Map.Entry有的地方用Entry                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??        //classCast应该是o转换时校验?NullPointer呢?在调用方法中没体现啊        } catch (ClassCastException unused) {            return false;        } catch (NullPointerException unused) {            return false;        }        return true;    }    /**     * 浅克隆都算不上,子类应该会覆盖此方法     * 翻到HashMap中查看,果然如此     */    protected Object clone() throws CloneNotSupportedException {        AbstractMap<K,V> result = (AbstractMap<K,V>)super.clone();        result.keySet = null;        result.values = null;        return result;    }     public static class SimpleEntry<K,V>        implements Entry<K,V>, java.io.Serializable    {}    public static class SimpleImmutableEntry<K,V>        implements Entry<K,V>, java.io.Serializable    {}

二、底层原理
采用线性哈希、链表散列。
每个key和value的映射通过Entry类来体现。

    /**     * Entry作为单向链表的元素,多个链表组成HashMap的存储数据结构     */    static class Entry<K,V> implements Map.Entry<K,V> {        final K key;        V value;        Entry<K,V> next;//指向next Entry        int hash;//哈希值        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;        }        //key和value都相等时,equals返回true        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 (key==null   ? 0 : key.hashCode()) ^                   (value==null ? 0 : value.hashCode());        }        public final String toString() {            return getKey() + "=" + getValue();        }        /**         * 执行put操作后执行此方法,但方法中并没哟具体操作         * 为子类保留的方法         */        void recordAccess(HashMap<K,V> m) {        }        /**         * 执行remove操作后执行此方法,但方法中并没哟具体操作.         * 为子类保留的方法         */        void recordRemoval(HashMap<K,V> m) {        }    }

三、构造器,基本field

/**     * 默认长度16,长度应为2的幕,有利于提高效率,在哪提高效率了呢,     */    static final int DEFAULT_INITIAL_CAPACITY = 16;    static final int MAXIMUM_CAPACITY = 1 << 30;    /**     * 默认负载因子     */    static final float DEFAULT_LOAD_FACTOR = 0.75f;    /**     * 存放数据的载体,transient     * 每个Entry存放的都是链表中的第一个first元素     */    transient Entry<K,V>[] table;    transient int size;    /**     * 下次扩展临界值,阈值     */    int threshold;    /**     * 负载因子     */    final float loadFactor;    /**     * 迭代器防止并发错误     */    transient int modCount;    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);        // 容量只能是2的幕        int capacity = 1;        while (capacity < initialCapacity)            capacity <<= 1;        this.loadFactor = loadFactor;        //阈值取两者中较小的,出现MAXIMUM_CAPACITY+1,是担心负载因子过大        threshold = (int)Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);        table = new Entry[capacity];        useAltHashing = sun.misc.VM.isBooted() &&                (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);        init();    }    public HashMap(int initialCapacity) {        this(initialCapacity, DEFAULT_LOAD_FACTOR);    }    public HashMap() {        this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);    }    /**     * 容量最小为16,负载因子位默认值     */    public HashMap(Map<? extends K, ? extends V> m) {        this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,                      DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);        putAllForCreate(m);    }    // internal utilities    /**     * 为子类留的钩子     */    void init() {    }

四,put,remove,get
先来看几个基本的方法

    /**     * 获得k的hash值     */    final int hash(Object k) {        int h = 0;        if (useAltHashing) {            if (k instanceof String) {                return sun.misc.Hashing.stringHash32((String) k);            }            h = hashSeed;        }        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,length获得index值     */    static int indexFor(int h, int length) {        return h & (length-1);    }    /**     * 扩充容量必须为2的次方     * 容量已经为最大值时,设置阈值为maxIntger,以容纳更多Entry     */    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;        //将Entrys转移到newTable中        transfer(newTable, rehash);        table = newTable;        //改变阈值        threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);    }    /**     * 将Entrys转移到newTable中     */    void transfer(Entry[] newTable, boolean rehash) {        int newCapacity = newTable.length;        //迭代器迭代期间update元素不会抛出异常        for (Entry<K,V> e : table) {            while(null != e) {                //保留next                Entry<K,V> next = e.next;                if (rehash) {                    e.hash = null == e.key ? 0 : hash(e.key);                }                //为每个Entry获取新的index                int i = indexFor(e.hash, newCapacity);                //e插入index为i的链表头                e.next = newTable[i];                newTable[i] = e;                e = next;            }        }    }

indexFor的计算方法十分精妙,
&运算:两个操作数中位都为1,结果才为1,否则结果为0.
length为2的N次幕,二进制为1000**00,length-1,二进制为111**11。
当h<=length-1时,h & (length-1) = h;
当h>length-1时,大于length-1的高位全部&运算为0,低位再进行&运算(比如h=101011,length-1=1111,h&(length-1) = 1011 & 1111),即h & (length-1) = h % (length-1)
hash()方法中的计算公式,有兴趣的可以参考http://www.iteye.com/topic/709945

     public V get(Object key) {        if (key == null)            return getForNullKey();        Entry<K,V> entry = getEntry(key);        return null == entry ? null : entry.getValue();    }    private V getForNullKey() {        //null key的hash,index一定为0        for (Entry<K,V> e = table[0]; e != null; e = e.next) {            if (e.key == null)                return e.value;        }        return null;    }    final Entry<K,V> getEntry(Object key) {        //计算其hash        int hash = (key == null) ? 0 : hash(key);      //求出index值,获取子链表中的第一个Entry,作为循环的初始变量        for (Entry<K,V> e = table[indexFor(hash, table.length)];             e != null;             e = e.next) {            Object k;            //此处判断key和e.key是否相等时,用了((k = e.key) == key || key.equals(k))            //很好理解,比如key是String类型(String覆盖equals方法),e.key与key的内存地址不同,但equsls方法返回true            //hashMap比较两个对象时,==和equals有一个相等,两个对象即相等            if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))                return e;        }        return null;    }    public V put(K key, V value) {        if (key == null)            return putForNullKey(value);        //获取hash值        int hash = hash(key);        //获取index值        int i = indexFor(hash, table.length);        //通过子链表的第一个Rntry向后遍历        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;                //执行recordAccess                e.recordAccess(this);                return oldValue;            }        }        modCount++;        //链表后添加元素        addEntry(hash, key, value, i);        return null;    }    /**     * Offloaded version of put for null keys     */    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;    }    /**     * This method is used instead of put by constructors and     * pseudoconstructors (clone, readObject).  It does not resize the table,     * check for comodification, etc.  It calls createEntry rather than     * addEntry.     */    private void putForCreate(K key, V value) {        int hash = null == key ? 0 : hash(key);        int i = indexFor(hash, table.length);        /**         * Look for preexisting entry for key.  This will never happen for         * clone or deserialize.  It will only happen for construction if the         * input Map is a sorted map whose ordering is inconsistent w/ equals.         */        for (Entry<K,V> e = table[i]; e != null; e = e.next) {            Object k;            if (e.hash == hash &&                ((k = e.key) == key || (key != null && key.equals(k)))) {                e.value = value;                return;            }        }        createEntry(hash, key, value, i);    }    private void putAllForCreate(Map<? extends K, ? extends V> m) {        for (Map.Entry<? extends K, ? extends V> e : m.entrySet())            putForCreate(e.getKey(), e.getValue());    }    public void putAll(Map<? extends K, ? extends V> m) {        int numKeysToBeAdded = m.size();        if (numKeysToBeAdded == 0)            return;        /*         * Expand the map if the map if the number of mappings to be added         * is greater than or equal to threshold.  This is conservative; the         * obvious condition is (m.size() + size) >= threshold, but this         * condition could result in a map with twice the appropriate capacity,         * if the keys to be added overlap with the keys already in this map.         * By using the conservative calculation, we subject ourself         * to at most one extra 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);        }        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);    }    /**     * 删除元素后,没有进行容量更新操作     */    final Entry<K,V> removeEntryForKey(Object key) {        int hash = (key == null) ? 0 : hash(key);        int i = indexFor(hash, table.length);        //定义前一个链表元素        Entry<K,V> prev = table[i];        //定义当前链表元素,初始时间prev与e为同一个值        Entry<K,V> e = prev;        //从first元素开始,遍历链表,找到key相等的元素后执行删除操作        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--;                //prev=e时,为first元素,直接更新table[inded]                if (prev == e)                    table[i] = next;                else                    prev.next = next;                //remove后执行recordRemoval                e.recordRemoval(this);                return e;            }            //更新 prev            prev = e;            //更新e            e = next;        }        return e;    }    void addEntry(int hash, K key, V value, int bucketIndex) {        //先判断,是否进行expend容量操作        if ((size >= threshold) && (null != table[bucketIndex])) {            resize(2 * table.length);            //expand容量后重新计算            hash = (null != key) ? hash(key) : 0;            //expand容量后重新计算index值            bucketIndex = indexFor(hash, table.length);        }        createEntry(hash, key, value, bucketIndex);    }    /**     * Like addEntry except that this version is used when creating entries     * as part of Map construction or "pseudo-construction" (cloning,     * deserialization).  This version needn't worry about resizing the table.     *     * Subclass overrides this to alter the behavior of HashMap(Map),     * clone, and readObject.     */    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自增        size++;    }

四,keySet,values,entrySet
HashMap提供了三个迭代器,分别用于生成keySet,values,Entry

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;                //将next指向第一个不为null的链表                //current没有赋值哦                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;    /**     * 返回的set与hashMap存在映射关系,修改其中一个,另一个也会改变     * keySet在哪里被初始化赋值的呢?     * 返回的set不支持add和addAll操作,什么原因     */    public Set<K> keySet() {        Set<K> ks = keySet;        //尚未赋值为null时,直接赋值并返回        return (ks != null ? ks : (keySet = new KeySet()));    }    //都是调用HashMap中的方法,很像代理    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();        }    }    //同keySet一致    public Collection<V> values() {        Collection<V> vs = values;        return (vs != null ? vs : (values = new Values()));    }    //remove方法继承自AbstractCollection,只删除了value,没有删除映射的Entry,    //原来AbstractCollection中的remove方法时先获得iterator,然后通过iterator删除,    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();        }    }    /**     * Returns a {@link Set} view of the mappings contained in this map.     * The set is backed by the map, so changes to the map are     * reflected in the set, and vice-versa.  If the map is modified     * while an iteration over the set is in progress (except through     * the iterator's own <tt>remove</tt> operation, or through the     * <tt>setValue</tt> operation on a map entry returned by the     * iterator) the results of the iteration are undefined.  The set     * supports element removal, which removes the corresponding     * mapping from the map, via the <tt>Iterator.remove</tt>,     * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt> and     * <tt>clear</tt> operations.  It does not support the     * <tt>add</tt> or <tt>addAll</tt> operations.     *     * @return a set view of the mappings contained in this map     */    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();        }    }

五,serialize

private void writeObject(java.io.ObjectOutputStream s)        throws IOException    {        Iterator<Map.Entry<K,V>> i =            (size > 0) ? entrySet0().iterator() : null;        // Write out the threshold, loadfactor, and any hidden stuff        s.defaultWriteObject();        // Write out number of buckets        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()) {                //key,value依次写入                s.writeObject(e.getKey());                s.writeObject(e.getValue());            }        }    }    private static final long serialVersionUID = 362498820763181265L;    /**     * Reconstitute the {@code HashMap} instance from a stream (i.e.,     * deserialize it).     */    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 hashSeed (can only happen after VM boot)        Holder.UNSAFE.putIntVolatile(this, Holder.HASHSEED_OFFSET,                sun.misc.Hashing.randomHashSeed(this));        // Read in number of buckets and allocate the bucket array;        s.readInt(); // ignored        // Read number of mappings        int mappings = s.readInt();        if (mappings < 0)            throw new InvalidObjectException("Illegal mappings count: " +                                               mappings);        //重新计算容量        int initialCapacity = (int) Math.min(                // capacity chosen by number of mappings                // and desired load (if >= 0.25)                //这样做的原因??                mappings * Math.min(1 / loadFactor, 4.0f),                // we have limits...                HashMap.MAXIMUM_CAPACITY);        int capacity = 1;        // find smallest power of two which holds all mappings        while (capacity < initialCapacity) {            capacity <<= 1;        }        table = new Entry[capacity];        //重新计算负载因子        threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);        useAltHashing = sun.misc.VM.isBooted() &&                (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);        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);        }    }

阅读源码时,如果有一定的算法基础就事半功倍。
阅读AbstractMap的其他子类后,相互之间比较区分一下。

0 0
原创粉丝点击