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

Why the internal implementation of HashSet creates dummy objects to insert as values in HashMap rather than inserting nulls?

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

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

刚看源码看到这段代码

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

public boolean remove(Object o) {
    return map.remove(o)==PRESENT;
}

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

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

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