为什么HashSet的内部实现会创建虚拟对象以在HashMap中作为值插入而不是插入空值?

HashSet是使用HashMap实现的,当我们向HashSet添加任何e1时,如果e1不在集合中,它会在HashMap中添加(e1,new Object())。 我的问题是为什么他们插入新的Object(),当他们可以插入像(e1,null),这是更优化的方法,因为没有创建新的对象。 在这里插入空值有什么缺点吗?

每次将新键put映射时, HashSet都不会添加新的Object 。 它确实使用了Object ,但每次都使用相同的Object 。 此值在HashSet源代码中命名为PRESENT

add方法在内部HashMap上调用put(key, PRESENT)remove方法调用内部HashMap上的remove(key) ,但它必须返回一个boolean指示该键是否存在。 如果将null存储为值,则HashSet需要先调用containsKey ,然后remove ,以确定密钥是否存在 – 额外的开销。 这里,只有一个Object的内存开销,这是非常小的。

我只是查看了源代码并看到了这段代码

 public boolean add(E e) { return map.put(e, PRESENT)==null; } public boolean remove(Object o) { return map.remove(o)==PRESENT; } 

如果使用null而不是PRESENT ,这些将不起作用; 在每种情况下,都需要额外的步骤。

例如,如果将HashSet对象提供给ConcurrentSkipListSet构造函数,则它不能包含任何空值。