- HashMap底层是数组+链表+红黑树,HashSet底层即是HashMap,在前面《JAVA集合之HashSet》已记录过,故此只做一些分析。
- 调用HashMap()的无参构造方法时,会初始化加载因子,并且此时table数组是null。
public HashMap() {
this.loadFactor = 0.75F;
}
- 当第一次向HashMap中添加元素时,会扩容到16,元素会存放在HashMap$Node对象中。我们知道HashMap是K-V的形式,其实最终集合里面的元素是放在Entry<K,V>中的,其中,K是放在set集合中,V是放在connection集合中。因为Entry对象有getKey()和getValue()方法,所以放在Entry中只是为了方便遍历。但是真正的数据其实还在Node对象里,Entry只是一个引用。
- Node对象实现了Entry对象
static class Node<K, V> implements Entry<K, V> {
- 代码验证K存放于set集合,V存放于connection集合
public static void main(String[] args) {
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put("a","aa");
hashMap.put("b","bb");
hashMap.put("c","cc");
Set<Object> keySet = hashMap.keySet();
Collection<Object> values = hashMap.values();
}
- HashSet跟hashMap由于底层一致,所以可以总结出一些互推的关系
- HashSet集合里的值与HashMap集合的key值都不能重复,原因很简单,都是因为底层源码中的key是不允许重复的,HashSet的值是存放在key中,value只是存放了一个公用的new Object()对象占位。
- HashSet集合里的值与HashMap集合的key值都可以为null,但是只能有一个。并且存放顺序和取出顺序不一致,那当HashMap的key为null时,这对<K,V>是不是也会放在第一位(分析见前文《JAVA集合之HashSet》)。
- HashMap集合的value可以为null,可以重复,底层源码并没有对其进行限制。
- 扩容机制前文也提到过,这里也不赘述。有一点需要提一下,大家熟知的哈希冲突就是通过key进行哈希运算得到了一个下标值,并且在这个下标值的table数组上已经存放了其他元素。HashMap的解决方法是将新加的元素添加到该下标Node对象的后面形成链表,俗称拉链法。下图补充计划hash值和下标值的源码:
hash值
static final int hash(Object var0) {
int var1;
return var0 == null ? 0 : (var1 = var0.hashCode()) ^ var1 >>> 16;
}
下标值(var9即下标值,var8是第一次扩容table长度16,var1是前面计算出来的hash值)
final V putVal(int var1, K var2, V var3, boolean var4, boolean var5) {
HashMap.Node[] var6;
int var8;
if ((var6 = this.table) == null || (var8 = var6.length) == 0) {
var8 = (var6 = this.resize()).length;
}
Object var7;
int var9;
if ((var7 = var6[var9 = var8 - 1 & var1]) == null) {
var6[var9] = this.newNode(var1, var2, var3, (HashMap.Node)null);
}