为什么 Sun Java 中的 HashSet 实现使用 HashMap 作为其后盾?

2022-08-31 22:36:18

看看 Java 6 的源代码,实际上是使用 来实现的,在 Set 的每个条目上使用虚拟对象实例。HashSet<E>HashMap<E,Object>

我认为这浪费了4字节(在32位机器上)的条目本身的大小。

但是,为什么它仍然被使用?除了更容易维护代码之外,还有什么理由使用它吗?


答案 1

实际上,这不仅仅是.Java 6 中接口的所有实现都基于底层 .这不是一项要求;这是一项要求。这只是实现的方式。您可以通过查看 Set 的各种实现的文档来亲眼看看。HashSetSetMap

您的主要问题是

但是,为什么它仍然被使用?除了更容易维护代码之外,还有什么理由使用它吗?

我认为代码维护是一个很大的激励因素。防止重复和膨胀也是如此。

Set并且是类似的接口,因为不允许重复的元素。(我认为唯一没有 a 支持的是 ,这是一个不寻常的集合,因为它是不可变的。MapSetMapCopyOnWriteArraySet

具体说来:

Set 的文档:

不包含重复元素的集合。更正式地说,集合不包含一对元素 e1 和 e2 使得 e1.等于(e2),并且最多有一个 null 元素。顾名思义,这个接口对数学集合抽象进行建模。

Set 接口除了从集合接口继承的约定之外,还对所有构造函数的协定以及 add、equals 和 hashCode 方法的协定进行了其他规定。为方便起见,此处还包括其他继承方法的声明。(这些声明附带的规范已针对 Set 接口进行了定制,但它们不包含任何其他规定。

毫不奇怪,关于构造函数的附加规定是,所有构造函数都必须创建一个不包含重复元素的集合(如上定义)。

地图

将键映射到值的对象。映射不能包含重复的键;每个键最多可以映射到一个值。

如果你可以使用现有代码实现你的 s,那么你可以从现有代码中实现的任何好处(例如速度)也会累积到你的代码中。SetSet

如果选择在没有后备的情况下实现 a,则必须复制旨在防止重复元素的代码。啊,美味的讽刺。SetMap

也就是说,没有什么可以阻止你以不同的方式实现你的s。Set


答案 2

我的猜测是,HashSet最初是根据HashMap实现的,以便快速轻松地完成它。就代码行而言,HashSet是HashMap的一小部分。

我猜它仍然没有被优化的原因是害怕变化。

但是,浪费比您想象的要糟糕得多。在 32 位和 64 位上,HashSet 比必需的大 4 倍,HashMap 比必要的大 2 倍。HashMap可以用一个数组来实现,其中有键和值(加上用于冲突的链)。这意味着每个条目有两个指针,在 64 位 VM 上为 16 个字节。实际上,HashMap 每个条目都包含一个 Entry 对象,该对象为指向 Entry 的指针添加 8 个字节,为 Entry 对象标头添加 8 个字节。HashSet还为每个元素使用32个字节,但浪费是4倍而不是2倍,因为它每个元素只需要8个字节。