今天面试官问我:Set如何保证不重复?
3 回答
答案在于add
方法的源代码。例如在源代码中TreeSet
add 方法的实现如下:
public boolean add(E e)
{
return m.put(e, PRESENT)==null;
}
其中,PRESENT
是Object
类的对象。并且m
是 的对象NavigableMap
。这NavigableMap
m
用于将元素存储e
为给定的key
key 。因此,每个键都有相同的对象。oracle doc中定义的方法是:PRESENT
value
e
m
PRESENT
put
Map
将指定的值与此映射中的指定键相关联。如果映射先前包含键的映射,则替换旧值。
...
...
返回:与 key 关联的前一个值,如果没有 key 的映射,则返回 null。(返回 null 还可以指示映射先前将 null 与 key 关联。)
因此,当您将重复元素放入 set 时,此元素将放入NavigableMap
带有 value 的 as 键中PRESENT
。如果此键不存在,NavigableMap
则put
方法返回null
并因此
m.put(e,PRESENT)==null
返回 true 并且我们知道该元素已添加。如果该键已经存在于NavigableMap
then方法中,则使用 PRESENTput
覆盖value
for the inside 并返回旧值(即 PRESENT)并因此
返回,我们知道该元素未添加。key
e
NavigableMap
m.put(e,PRESENT)==null
false
Set 是一种抽象数据类型,可以通过多种方式实现。就其本身而言,它是合同的规范;因此,它不保证任何事情。由接口的实现来保证合同的履行。
因此,看看这些实现如何以及为什么工作会更有趣。一些常见的实现是:
- 哈希表,在 Java 中由
HashSet
- 平衡树,在 Java 中实现
TreeSet
- 位集(用于特殊类型),在 Java 中由
EnumSet
and实现BitSet
- 跳过列表,由
ConcurrentSkipListSet
- 朴素数组:在添加元素之前扫描数组;不经常使用。在Java中实现为
CopyOnWriteArraySet
在一次工作面试中,您会回答上述问题,并愿意解释任何一种实现的细节。面试官应该已经知道其中的一些,除非被问到,否则开始漫无边际地谈论它们对你没有好处。
从规范的角度来看,它是通过例如指定add
如果您尝试添加重复项时该方法必须执行的操作来实现的。该add
方法的文档说明了这一点,例如:
如果指定元素尚不存在,则将其添加到此集合中(可选操作)。更正式地说,如果集合不包含元素 e2,则将指定的元素 e 添加到此集合中,使得 (e==null ? e2==null : e.equals(e2))。如果该集合已包含该元素,则调用将保持该集合不变并返回 false。结合对构造函数的限制,这确保了集合永远不会包含重复的元素。
从同一页面(http://docs.oracle.com/javase/6/docs/api/java/util/Set.html):
毫不奇怪,对构造函数的附加规定是,所有构造函数都必须创建一个不包含重复元素的集合(如上所述)。
(为了完整起见,还有关于equals
并hashCode
确保Set
正确建模集合抽象的规定。)