HashMap是Java和Android程序员的基本功, JDK1.8对HashMap进行了优化, 你真正理解它了吗?
考虑如下问题:
1、哈希基本原理?(答:散列表、hash碰撞、链表、红黑树)
3、resize如何实现的, 记住已经没有rehash了!!!(答:拉链entry根据高位bit散列到当前位置i和size+i位置)
4、为什么获取下标时用按位与&,而不是取模%? (答:不只是&速度更快哦, 我觉得你能答上来便真正理解hashmap了)
5、什么时机执行resize?
答:hashmap实例里的元素个数大于threshold时执行resize(即桶数量扩容为2倍并散列原来的Entry)。 PS:threshold=桶数量*负载因子
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length; //初始化桶,默认16个元素
if ((p = tab[i = (n - 1) & hash]) == null) //如果第i个桶为空,创建Node实例
tab[i] = newNode(hash, key, value, null);
else { //哈希碰撞的情况, 即(n-1)&hash相等
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p; //key相同,后面会覆盖value
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); //红黑树添加当前node
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null); //链表添加当前元素
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash); //当链表个数大于等于7时,将链表改造为红黑树
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue; //覆盖key相同的value并return, 即不会执行++size
}
}
++modCount;
if (++size > threshold) //key不相同时,每次插入一条数据自增1. 当size大于threshold时resize
resize();
afterNodeInsertion(evict);
return null;
}
6、为什么负载因子默认为0.75f ? 能不能变为0.1、0.9、2、3等等呢?
答:0.75是平衡了时间和空间等因素; 负载因子越小桶的数量越多,读写的时间复杂度越低(极限情况O(1), 哈希碰撞的可能性越小); 负载因子越大桶的数量越少,读写的时间复杂度越高(极限情况O(n), 哈希碰撞可能性越高)。 0.1,0.9,2,3等都是合法值。
7、影响HashMap性能的因素?
1、 负载因子;
2、哈希值;理想情况是均匀的散列到各个桶。 一般HashMap使用String类型作为key,而String类重写了hashCode函数。
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
8、HashMap的key需要满足什么条件?
答:必须重写hashCode和equals方法, 常用的String类实现了这两个方法。
示例代码:
private static class KeyClass {
int age;
String name;
public boolean equals(Object anyObject) {
if (anyObject == this) {
return true;
}
if (anyObject instanceof KeyClass) {
KeyClass obj = (KeyClass) anyObject;
if (obj.age == this.age
&& obj.name == this.name) {
return true;
}
}
return false;
}
public int hashCode() {
return name==null? age : age|name.hashCode();
}
}
public static void main(String[] args) {
HashMap<KeyClass, String> map = new HashMap<>();
KeyClass obj1 = new KeyClass();
KeyClass obj2 = new KeyClass();
obj1.age = 1;
obj1.name = "Tom";
obj2.age = 2;
obj2.name ="Jack";
map.put(obj1, "