咨询区
Royi Namir
为什么 List
是按照 2倍
扩容。
private void EnsureCapacity(int min)
{if (this._items.Length < min){int num = (this._items.Length == 0) ? 4 : (this._items.Length * 2);if (num < min){num = min;}this.Capacity = num;}
}
而 Dictionary<K,V>
是按 素数
扩容。
private void Resize()
{int prime = HashHelpers.GetPrime(this.count * 2);int[] numArray = new int[prime];for (int i = 0; i < numArray.Length; i++){numArray[i] = -1;}Entry<TKey, TValue>[] destinationArray = new Entry<TKey, TValue>[prime];Array.Copy(this.entries, 0, destinationArray, 0, this.count);for (int j = 0; j < this.count; j++){int index = destinationArray[j].hashCode % prime;destinationArray[j].next = numArray[index];numArray[index] = j;}this.buckets = numArray;this.entries = destinationArray;
}
为什么不都按照 2倍
来呢?这样还可以实现代码复用。
回答区
Gman
Dictionary
是启发式的,它需要保证 hashcode
必须准均匀的分布在各个桶中,.NET 的 Dictionary
采用的是素数来实现这个均衡,下面是计算桶索引的算法。
int num = this.comparer.GetHashCode(key) & 2147483647; // make hash code positive
// get the remainder from division - that's our bucket index
int num2 = this.buckets[num % ((int)this.buckets.Length)];
不过 Java 采用的和 .NET 中的 List 是一样的扩容机制。
resize(2 * table.length);
在翻倍之后,java 会重新计算 hashcode 值的。
static int hash(int h) {// 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);
}
static int indexFor(int h, int length) {return h & (length-1);
}// from put() method
int hash = hash(key.hashCode()); // get modified hash
int i = indexFor(hash, table.length); // trim the hash to the bucket count
List 不是启发式的,所以没有这么烦恼,
点评区
其实说到底,Dictionary采用素数,本质目的就是减少桶挂链,减少hashcode冲突,如果挂链过长,那就达不到 Contains,Add,Get 等操作的 O(1)
时间复杂度,这也就失去了 Dictionary 原本的特性。