在我解决Java测试时,我提出了以下问题:
您需要在集合中存储元素,以确保不存储重复项,并且可以按自然顺序访问所有元素。哪个接口提供该功能?
A. java.util.Map
B. java.util.Set
C. java.util.List
D. java.util.Collection
我不知道这里的正确案例是什么?我们可以在任何这些集合中存储相同的元素,除非在Set
中,但Set
不提供自然顺序。怎么了?
该测试的正确答案是Set
让我们记住,它要求的接口可以提供这个;如果正确实现,Set
接口可以提供它。
Map
接口不保证存储事物的顺序,因为它是特定于实现的。但是,如果您使用正确的实现(即,文档拼写的TreeMap
),那么您将保证自然顺序并且没有重复条目。
但是,没有关键值对的要求。Set
接口也不保证存储内容的顺序,因为它的具体实现。但是,和TreeMap
一样,TreeSet
是一个可以用来以自然顺序存储东西而没有重复的东西。
这是它的样子。
Set<String> values = new TreeSet<>();
List
界面肯定会允许重复,立即排除它。Collection
接口没有任何直接实现它的东西,但它是整个集合层次结构的族长。所以,从理论上讲,这样的代码是合法的:
Collection<String> values = new TreeSet<>();
...但是你失去了关于它实际上是什么类型的收集的信息,所以我不鼓励它的使用。TreeSet会给你排序(默认情况下自然排序通过Comparator进行自定义排序)。
更一般地说,SortedSet是提供唯一性和排序的更通用的界面。
一个集合,进一步提供其元素的总排序。元素按照它们的自然顺序排序,或者通过通常在排序集创建时提供的比较器排序。 set的迭代器将按升序元素顺序遍历集合。提供了几个额外的操作以利用订购。
如果按照自然顺序,您的意思是插入顺序,那么LinkedHashSet就是您转到Set实现。
正确的答案是:SortedSet提供关于元素的自然顺序的保证。 TreeSet是典型的实现
严格来说,当从上面选择时,List
是唯一具有已定义迭代次序的接口,但它确实允许重复。
另一方面,Set
和Map
不允许重复(Map
的密钥),但它们也没有定义迭代的顺序,默认情况下它们是无序的,HashSet
/ HashMap
是反例。
Collection
不允许。
因此,严格来说 - 没有一个建议的接口提供所需的功能。但是,正如其他人所建议的那样,接口的特定实现允许元素的自然顺序而没有重复,主要是SortedSet
接口及其TreeSet
实现
为了进一步说明为什么Set
不是一个好的选择,如果你有一个变量,让它为mySet
,你想要它被订购,当你使用Set
界面时,用户会感到惊讶,想象下面的代码:
public int processMyDataStructure(Set set) {
//some calculation that assumes set is ordered
return result;
}
并且用户为您提供HashSet
作为参数 - 您将从您的方法中获得错误的行为,因为Set
不保证订购。为了避免它,你应该要求SortedSet
而不是Set
。
我昨天在面试时遇到了这个问题,需要对此发表评论:问题(假设其中一个A,B,C或D答案必须正确)显然是错误的。没有列出正确的答案。
Set接口中没有任何内容保证元素的返回顺序。并且没有像Makoto在他的accepted answer中所希望的那样,作为正确的实现,理论上可以完成这项工作,因为我们不会在这里要求任何实现,而是接口是否提供所请求的功能。
因此,提供答案的测试问题是错误的。
再多说一点accepted answer,还有一个原因是错误的。具体来说,Makoto认为,List接口肯定会允许重复,立即排除它。这个论点可能会被引用来自List规范说:
有人可能希望通过在用户尝试插入时抛出运行时异常来实现禁止重复的列表,但我们希望这种用法很少见,这是不可想象的。
所以在我看来,给出的任何答案都是同样错误的,或者,正如accepted answer所希望的那样,同样正确,因为我们可以自由地编写以我们希望的任何方式表现的List(或Map或Collection)的实现(在边界集中)通过接口规范),但接口和它们的规范在这里是为了保证一些合同,而这个问题实际上是关于它们的,而不是关于可能的实现。