一直知道 HashMap 有默认的容量和加载因子,今天想看看源代码,希望能了解的更清楚一些。
我们先看看默认的构造器吧,以下为我本机的 JDK6.0 的源代码.
- /**
- * 默认的初始化的容量,必须是2的幂次数<br>
- * The default initial capacity - MUST be a power of two.
- */
- static final int DEFAULT_INITIAL_CAPACITY = 16;
-
- /**
- * 默认的加载因子
- */
- static final float DEFAULT_LOAD_FACTOR = 0.75f;
-
- /**
- * 下一个需要重新分配的尺寸值。等于容量乘以加载因子。<br>
- * 也就是说,一旦容量到了这个数值,将重新分配容器的尺寸。
- * The next size value at which to resize (capacity * load factor).
- * @serial
- */
- int threshold;
-
- public HashMap() {
- this.loadFactor = DEFAULT_LOAD_FACTOR;
- threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
- table = new Entry[DEFAULT_INITIAL_CAPACITY];
- init();
- }
从代码可以看出,默认的容量是16,而 threshold是16*0.75 = 12;
我们来看看增加的部分代码。
- public V put(K key, V value) {
- // 我们忽略掉这部分的代码,只看我们这里最关心的部分
- addEntry(hash, key, value, i); // 这里增加了一个Entry,我们看看代码
- return null;
- }
-
- void addEntry(int hash, K key, V value, int bucketIndex) {
- Entry<K,V> e = table[bucketIndex];
- table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
- if (size++ >= threshold) // 这里是关键,一旦大于等于threshold的数值
- resize(2 * table.length); // 将会引起容量2倍的扩大
- }
-
- 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]; // 新的容器空间
- transfer(newTable); // 复制数据过去
- table = newTable;
- threshold = (int)(newCapacity * loadFactor); // 重新计算threshold的值
- }
其中有一点,起始容量必须是2的幂次,这如何保证呢?我们来看看其构造方法
- public HashMap(int initialCapacity, float loadFactor) {
- // 忽略掉一部分代码....
-
- // Find a power of 2 >= initialCapacity
- // 重新查找不比指定数值大的最大的2的幂次数
- int capacity = 1;
- while (capacity < initialCapacity)
- capacity <<= 1;
- // 其它的初始化代码 ...
- }
总结:
相对准确的估算数据量,将极大的影响HashMap的性能,因为resize是一个重新分配的过程,耗时应该是里面最大的。
加载因子较小,会有更多的空间空闲,我不知道这个0.75是不是一个折中方案。也许0.9也是一个不错的选择,特别是那些数据量虽然很大,但不是经常变化的地方,比如公司人员,城市列表等相对比较固定的数据
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于