(本文基于JDK1.8)
前言
HashMap的知识点很多,分析完它的源码,自我感觉成长不少,我知道有部分源码还没有分析,包括JDK1.8新增加的添加元素的方法,判断是否某个Key对象的地方,判断是否包含某个Value对象的方法,还有红黑树结构(这块是软肋)
总结
1、HashMap实现了Map接口
2、它是一个非线程安全类,不能在多线程环境下使用
3、添加添加时,允许key对象与value对象为null
4、HashMap中的元素是完全无序的,遍历元素的时候会有影响
5、底层存储使用的是数组,因为HashMap对象持有了一个数组对象
6、每一个元素在数组的位置(下标)在HashMap中也称为桶(tong),比如哈希表的容量是10,那么就说是有10个桶(英文:bucket),这是因为哈希冲突时,可能有多个元素与数组下标相关
7、HashMap在JDK1.7中的存储结构是数组+单链表,在JDK1.8是数组+单链表+红黑树,引入红黑树的目的是为了解决单链表过长时,查找元素的时间复杂度为O(n)
8、单链表和红黑树的存在是为了解决哈希冲突(Key对象不同,但hashCode相同):即 key1 != key2,但key1.hashCode() == key2.hashCode()
9、JDK1.7中数组的每一个桶,指向的一直都是单链表的头结点(第一个结点),新结点都是放在头结点处(在JDK1.7中,新的结点永远做单链表的第一个结点,这是头插法);JDK1.8改了规则,采用尾插法,新结点作为最后一个节点
10、JDK1.8开始,当单链表的长度大于TREEIFY_THRESHOLD时,TREEIFY_THRESHOLD的值是8,但是我发现用的时候都-1了(因为当前插入的结点还没计算到总数中)
11、Entry结点类(Java1.7)在Java1.8中改为Node结点类,JDK1.7中的数组中的每一个元素是一个Entry对象,JDK1.8中则是Node对象
a、Java1.7的时候是Entry类
b、Java1.8的时候改叫为Node,实现了Entry
static class Node<K,V> implements Map.Entry<K,V> {
final int hash; //经过hash函数计算后hash值,根据数组长度与key的hashCode,进行位与运算,其实就是高效取模
final K key; //持有的key对象的引用
V value; //持有的value对象的引用
Node<K,V> next; //指向下一个结点对象的引用
//省略好多代码
}
HashMap类结构
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {
}
a、继承AbstractMap(它真正实现了Map接口中的通用方法)
b、实现Map ,HashMap代码阅读性显著提高(AbstractMap也实现Map接口)
c、实现Cloneable,支持clone对象
d、实现Seralizable,支持序列化
HashMap添加元素的方法
1、put(K key, V value) 传入key与value,插入一个元素
2、putAll(Map<? extends K, ? extends V> m) 把另外一个哈希表的所有元素合并进来,嘿嘿
3、putIfAbsent(K key, V value) Java1.8新增的哦,只有key不存在的时候,才会添加元素成功哦(就是说如果key已经存在,则不会覆盖掉原来的value)
4、computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
5、computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)
6、compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
HashMap删除元素的方法
1、remove(Object key) 干掉匹配key的元素,嘿嘿
2、remove(Object key, Object value) 干掉即匹配key、又匹配value的元素
3、clear() 干掉哈希表中的所有元素,欧耶
HashMap修改元素的方法
1、replace(K key, V value) 找到匹配的key,替换掉原来的Value,会返回旧的Value
2、replace(K key, V oldValue, V newValue) 找到匹配的key,同时匹配Value,然后替换成新的Value,返回值是boolean类型
3、replaceAll(BiFunction<? super K, ? super V, ? extends V> function) 这个,我懵逼了,注释怎么都么有?大伙儿baidu搜一下?
HashMap查找元素的方法
1、get(Object key) 根据key,返回value,欧耶
2、getOrDefault(Object key, V defaultValue) key存在,返回value,不存在,返回defaultValue(Java1.8新增)
HashMap 的2种遍历方式
1、Iterator对象……这是entry组成的Set
Map map = new HashMap();
Iterator iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
Object key = entry.getKey();
Object val = entry.getValue();
}
2、Key组成的Set
Map map = new HashMap();
Iterator iter = map.keySet().iterator();
while (iter.hasNext()) {
Object key = iter.next();
Object val = map.get(key);
}
HashMap的知识点很多,非常适合做面试题
1、数组的默认容量,用的2的幂(默认16,2的4次幂),因为用位运算(位与)替换了取模运算,为了性能,所以必须是2的N次方,数组的容量参与hash()方法的计算
2、加载因子:容量*加载因子 == 扩容阈值
3、扩容知识点,数组变大了,就要重新计算每一个key的hash值(这里注意,HashMap中没有直接使用key的hashCode()),称为resize(rehash)
4、查找速度无冲突O(1),有冲突O(n),大于等于8个结点时,为O(logn)
5、哈希函数设计