HashMap1.7源码

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

一些问题(自己瞎想的,并不是面试题)

  1. 为什么容量总是2的次幂?(构造函数带有capacity参数时,也变成最小的>=它的二次幂)?
    因为通过hash值得到在哈希表的下标,可以不用取余了。直接hash(key) & (length - 1);
  2. 什么时候分配table数组,它一开始是等于 (Entry<K, V>[]) EMPTY_TABLE 的?
  • inflateTable()或者resize()时分配table数组
  • 为啥不让它等于null呢???不知道不知道
  1. 用一个map初始化一个HashMap的时候,怎么做的?容量为什么刚开始那样设置?
    map1.putAll(map2) 将map2的所有键值对加入到map1中
    如果 mp2的capacity>该表的capacity 那就将表扩展成最接近且>=m.capcity的2次幂
    至于为什么不叠加呢:
    假如添加的key与原来的大量重叠,可能造成添加后的capacity是合适的capacity的2倍。所以保守起见,。。(大不了以后addEntry有需要的时候再resize)
  2. 什么时候需要resize()?怎么扩容?
  • putAll()方法 map1.put(map2)时,map1的capacity<min(MAXIMUM_CAPACITY,(int)(numKeysToBeAdded / loadFactor + 1))时,需要将map1的capacity扩到>=后者的2次幂
  • put的addEntry()方法中(size >= threshold) && (null != table[bucketIndex])
  • put()方法当key=null时,会调用私有方法putForNullKey(),显然这里也会调用addEntry()进而可能resize

  • new 一个Entry数组,容量为原来的2倍
  • 将map里面的键值对全部通过transfer函数放到新的Entry数组(tranfer第二个参数是initHashSeedAsNeeded())
  • 给table赋值为这个Entry数组,给threadshold赋值为newCapacity * loadFactor
  1. key是怎么hash的?什么时候需要rehash?
final int hash(Object k) {
    int h = hashSeed;
    if (0 != h && k instanceof String) {
        //hashSeed!=0 且k是字符串类型的时候 更换哈希方法
        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);
}
  • 不精确的来讲 capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD时候需要rehash.
  • Holder.ALTERNATIVE_HASHING_THRESHOLD可以在配置文件中指定,HashMap的静态局部类Holder的静态代码块会读取配置文件的该项的配置,-1代表禁用,不配置的话默认是Integer.MAX_VALUE,几乎永远不会超,所以几乎很少会rehash
  • rehash的影响就是hash(key)的时候hashseed变化了,导致hash(key)也变了。其次就是rehash且key是字符串派生类的话,要采用sun.misc.Hashing.stringHash32((String) k);这个哈希函数了,不知道是什么东东
  1. 说一下writeObject,readObject
  • writeObject 先是通过输出流的defaultWriteObject()方法将capacity,loadFactor写入输出流,然后再写入threshold,size,再遍历entrySet将键值对的key,value写入
  • readObject 先是通过defaultReadObject()得到capacity,loadFactor,然后计算出capacity(这里loadFactor<=0.25的话按0.25算,clone()也是这样,不知道为什么)。假如被克隆的map的sz=0直接给threshold赋值为capacity,否则inflateTable(capacity)。将所有键值对通过putForCreate(K key, V value)插入map。
  1. 说一下HashMap1.7里面一些巧妙地位运算?
static int indexFor(int h, int length) {
    return h & (length - 1);
}
//返回离number最近且>=number的2次幂
private static int roundUpToPowerOf2(int number) {
    // assert number >= 0 : "number must be non-negative";
    return number >= MAXIMUM_CAPACITY
            ? MAXIMUM_CAPACITY
            : (number > 1) ? Integer.highestOneBit((number - 1) << 1) : 1;
}
//Integer类的静态方法
public static int highestOneBit(int i) {
    // HD, Figure 3-1
    i |= (i >>  1);
    i |= (i >>  2);
    i |= (i >>  4);
    i |= (i >>  8);
    i |= (i >> 16);
    return i - (i >>> 1);
}
//jdk1.8将这个函数有所改动,并不需要扰动这么多,毕竟位运算也需要时间
final int hash(Object k) {
    int h = hashSeed;
    if (0 != h && k instanceof String) {
        //hashSeed!=0 且k是字符串类型的时候 更换哈希方法
        return sun.misc.Hashing.stringHash32((String) k);
    }
    h ^= k.hashCode();
    h ^= (h >>> 20) ^ (h >>> 12);
    return h ^ (h >>> 7) ^ (h >>> 4);
}
  1. 说一下put remove 头插 并发的时候?resize并发的时候会有什么危险呢?
  • createEntry()很显然并发不安全:
    可能多个线程同时执行 Entry<K, V> e = table[bucketIndex];,且他们的bucketIndex是相同的,就会存在两个Entry的next指向原来的table[bucketIndex],然后table[bucketIndex]=其中一个entry,这样就会导致丢失节点,且另一个entry一直有引用即它的next指向原来的table[bucketIndex],不能被回收。
