为什么java.util.Set没有get(int index)?

我相信有一个很好的理由,但有人能解释一下为什么java.util.Set接口缺少get(int Index),或任何类似的get()方法吗?

集合似乎很适合把东西放进去,但我找不到一种优雅的方法来从中检索单个项目。

如果我知道我想要第一项,我可以使用set.iterator().next(),但否则,似乎我必须强制转换到数组来检索特定索引处的项?

从集合中检索数据的适当方法是什么?(与使用迭代器不同)

我相信它被排除在API之外的事实意味着有一个很好的理由不这样做——有人能启发我吗?

<强>编辑: 这里有一些非常棒的回答,还有一些说“更多的背景”。具体的场景是一个dbUnit测试,其中我可以合理地断言从查询返回的集合只有1个项,并且我正在尝试访问该项

然而,这个问题在没有场景的情况下更有效,因为它仍然更集中:

set和list有什么区别

感谢大家的精彩回答。

207054 次浏览

因为集合没有顺序。一些实现可以(特别是那些实现java.util.SortedSet接口的实现),但这不是集合的一般属性。

如果您试图以这种方式使用集合,则应该考虑使用列表。

这是因为Set只保证惟一性,而没有说明最佳访问或使用模式。也就是说,一个Set可以是一个List或者Map,每一个都有非常不同的检索特征。

只是添加了一个在mmyers的回答中没有提到的点。

如果我知道我想要第一项,我就可以 使用set.iterator().next(),但是 不然的话,我只好放弃了 数组中检索项 具体指标?< / p >

什么是合适的方式 从集合中检索数据?(其他 而不是使用迭代器)

你还应该熟悉SortedSet接口(其最常见的实现是TreeSet)。

SortedSet是一个集合(即元素是唯一的),它由元素的自然排序或使用一些Comparator来保持排序。可以使用first()last()方法轻松访问第一个和最后一个项。SortedSet每隔一段时间就会派上用场,当你需要保持你的集合无重复且以某种方式有序时。

编辑:如果你需要一个Set,它的元素是按插入顺序保存的(很像List),看一下LinkedHashSet

我能想到的在集合中使用数值索引的唯一原因是为了迭代。为此,请使用

for(A a : set) {
visit(a);
}

我不确定是否有人这么说过,但你需要明白以下几点:

集合中没有“first”元素。

因为,正如其他人所说,集合没有顺序。集合是一个数学概念,特别不包括排序。

当然,你的计算机不能真的在内存中保存一个没有顺序的东西列表。它必须有一定的顺序。内部它是一个数组或者链表之类的。但是你不知道它是什么,它没有第一元素;“第一个”出现的元素是偶然出现的,下次可能就不是第一个了。即使您采取措施“保证”特定的第一个元素,它仍然是偶然出现的,因为您只是碰巧为Set的一个特定实现获得了它;对于您所做的工作,不同的实现可能不会以这种方式工作。事实上,您可能并不像您认为的那样了解您正在使用的实现。

人们会遇到这一切。的。时间。使用RDBMS系统,但不理解。RDBMS查询返回一组记录。这与数学中的集合类型相同:项的无序集合,只是在这种情况下,项是记录。除非使用order BY子句,否则RDBMS查询结果根本没有保证的顺序,但人们一直认为它是有保证的,然后在某一天,当数据或代码的形状发生轻微变化并触发查询优化器以不同的方式工作时,结果突然没有按照他们期望的顺序出现。这些人通常在数据库课上(或在阅读文档或教程时)没有注意到事先向他们解释过查询结果没有保证顺序。

这就引出了一个问题,什么时候应该使用集合,什么时候应该使用列表。通常,建议是这样的:

  1. 如果需要有序数据,请使用List
  2. 如果你需要唯一的数据,使用一个集合
  3. 如果两者都需要,可以使用SortedSet(用于按比较器排序的数据)或OrderedSet/UniqueList(用于按插入排序的数据)。不幸的是,Java API还没有OrderedSet/UniqueList。

第四种经常出现的情况是,两者都不需要。在这种情况下,你会看到一些程序员使用列表,一些使用集合。就我个人而言,我觉得把set看作一个没有顺序的列表是非常有害的——因为它真的是另一种野兽。除非你需要集唯一性或集相等性,否则总是倾向于列表。

标准Java集合中缺少一些数据结构。

包(类似set,但可以多次包含元素)

UniqueList(有序列表,每个元素只能包含一次)

在这种情况下,你似乎需要一个唯一性列表

如果你需要灵活的数据结构,你可能会对谷歌集合感兴趣

根据Set集合的定义,Set中的元素是无序的。所以它们不能被索引访问。

但是为什么我们没有一个get(object)方法,不是通过提供索引作为参数,而是提供一个与我们正在寻找的对象相等的对象? 通过这种方式,我们可以访问Set中元素的数据,只需要知道equal方法使用的属性

实际上,在编写使用对象-关系映射的JavaEE应用程序(例如Hibernate)时,这是一个反复出现的问题;在所有回复这里的人中,Andreas Petersson是唯一一个理解真正问题并给出正确答案的人:Java缺少一个UniqueList!(或者您也可以将其称为OrderedSet或IndexedSet)。

Maxwing提到了这个用例(在这个用例中,您需要有序且唯一的数据),他建议使用SortedSet,但这不是Marty Pitt真正需要的。

