public class HashMap<K, V>
extends AbstractMap<K, V>
implements Map<K, V>, Cloneable, Serializable
一些问题(自己瞎想的,并不是面试题)
- 为什么容量总是2的次幂?(构造函数带有capacity参数时,也变成最小的>=它的二次幂)?
因为通过hash值得到在哈希表的下标,可以不用取余了。直接hash(key) & (length - 1); - 什么时候分配table数组,它一开始是等于 (Entry<K, V>[]) EMPTY_TABLE 的?
- inflateTable()或者resize()时分配table数组
为啥不让它等于null呢???不知道不知道
- 用一个map初始化一个HashMap的时候,怎么做的?容量为什么刚开始那样设置?
map1.putAll(map2) 将map2的所有键值对加入到map1中
如果 mp2的capacity>该表的capacity 那就将表扩展成最接近且>=m.capcity的2次幂
至于为什么不叠加呢:
假如添加的key与原来的大量重叠,可能造成添加后的capacity是合适的capacity的2倍。所以保守起见,。。(大不了以后addEntry有需要的时候再resize) - 什么时候需要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
- 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);这个哈希函数了,不知道是什么东东
- 说一下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。
- 说一下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);
}
- 说一下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]只能等与其中一个。。
- 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;
}