void createEntry(int hash, K key, V value, int bucketIndex) {
    //下面的意思就是将该值插入table[bucketIndex]链表头部
    Entry<K, V> e = table[bucketIndex];
    //其中下面构造函数第4个形参意思是链表节点的next引用
    table[bucketIndex] = new Entry<>(hash, key, value, e);
    size++;
}
  • 显然 removeEntryForKey也会丢失节点
    在这里插入图片描述
  • resize()需要调用transfer()方法,很显然这个方法并发时可能会死循环或丢失节点
    死循环情况: 假如两个线程的e都走到了第一步,然后e1,e2指向同一entry,之后e1.next->新表的里面的table[i],table[i]就是e1了,第二个线程再继续执行,e2.next->table[i],而table[i]就是e1,e1和e2所指的对象一样,会导致它的next是它自己。。
    丢失节点情况: 很显然两个线程同时走到第2步,两个entry的next同时指向新表的table[i],而table[i]只能等与其中一个。。
    这大
  1. default权限的capacity loadFactor方法are used when serializing HashSets?
    挖坑 不知道

属性

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
static final int MAXIMUM_CAPACITY = 1 << 30;
static final float DEFAULT_LOAD_FACTOR = 0.75f;
static final Entry<?, ?>[] EMPTY_TABLE = {};
transient Entry<K, V>[] table = (Entry<K, V>[]) EMPTY_TABLE;
transient int size;
int threshold;
final float loadFactor;
transient int modCount;
static final int ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer.MAX_VALUE;
transient int hashSeed = 0;
private transient Set<Map.Entry<K, V>> entrySet = null;
private static final long serialVersionUID = 362498820763181265L;
//下面这两个是继承自AbstractMap的。
transient volatile Set<K>        keySet = null;
transient volatile Collection<V> values = null;

内部类

Holder

//包含一些在启动JVM之后才能初始化的值
private static class Holder {
    //table capacity大于这个值以及加上别的条件(如JVM启动 hashSeed==0等)
    // 决定要不要 use alternative hashing.(alternative hashing 就是根据一个别的类得到一个hashSeed值)
    static final int ALTERNATIVE_HASHING_THRESHOLD;

    static {
        //从配置中获取刚才说的jdk.map.althashing.threshold这个属性
        String altThreshold = java.security.AccessController.doPrivileged(
                new sun.security.action.GetPropertyAction(
                        "jdk.map.althashing.threshold"));
        int threshold;
        try {
            //大部分情况下都是空的
            //空的话就是默认值Integer.MAX_VALUE(这个值的话就永远用不到 永远不需要refresh(即重新哈希));
            threshold = (null != altThreshold) ? Integer.parseInt(altThreshold) : ALTERNATIVE_HASHING_THRESHOLD_DEFAULT;
            //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;//为配置中的值
    }
}

Entry< K,V>

//Entry<K,V>里面为何要存hash? 看getEntry(Object key)函数
static class Entry<K, V> implements Map.Entry<K, V> {
    final K key;
    V value;
    Entry<K, V> next;
    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;}

    //setValue会返回oldValue
    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;
    }

    //HashMap键值对的hashCode就是key的hashCode ^ value的hashCode
    public final int hashCode() {
        return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue());
    }

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

    //每当put(k,v)覆盖原来的值时,都会调用此方法。
    //是为了以后让Entry的子类(由于Entry是default权限,只能被同一个包里面的类继承)
    //重写该方法
    void recordAccess(HashMap<K, V> m) {
    }

    //This method is invoked whenever the entry is removed from the table.
    void recordRemoval(HashMap<K, V> m) {
    }
}

HashIterator < E>

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

    //遍历table找到第一个不为null的key
    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;
    }
	//返回当前的键值对,并将next置为下一个键值对
    final Entry<K, V> nextEntry() {
        //并发了,在刚new HashIterator之后,modCount又变了
        if (modCount != expectedModCount) throw new ConcurrentModificationException();
        Entry<K, V> e = next;
        if (e == null) throw new NoSuchElementException();
        //找到后,将next赋值为除了当前key下一次在hashmap里的key
        if ((next = e.next) == null) {
            Entry[] t = table;
            while (index < t.length && (next = t[index++]) == null) ;
        }
        current = e;
        return e;
    }
    //将current.key从hashMap里面删除。
    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;
    }
}

