HashSet底层HashMap源码分析
在看HashSet源码的时候,意外发现底层HashMap保存的value居然不是null,而是保存一个Object作为Value。顿觉有悖常理,于是来分析一下:
HashSet的add方法:
public boolean add(E e) {return map.put(e, PRESENT)==null;}
可以看到这里调用底层HashMap的时候,往value里放了一个PRESENT,中文直译为“存在”,为什么叫PRESENT,很形象,待会解释。
我们再来看看PRESENT是什么:
private static final Object PRESENT = new Object();
PRESENT是一个类静态变量,也就是说整个类所有的value也就这么一个对象,而且这个对象根本没有任何意义。
带着问题去分析:
- 问题一:按理说,HashSet只需要判断是否重复(存在),压根就不需要存放value的呀,那么存放一个null不就好了?何必存一个没有意义的Object?这个问题需要解释问题二
- 问题二:为什么add方法需要返回值?返回值的意义是什么?
- 问题三:如果给value存null会发生什么?
先分析问题三,如果存了一个null,我们知道HashMap是允许null值的,那么我们去看看map.put方法,存null会怎么样:
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
/**
* Implements Map.put and related methods.
* @return previous value, or null if none
*/
看注释最后一行:返回这个key之前的value,如果这个key之前没有value,那么返回null。
如果我们HashSet存null进去,那么分析一下返回值:
- 如果HashSet中已有了这个key,比如Set中已经有了一个KV:("张三", null),又填进去一个KV:("张三", null),此时应当返回privous value,也就是null
- 如果HashSet中没有这个key,比如第一次添加KV:("张三", null),那么应当返回null
发现问题了,如果HashSet中把null存为value,那么无论set中是否已有重复,add方法都会返回null,那么add方法就无法检验是否重复。
问题二也顺带解决了,add方法返回值,就是判断HashMap.put方法返回是否为null,也就是判断add方法添加的新元素是否已经重复。
问题一也解决了,这个Object本身确实没有意义,但是它对我们Set有意义:
它唯一的意义就是,它不等于null呀。。。也就是说,只要你开心,你往里放个什么东西都行。(毕竟还是要考虑内存占用的)
现在你肯定知道为什么这个Object叫“存在”了吧?因为张麻子说过,你和钱对我都不重要,有你,对我很重要。