数据结构
- JDK7中的HashMap采用数组+链表的结构来存储数据
- JDK8中的HashMap采用数组+链表或树的结构来存储数据
初始化
- HashMap默认散列数组多大?
答:16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
- 若new HashMap(20),散列数组大小是?
答:32
private void inflateTable(int toSize) {
// Find a power of 2 >= toSize
int capacity = roundUpToPowerOf2(toSize);
...
table = new Entry[capacity];
...
注:HashMap 的散列数组大小一定是2的幂,如果 new 的时候指定了容量且不是2的幂,实际容量会是最接近(大于)指定容量的2的幂。
- HashMap 什么时候开辟散列数组占用内存?
答:HashMap在new后并不会立即分配散列数组,而是第一次put 时初始化
类似的有,ArrayList也在第一次add时分配空间
public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
...
扩容
- 何时自动扩容?
答:HashMap在put的元素数量大于 Capacity * LoadFactor 之后会进行扩容
对默认散列数组,首次扩容在当size=16 * 0.75时做put操作前
void addEntry(int hash, K key, V value, int bucketIndex) {
if ((size >= threshold) && (null != table[bucketIndex])) {
resize(2 * table.length);
哈希算法
对象的equals重写规范:
答:HashMap对hashCode的协议要求,equals相同的对象必须具有相等的hashcode,反之不要求若使用object作为key,而其hashcode可能随着属性value改变而改变,怎么保障不丢数据?
答:可替换为IdentityHashMap,它使用==来做key的唯一判断
非并发安全问题
- 并发使用存在什么风险?
答:并发resize进行transfer的时候可能形成循环链表
注:可参考《疫苗:Java HashMap的死循环》