这个“IndexedSet”和SortedSet不一样——在SortedSet中,元素是通过比较器排序的(或者使用它们的“自然”排序)。

但相反,它更接近于LinkedHashSet(其他人也建议),甚至更接近于一个(也不存在)"ArrayListSet",因为它保证元素以插入时相同的顺序返回。

但是LinkedHashSet是一个实现,而不是一个接口!所需要的是IndexedSet(或ListSet,或OrderedSet,或UniqueList)接口!这将允许程序员指定他需要一个具有特定顺序且没有重复的元素集合,然后用任何实现实例化它(例如Hibernate提供的实现)。

因为JDK是开源的,也许这个接口最终会被包含在Java 7中…

要获取Set中的元素,我使用以下命令:

public T getElement(Set<T> set, T element) {
T result = null;
if (set instanceof TreeSet<?>) {
T floor = ((TreeSet<T>) set).floor(element);
if (floor != null && floor.equals(element))
result = floor;
} else {
boolean found = false;
for (Iterator<T> it = set.iterator(); !found && it.hasNext();) {
if (true) {
T current = it.next();
if (current.equals(element)) {
result = current;
found = true;
}
}
}
}
return result;
}
我遇到了这样的情况,我实际上想要通过索引访问排序Set(我同意其他海报,使用索引访问未排序的Set是没有意义的)。 例如,在树中,我希望对子节点进行排序,不允许重复子节点。< / p >

我需要通过索引访问来显示它们,set属性可以有效地消除重复。

在java中找不到合适的集合。util或谷歌集合,我发现它直接实现它自己。基本思想是包装SortedSet并在需要通过索引访问时创建一个List(并且在SortedSet被更改时忘记该列表)。当然,这只有在更改包装的SortedSet和在集合的生命周期中分离访问列表时才有效。否则它就像一个经常排序的列表,也就是说太慢了。

对于大量的子节点,这大大提高了我通过Collections.sort进行排序的列表的性能。

如果你要在一个集合中通过索引进行大量的随机访问,你可以得到它的元素的数组视图:

Object[] arrayView = mySet.toArray();
//do whatever you need with arrayView[i]

但它有两个主要缺点:

  1. 它的内存效率不高,因为需要为整个集合创建一个数组。
  2. 如果集合被修改,视图就会过时。

你可以做new ArrayList<T>(set).get(index)

Set 接口没有get索引类型的调用,甚至没有更基本的调用,比如first()或last(),是因为它是一个模糊的操作,因此是一个潜在的危险操作。如果一个方法返回一个Set,并且你对它调用first()方法,假设一个泛型Set不能保证排序,那么预期的结果是什么?结果对象可能在方法的每次调用之间变化很大,也可能没有变化,从而让您产生一种错误的安全感,直到您使用的库更改了下面的实现,现在您发现所有代码都毫无原因地中断了。

这里列出的关于变通办法的建议很好。如果需要索引访问,请使用列表。对泛型Set使用迭代器或toArray时要小心,因为a)不能保证顺序,b)不能保证顺序不会随着后续调用或不同的底层实现而改变。如果你需要介于两者之间的东西,SortedSet或LinkedHashSet就是你想要的。

< p > / / 我希望Set界面有一个get-random-元素。< / p >

如果你不介意要排序的集合,那么你可能有兴趣看看indexed-tree-map项目。

增强的TreeSet/TreeMap提供了通过索引访问元素或获取元素索引的功能。实现基于更新RB树中的节点权重。所以这里没有迭代,也没有列表备份。

请注意,只有2个基本的数据结构可以通过索引访问。

  • 数组数据结构可以通过具有O(1)时间复杂度的索引来访问,从而实现get(int index)操作。
  • LinkedList数据结构也可以通过索引访问,但以O(n)时间复杂度来实现get(int index)操作。

在Java中,ArrayList是使用数组数据结构实现的。

虽然数据结构通常可以通过散列表/ HashMapBalancedTree数据结构实现,为了快速检测元素是否存在并添加不存在的元素,通常一个良好实现的可以实现O(1)时间复杂度的contains操作。在Java中,HashSet最常用的实现,它是通过调用HashMap API实现的,而HashMap是使用contains0 (contains1和contains2的组合)实现的。

由于可以通过不同的数据结构实现,因此它没有get(int index)方法。

java.util.Set是一个无序项的集合。如果Set有一个get(int index)就没有意义了,因为 Set没有索引,你只能猜测它的值。< / p >

如果你真的想要这个,编写一个方法从Set中获取随机元素。

Set是一个接口,它的一些实现类是HashSet、TreeSet和LinkedHashSet。它在底层使用HashMap来存储值。因为HashMap不保留顺序,所以不可能通过索引获取值。

你现在肯定在想Set是如何使用HashMap的,因为HashMap存储了一个键和值对,而Set没有。有效的问题。当你在Set中添加一个元素时,它在内部维护一个HashMap,其中键是你想在Set中输入的元素,值是虚拟常量。下面是add函数的内部实现。因此,HashMap中的所有键都将具有相同的常量值。

// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();


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

因为Set在随机位置存储唯一的元素,并且在内部它使用多个数据结构。即数组,链表,带有哈希的树。

< p >链接 https://en.wikipedia.org/wiki/Set_ (abstract_data_type) < / p >