通过开源软件学数据结构:
data store:
keys: {array of primitive | array of object}
values: {none | array of primitive | array of object} same size as keys
objects support : hashCode(), equals()
implemented types of keyTable:
{objectKeyTable: variable size Object[] array for keys |
intKeyTable: variable size int[] for keys |
longKeyTable: variable size long[] for keys }
implemented types of valueTable:
{
objectValueTable: variable size Object[] array for values |
intValueTable: variable size int[] for values |
longValueTable: variable size long[] for values
}
valueTable does not exist for sets or for object pools
hash index:
hashTable: fixed size int[] array for hash lookup into keyTable
linkTable: pointer to the next key ; size equal or larger than hashTable but equal to the valueTable
access count table:
{
none |variable size int[] array for access count
} same size as xxxKeyTable
HashIndex数组使用情况:
通过api对BaseHashMap的介绍可以看到他主要由KeyTable,ValueTable(包括int,long,object类型的Table,这里主要讨论int类型的),还有HashIndex中的HashTable,LinkTable四个类型的数组组成。
假设:IntKeyTable,ValueTable,LinkTable为length=6的数组,HashIndex为length=3的数组。
输入key的HashCode为:0,1,2,3,4,5
看下大概的原理图:
接下来通过插入这几个hashcode的值来看他hashmap的实现!
/**
* type-specific method for adding or removing keys in long or int->Object
* maps
*/
protected Object addOrRemove(long longKey, Object objectValue,Object objectValueTwo, boolean remove) {
int hash = (int) longKey;
int index = hashIndex.getHashIndex(hash); #1
int lookup = hashIndex.hashTable[index]; #2
int lastLookup = -1;
Object returnValue = null;
for (; lookup >= 0; lastLookup = lookup, lookup = hashIndex.getNextLookup(lookup)) {//根据LinkTable的顺序进行查找 #3
if (isIntKey) {
if (longKey == intKeyTable[lookup]) {
break;
}
} else {
if (longKey == longKeyTable[lookup]) {
break;
}
}
}
//如果找到的话lookup>=0
if (lookup >= 0) {
if (remove) {
if (longKey == 0) {
hasZeroKey = false;
zeroKeyIndex = -1;
}
if (isIntKey) {
intKeyTable[lookup] = 0;
} else {
longKeyTable[lookup] = 0;
}
returnValue = objectValueTable[lookup];
objectValueTable[lookup] = null;
hashIndex.unlinkNode(index, lastLookup, lookup);
if (isTwoObjectValue) {
objectKeyTable[lookup] = null;
}
if (accessTable != null) {
accessTable[lookup] = 0;
}
return returnValue;
}
if (isObjectValue) {
returnValue = objectValueTable[lookup];
objectValueTable[lookup] = objectValue;
}
if (isTwoObjectValue) {
objectKeyTable[lookup] = objectValueTwo;
}
if (accessTable != null) {
accessTable[lookup] = ++accessCount;
}
return returnValue;
}
// not found
if (remove) {
return returnValue;
}
if (hashIndex.elementCount >= threshold) {
if (reset()) {
return addOrRemove(longKey, objectValue, objectValueTwo, remove);
} else {
return null;
}
}
lookup = hashIndex.linkNode(index, lastLookup); #4
if (isIntKey) {
intKeyTable[lookup] = (int) longKey;
} else {
longKeyTable[lookup] = longKey;
}
if (longKey == 0) {
hasZeroKey = true;
zeroKeyIndex = lookup;
}
objectValueTable[lookup] = objectValue;
if (isTwoObjectValue) {
objectKeyTable[lookup] = objectValueTwo;
}
if (accessTable != null) {
accessTable[lookup] = ++accessCount;
}
return returnValue;
}
#1源码:
/**
* @param hash
*/
public int getHashIndex(int hash) {
return (hash & 0x7fffffff) % hashTable.length; #1.1
}
#1.1 就是根据HashTable的长度做了一个模运算!为了方便看结果所以上面假设HashTable的length是3!
#2就是根据获得的HashTable的索引获取该索引处的值,如果第一次获取的话是-1,如果不是第一次,便是具有相同index的link的第一个元素的值。
看下HashTable的初始化:
public void resetTables() {
int to = hashTable.length;
int[] intArray = hashTable;
while (--to >= 0) {
intArray[to] = -1; //初始化为-1
}
newNodePointer = 0;
elementCount = 0;
reclaimedNodePointer = -1;
modified = false;
}
初始状态,HashTable的值都是-1.
#3的源码:
/**
* This looks from a given node, so the parameter is always > -1.
*
* @param lookup A valid node to look from
* @return either -1 or the next node from this node
*/
public int getNextLookup(int lookup) {
return linkTable[lookup];
}
#4是在没有找到相同的内容,并且元素数量没有超过map的最大容量的情况下插入的:
/**
* Link a new node to the end of the linked for a hash index.
*
* @param index an index into hashTable
* @param lastLookup either -1 or the node to which the new node will be linked
* @return the new node
*/
public int linkNode(int index, int lastLookup) {
// get the first reclaimed slot
int lookup = reclaimedNodePointer; #5
if (lookup == -1) {
lookup = newNodePointer++; #6
} else {
// reset the first reclaimed slot
reclaimedNodePointer = linkTable[lookup]; #7
}
// link the node
if (lastLookup == -1) {
hashTable[index] = lookup; #8
} else {
linkTable[lastLookup] = lookup; #9
}
linkTable[lookup] = -1; #10
elementCount++; #11
modified = true;
return lookup;
}
#5 reclaimedNodePointer初始化是-1,表示没有可循环利用的空间链表,在没有空闲链表的情况下执行了#6,newNodePointer的值是elementCount的值是一致的。
如果不是第二个插入元素的话,便会执行#9将当前的IntKeyTable的index值放入LinkTable中索引为lastLookup的值中。并且设置这个队列的末尾表示符,通过#10来完成的。然后IntKeyTable中的元素加一(#11)。