KeyIterator、ValueIterator、EntryIterator

//KeyIterator继承HashIterator 具有删除元素的特性
private final class KeyIterator extends HashIterator<K> {
    public K next() {return nextEntry().getKey();}
}

private final class ValueIterator extends HashIterator<V> {
    public V next() {return nextEntry().value;}
}

private final class EntryIterator extends HashIterator<Map.Entry<K, V>> {
    public Map.Entry<K, V> next() {return nextEntry();}
}

KeySet、Values、EntrySet

//KeySet这些方法以及继承AbstractSet的方法都在Set接口有定义
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();}
}

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();}
}

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();}
}

构造方法

public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)//初始化map容量小于0 则抛出参数不合法异常
        	throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)//大于(1<<30) 就改为最大容量
        	initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor)) //isNaN(0.0/0.0)==true 浮点数/0是不会异常的得到无穷大
        	throw new IllegalArgumentException("Illegal load factor: " + loadFactor);
        this.loadFactor = loadFactor;
        threshold = initialCapacity;//构造的时候,threshold先等于初始容量,inflateTable()才会得到真正的threshold
        init();
}
public HashMap(int initialCapacity) {
    this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
public HashMap() {
    this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}
public HashMap(Map<? extends K, ? extends V> m) {
    //m.size()<=capcity*load_factor ==> capacity>=m.size()/DEFAULT_LOAD_FACTOR
    //为什么要+1呢?我认为一方面和除法有关系
    this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
            DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
	//这个时候threshold=capacity(inflateTable()之后就会是真正的值)
    inflateTable(threshold);//这里传进去的参数threshold目前还是capacity
    putAllForCreate(m);
}

private 方法

roundUpToPowerOf2

//返回离number最近且>=number的2次幂
private static int roundUpToPowerOf2(int number) {
    // assert number >= 0 : "number must be non-negative";
    return number >= MAXIMUM_CAPACITY
            ? MAXIMUM_CAPACITY //highestOneBit得到最高位的权值,如1101得到8
            : (number > 1) ? Integer.highestOneBit((number - 1) << 1) : 1;
}

inflateTable

//实例化table
private void inflateTable(int toSize) {
	// Find a power of 2 >= toSize
	int capacity = roundUpToPowerOf2(toSize);
	//threshold此时才是真正的值(至于为啥与最大容量+1比较 是因为put的时候>=threshold且有哈希冲突)
	threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
	table = new Entry[capacity];//真正的分配table数组
	// (用到的时候才分配 因为有的人初始化后不用它了这不是浪费吗)
	initHashSeedAsNeeded(capacity);
}

getForNullKey、putForNullKey

private V getForNullKey() {
    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;
}
//未被覆盖时modCount++
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);//当put的key值存在时 应该调用这个方法。
            return oldValue;
        }
    }
    modCount++;
    addEntry(0, null, value, 0);
    return null;
}

putForCreate、putAllForCreate

 //在该类中只被putAllForCreate调用
 //该方法与put(K,V)区别时 调用该方法保证容量够用,不用判表是否空然后inflateTable
 //不用调用addEntry,直接createEntry,addEntry相比于后者要判断是否需要扩容(需要的话就扩容)
 private void putForCreate(K key, V value) {
     int hash = null == key ? 0 : hash(key);
     //key==null时hash=0; 0%length=0 自然哈希表下标也是0 key=null时存在tabl[0]
     int i = indexFor(hash, table.length);
     //key存在时 用新值代替之前的值 不存在时createEntry
     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);
 }

 //在该类中只被传入map的构造函数 clone函数调用
 private void putAllForCreate(Map<? extends K, ? extends V> m) {
     for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
         putForCreate(e.getKey(), e.getValue());
 }

default 方法

indexFor 、hash、initHashSeedAsNeeded

