IdentityHashMap是一个特殊的HashMap,它允许非引用相等的key,即使他们equals结果是true。
和HashMap HashTable不同的是,它是简单的对象数组结果,一个map对象占两个数组slot,一个放key一个放value,十分简单粗暴。
先看无参构造器:
/**
* Constructs a new, empty identity hash map with a default expected
* maximum size (21).
*/
public IdentityHashMap() {
init(DEFAULT_CAPACITY);
}
private void init(int initCapacity) {
// assert (initCapacity & -initCapacity) == initCapacity; // power of 2
// assert initCapacity >= MINIMUM_CAPACITY;
// assert initCapacity <= MAXIMUM_CAPACITY;
threshold = (initCapacity * 2)/3;
table = new Object[2 * initCapacity];
}
看一下key相等性的判断:
put方法():
public V put(K key, V value) {
Object k = maskNull(key);
Object[] tab = table;
int len = tab.length;
// index也是通过hash算法来寻址的。
int i = hash(k, len);
Object item;
while ( (item = tab[i]) != null) {
// 注意这里的key相等性判断是用 == ,不是equals 只有是同一个对象才能算是重复的key
if (item == k) {
@SuppressWarnings("unchecked")
V oldValue = (V) tab[i + 1];
tab[i + 1] = value;
return oldValue;
}
i = nextKeyIndex(i, len);
}
modCount++;
// 这里就可以明显看出,一个slot存key,后续一个存value。
tab[i] = k;
tab[i + 1] = value;
if (++size >= threshold)
resize(len); // len == 2 * current capacity.
return null;
}
IdentityHashMap的核心实现似乎在于hash算法,它负责添加和查询时的寻址。
/**
* Returns index for Object x.
*/
private static int hash(Object x, int length) {
int h = System.identityHashCode(x);
// Multiply by -127, and left-shift to use least bit as part of hash
return ((h << 1) - (h << 8)) & (length - 1);
}
它的迭代封装,把key和value封装到Entry对象中:
private abstract class IdentityHashMapIterator<T> implements Iterator<T> {
int index = (size != 0 ? 0 : table.length); // current slot.
int expectedModCount = modCount; // to support fast-fail
int lastReturnedIndex = -1; // to allow remove()
boolean indexValid; // To avoid unnecessary next computation
Object[] traversalTable = table; // reference to main table or copy
public boolean hasNext() {
Object[] tab = traversalTable;
// i+=2
for (int i = index; i < tab.length; i+=2) {
Object key = tab[i];
if (key != null) {
index = i;
return indexValid = true;
}
}
index = tab.length;
return false;
}
protected int nextIndex() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (!indexValid && !hasNext())
throw new NoSuchElementException();
indexValid = false;
lastReturnedIndex = index;
index += 2;
return lastReturnedIndex;
}
public void remove() {
if (lastReturnedIndex == -1)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
expectedModCount = ++modCount;
int deletedSlot = lastReturnedIndex;
lastReturnedIndex = -1;
// back up index to revisit new contents after deletion
index = deletedSlot;
indexValid = false;
// Removal code proceeds as in closeDeletion except that
// it must catch the rare case where an element already
// seen is swapped into a vacant slot that will be later
// traversed by this iterator. We cannot allow future
// next() calls to return it again. The likelihood of
// this occurring under 2/3 load factor is very slim, but
// when it does happen, we must make a copy of the rest of
// the table to use for the rest of the traversal. Since
// this can only happen when we are near the end of the table,
// even in these rare cases, this is not very expensive in
// time or space.
Object[] tab = traversalTable;
int len = tab.length;
int d = deletedSlot;
Object key = tab[d];
tab[d] = null; // vacate the slot
tab[d + 1] = null;
// If traversing a copy, remove in real table.
// We can skip gap-closure on copy.
if (tab != IdentityHashMap.this.table) {
IdentityHashMap.this.remove(key);
expectedModCount = modCount;
return;
}
size--;
Object item;
for (int i = nextKeyIndex(d, len); (item = tab[i]) != null;
i = nextKeyIndex(i, len)) {
int r = hash(item, len);
// See closeDeletion for explanation of this conditional
if ((i < r && (r <= d || d <= i)) ||
(r <= d && d <= i)) {
// If we are about to swap an already-seen element
// into a slot that may later be returned by next(),
// then clone the rest of table for use in future
// next() calls. It is OK that our copy will have
// a gap in the "wrong" place, since it will never
// be used for searching anyway.
if (i < deletedSlot && d >= deletedSlot &&
traversalTable == IdentityHashMap.this.table) {
int remaining = len - deletedSlot;
Object[] newTable = new Object[remaining];
System.arraycopy(tab, deletedSlot,
newTable, 0, remaining);
traversalTable = newTable;
index = 0;
}
tab[d] = item;
tab[d + 1] = tab[i + 1];
tab[i] = null;
tab[i + 1] = null;
d = i;
}
}
}
}
private class EntryIterator extends IdentityHashMapIterator<Map.Entry<K,V>>{
private Entry lastReturnedEntry = null;
public Map.Entry<K,V> next() {
// 私有内部类Entry
lastReturnedEntry = new Entry(nextIndex());
return lastReturnedEntry;
}
// ...
private class Entry implements Map.Entry<K,V> {
private int index;
private Entry(int index) {
this.index = index;
}
// ...
}
}
网友评论