HashMap 简析
最近要准备面试,好多东西都忘了。有时间写点博客方便回头看。
数据结构
HashMap采用数组+哈希表+链表的方式来储存键值对。在jdk1.8发布后,hashmap的链表长度大于一定值过后,变成红黑树
源码解析(JDK1.7)
1.HashMap初始化
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // 默认容量 16
static final int MAXIMUM_CAPACITY = 1 << 30;//最大容量
static final float DEFAULT_LOAD_FACTOR = 0.75f;//默认加载因子,影响HashMap扩容(越大扩容越晚)
int threshold;//扩容阀值(当HashMap的size大于等于他时扩容)
final float loadFactor;//加载因子
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
threshold = initialCapacity;
init();
}
HashMap的构造方法很简单,就是将容量和加载因子赋值,init()中没有代码。
2.HashMap添加元素(保证数组长度为2的次方)
public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);//table数组初始化
}
if (key == null)
return putForNullKey(value);//null key特殊处理
int hash = hash(key);//hash
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;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
private void inflateTable(int toSize) {
//保证数组长度为2的次方
int capacity = roundUpToPowerOf2(toSize);
threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
table = new Entry[capacity];
initHashSeedAsNeeded(capacity);
}
private static int roundUpToPowerOf2(int number) {
//Integer.highestOneBit()
//如果一个数是0, 则返回0;
//如果是负数, 则返回 -2147483648:
//如果是正数, 返回的则是跟它最靠近的比它小的2的N次方
return number >= MAXIMUM_CAPACITY
? MAXIMUM_CAPACITY
: (number > 1) ? Integer.highestOneBit((number - 1) << 1) : 1;
}
static int indexFor(int h, int length) {
// 保证length是2的次方后,这样计算比取余更高效
return h & (length-1);
}
//添加元素
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) {
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<>(hash, key, value, e);
size++;
}
HashMap通过Integer.highestOneBit((number - 1) << 1)得出2的N次方来保证数组的长度是2的N次方。
所以我们给HashMap传入初始容量时也最好是2的N次方。
3.HashMap扩容
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, initHashSeedAsNeeded(newCapacity));
table = newTable;
threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
}
项目 | 时间 | 数量 |
---|---|---|
JAVA基础 | LL | 1 |
JAVA多线程 JVM JMM | LL | 2 |
中间件框架 | LL | 3 |
数据库 | LL | 4 |