static int indexFor(int h, int length) {
    return h & (length - 1);
}
final int hash(Object k) {
    int h = hashSeed;
    if (0 != h && k instanceof String) {
        //hashSeed!=0 且k是字符串类型的时候 更换哈希方法
        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);
}
//判断是不是需要rehash(需要的话说明在该函数中HashSeed变了)
final boolean initHashSeedAsNeeded(int capacity) {
    //hashSeed默认为0 所以这个为false;
    boolean currentAltHashing = hashSeed != 0;
    //hashSeed只有在这个函数下面代码才会被改动 别的没有代码去改动hashSeed。
    boolean useAltHashing = sun.misc.VM.isBooted() &&
            (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
    //正常情况下JVM被启动(特殊情况当然我也不懂啦hh) 只要capacity>=定义的更改哈希方法的阈值时
    //这样useAltHashing=true 那么就会更改。
    boolean switching = currentAltHashing ^ useAltHashing;
    if (switching) {
        hashSeed = useAltHashing
                ? sun.misc.Hashing.randomHashSeed(this)
                : 0;
    }
    //意思就是在capacity不超过那个值时,hashSeed永远不会被改变
    // (系统配置文件不自定义对应属性的话) 这个函数永远返回false;
    return switching;
}

getEntry、addEntry、createEntry

//Entry的hash属性就是hash(Entry里面的key)
//hash(key)在table中对应的下标会有一条链,链中键值对的hash值等于hash(key)且key相等
//我觉得Entry里面存hash,就是为了这里判断。可是判断直接判断key相等不就行了吗?
//假如key是String类型,很长很长,大量的判equals浪费时间,先判一下hash做过滤条件
final Entry<K, V> getEntry(Object key) {
    if (size == 0) { return null; }
    int hash = (key == null) ? 0 : hash(key);
    for (Entry<K, V> e = table[indexFor(hash, table.length)];
         e != null;
         e = e.next) {
        Object k;
        if (e.hash == hash &&//键值对的hash=hash(key)
                ((k = e.key) == key || (key != null && key.equals(k))))
            return e;
    }
    return null;
}

//当size>=threshold且产生了哈希冲突(null!=table[bucketIndex]) 需要revize到原来的2倍
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) {
    //下面的意思就是将该值插入table[bucketIndex]链表头部
    Entry<K, V> e = table[bucketIndex];
    //其中下面构造函数第4个形参意思是链表节点的next引用
    table[bucketIndex] = new Entry<>(hash, key, value, e);
    size++;
}

resize、transfer

//扩容为什么新建一个哈希表就不用多说了吧。。
// 因为扩容后key所在哈希表的下标会变(不能说hash(key)会变 这样不准确 见transfer)
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];
    //这个函数就是真正的新建一个哈希表 即对newTable进行操作
    transfer(newTable, initHashSeedAsNeeded(newCapacity));
    table = newTable;
    threshold = (int) Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
}

//将oldTable里面的键值对挪到newTable中
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) {//这个值大部分情况都是0;
                e.hash = null == e.key ? 0 : hash(e.key);
            }
            //所以正常情况下(容量不过大时以及..) key的hash不会改变
            //表的容量会比之前变为了2倍。。然后index要么不变要么变成了原来的+length
            int i = indexFor(e.hash, newCapacity);
            //这个时候多线程操作会造成 链表循环。。
            e.next = newTable[i];
            newTable[i] = e;
            e = next;
        }
    }
}

removeEntryForKey、removeMapping(Map.Entry的派生类)

//传参key,删除对应的键值对
//没找到返回null 找到返回Entry<K,V>;
//被HashMap的remove(Object key)方法,HashIterator的remove()方法,KeySet的remove(Object o)调用
//key存在modCount++
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;
}

//传参键值对,删除键值对
//只在HashMap.EntrySet的public boolean remove(Object o);方法被调用
final Entry<K, V> removeMapping(Object o) {
    //o不是Map.Entry类或者它的派生类
    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;
}

capacity、loadFactor

。。。在HashMap里面下面俩方法没被使用

// These methods are used when serializing HashSets
int capacity() {
    return table.length;
}

float loadFactor() {
    return loadFactor;
}

writeObject、readObject

//threshold,loadfactor,capacity,size
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());
        }
    }
}

/*关于readObject()/ writeObject()是私有的,
这里是因为:如果你的类Bar扩展了一些类Foo; Foo还实现了readObject()/ writeObject(),
而Bar也实现了readObject()/ writeObject().现在,当Bar对象被序列化或反序列化时,
JVM需要自动为Foo和Bar调用readObject()/ writeObject()(即,不需要显式调用这些超类方法).
但是,如果这些方法不是私有的,那么它将成为方法重写,并且JVM不能再调用子类对象上的超类方法.
也就是防止子类瞎写。。
[
子类要是有transient属性的话,就有点凉凉,就直接为空了。可能根本不需要这个属性的值。
其实不实现这个属性也行,大不了就拿一个错误的(或者利用这个属性里面一些对的)。
(这样可能耗时间而已)
]
* */
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 --capacity
    s.readInt(); // ignored.
    // Read number of mappings --size
    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);
    }
}

init()

在HashMap构造,clone(),readObject()中用到,说白了就是让子类在这里做一些自己想做的事情。

void init() {
}

public 方法(对外API)

size、isEmpty、get、containsKey、containsValue

public int size() {
    return size;
}

public boolean isEmpty() {
    return size == 0;
}

public V get(Object key) {
    if (key == null) return getForNullKey();
    Entry<K, V> entry = getEntry(key);
    return null == entry ? null : entry.getValue();
}
public boolean containsKey(Object key) {
    return getEntry(key) != null;
}
//把哈希表以及对应的链全部遍历一遍 o(n)查找
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;
}

put、putAll、remove、clear

//oldValue没被覆盖时modCount++
//如key若存在 新的value替代oldValue 并返回oldValue; 不存在则返回null
public V put(K key, V value) {
    if (table == EMPTY_TABLE) {//实例化哈希表
        inflateTable(threshold);
    }
    if (key == null) return putForNullKey(value);
    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;
            //当put的key值存在时 应该调用这个方法。
            e.recordAccess(this);//在HashMap中是空方法,应该被子类重写。
            return oldValue;
        }
    }
    modCount++;
    addEntry(hash, key, value, i);
    return null;//key不存在时返回null
}

//map1.putAll(map2) 将map2的所有键值对加入到map1中
//如果 mp2的capacity>该表的capacity 那就将表扩展成最接近且>=m.capcity的2次幂
//至于为什么不叠加呢:
//假如添加的key与原来的大量重叠,可能造成添加后的capacity是合适的capacity
//的2倍。所以保守起见,。。(大不了以后addEntry有需要的时候再resize)
public void putAll(Map<? extends K, ? extends V> m) {
    int numKeysToBeAdded = m.size();
    if (numKeysToBeAdded == 0) return;
    //我觉得这里jdk作者可能写错了,,应该是 numKeysToBeAdded / loadFactor;
    if (table == EMPTY_TABLE) {
        inflateTable((int) Math.max(numKeysToBeAdded * loadFactor, threshold));
    }
    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());
}

//map里面不存在key的键值对,返回null,否则返回key对应的value
public V remove(Object key) {
    Entry<K, V> e = removeEntryForKey(key);
    return (e == null ? null : e.value);
}

//清空HashMap的时候,并不会table=EMPTY_TABLE,而是还用原来的数组,,不过每个数组元素成为了null.
public void clear() {
    modCount++;
    Arrays.fill(table, null);
    size = 0;
}

keySet、values、entrySet

//KeySet的方法(包含继承父类的方法)在Set接口都有定义,所以返回Set<K>类型,并不会让KeySet的某些功能不可使用
public Set<K> keySet() {
    Set<K> ks = keySet;
    return (ks != null ? ks : (keySet = new KeySet()));
}

public Collection<V> values() {
    Collection<V> vs = values;
    return (vs != null ? vs : (values = new Values()));
}

public Set<Map.Entry<K, V>> entrySet() {
    return entrySet0();//显然entrySet0别的方法也用到了,抽成一个private方法,减少代码融合
}
//这个就放在这写把。。
private Set<Map.Entry<K, V>> entrySet0() {
    Set<Map.Entry<K, V>> es = entrySet;
    return es != null ? es : (entrySet = new EntrySet());
}

clone

// 也就是说JavaDoc指明了 Object.clone() 有特殊的语义,
// 它就是能把当前对象的整个结构完全浅拷贝一份出来。
// 每层 clone() 都顺着 super.clone() 的链向上调用的话最终就会来到 Object.clone() ,
// 于是根据上述的特殊语义就可以有 x.clone().getClass() == x.getClass() 。
// 至于这是如何实现的,其实很简单。可以把JVM原生实现的 Object.clone() 的语义想像为
// 拿到 this 引用之后通过反射去找到该对象实例的所有字段,然后逐一字段拷贝。
// HotSpot VM中,Object.clone() 在不同的优化层级上有不同的实现。其中最不优化的版本是这样做的:
// 拿到 this 引用,通过对象头里记录的Klass信息去找出这个对象有多大,
// 然后直接分配一个新的同样大的空对象并且把Klass信息塞进对象头(这样就已经实现了 x.clone().getClass() == x.getClass() 这部分语义),
// 然后直接把对象体的内容看作数组拷贝一样从源对象“盲”拷贝到目标对象,bitwise copy。然后就完事啦。

//重写clone()方法
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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值