243

我敢肯定有一个很好的理由,但是有人可以解释为什么java.util.Set缺少界面get(int Index)或任何类似的get()方法吗?

似乎集合非常适合放入东西,但我找不到从中检索单个项目的优雅方法。

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

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

我确信它被排除在 API 之外的事实意味着有充分的理由不这样做 - 有人可以启发我吗?

编辑: 这里有一些非常棒的答案,还有一些说“更多上下文”。具体场景是 dbUnit 测试,我可以合理地断言从查询返回的集合只有 1 个项目,并且我试图访问该项目。

但是,如果没有这个场景,这个问题会更有效,因为它仍然更加集中:

set 和 list 有什么区别

感谢所有人在下面提供出色的答案。

4

19 回答 19

180

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

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

于 2009-04-20T19:20:33.657 回答
77

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

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

这个“IndexedSet”与 SortedSet 不同——在 SortedSet 中,元素通过使用 Comparator(或使用它们的“自然”排序)进行排序。

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

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

由于JDK是开源的,也许这个接口最终会包含在Java 7中......

于 2010-05-30T00:55:09.507 回答
29

只需添加mmyers 的回答中未提及的一点。

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

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

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

SortedSet 是一个 Set (即元素是唯一的),它通过元素的自然顺序或使用 some保持有序Comparatorfirst()您可以使用和last()方法轻松访问第一个和最后一个项目。ASortedSet每隔一段时间就会派上用场,当您需要保持您的收藏不重复并以某种方式排序时。

编辑:如果您需要一个 Set 其元素按插入顺序保存(很像 List),请查看LinkedHashSet.

于 2009-04-20T19:31:46.687 回答
25

这种问题会引出什么时候应该使用集合以及什么时候应该使用列表的问题。通常,建议是:

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

经常出现的第四种情况是您两者都不需要。在这种情况下,您会看到一些程序员使用列表,而一些程序员使用集合。就我个人而言,我发现将 set 视为一个没有排序的列表是非常有害的——因为它实际上是一个完全不同的野兽。除非您需要诸如设置唯一性或设置相等性之类的东西,否则请始终使用列表。

于 2009-04-21T09:42:30.090 回答
18

我不确定是否有人以这种方式准确地拼写出来,但您需要了解以下内容:

集合中没有“第一个”元素。

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

当然,您的计算机无法真正保留内存中未排序的内容列表。它必须有一些排序。在内部,它是一个数组或链表或其他东西。但你并不真正知道它是什么,它也没有真正的第一个元素;出现“第一个”的元素是偶然出现的,下次可能不会是第一个。即使你采取了措施来“保证”一个特定的第一个元素,它仍然是偶然出现的,因为你只是碰巧把它用于一个特定的 Set 实现;不同的实现可能不适用于您所做的事情。而且,事实上,您可能并不像您认为的那样了解您正在使用的实现。

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

于 2009-04-20T21:25:20.407 回答
10

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

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

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

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

如果您需要灵活的数据结构,您可能会对Google Collections感兴趣

于 2009-04-28T07:29:23.397 回答
7

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

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

于 2010-01-19T12:37:24.913 回答
7

如果您要通过集合中的索引进行大量随机访问,您可以获得其元素的数组视图:

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

但是有两个主要缺点:

  1. 它的内存效率不高,因为需要创建整个集合的数组。
  2. 如果该集合被修改,该视图将变得过时。
于 2011-01-07T11:54:18.463 回答
5

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

于 2009-04-20T19:22:23.247 回答
5

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

for(A a : set) { 
   visit(a); 
}
于 2009-04-20T20:25:23.733 回答
3

我遇到了我实际上想要一个通过索引访问的排序集的情况我同意其他发布者的观点,即使用索引访问一个未排序的集是没有意义的)。一个例子是一棵树,我希望对孩子进行排序并且不允许重复的孩子。

我需要通过索引访问来显示它们,并且设置的属性可以派上用场来有效地消除重复。

在 java.util 或 google 集合中找不到合适的集合,我发现自己实现它很简单。基本思想是包装一个 SortedSet 并在需要通过索引访问时创建一个 List(并在 SortedSet 更改时忘记该列表)。当然,这只有在更改包装的 SortedSet 并且访问列表在 Collection 的生命周期中是分开的时才有效。否则,它的行为就像一个经常排序的列表,即太慢。

对于大量的孩子,这比我通过 Collections.sort 排序的列表提高了很多。

于 2010-08-18T13:33:43.910 回答
2

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

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

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

Set数据结构通常可以通过HashTable/HashMapBalancedTree数据结构来实现,为了快速检测元素是否存在并添加不存在的元素,通常实现良好的Set可以实现O(1)时间复杂度contains操作。在 Java 中,是SetHashSet最常用的实现,它是通过调用API 来实现的,并且是使用链表ArrayLinkedList的组合)的单独链接来实现的。HashMapHashMap

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

于 2015-04-01T11:39:59.147 回答
1

Set接口没有 get index-type 调用甚至更基本的调用,例如 first() 或 last() 的原因是因为它是一个模棱两可的操作,因此是一个潜在的危险操作。如果一个方法返回一个 Set,并且您在其上调用 first() 方法,那么预期的结果是什么,因为泛型 Set 不保证排序?结果对象在方法的每次调用之间可能会有很大的不同,或者它可能不会让你陷入一种错误的安全感,直到你使用的库改变了下面的实现,现在你发现你的所有代码都中断了没有特别的理由。

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

// 我确实希望 Set 接口有一个 get-random-element。

于 2012-05-21T18:13:23.067 回答
1

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

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

于 2014-01-27T14:09:04.923 回答
1

java.util.Set是未排序项的集合。如果 Set 有 get(int index) 则没有任何意义,因为 Set 没有索引,而且您只能猜测值。

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

于 2016-06-10T08:42:26.363 回答
0

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;
}
于 2020-03-21T04:40:27.103 回答
0

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

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

于 2021-07-04T20:15:55.543 回答
-1

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

于 2012-02-02T22:55:12.603 回答
-3

要获取集合中的元素,我使用以下一个:

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;
}
于 2010-06-20T10:19:55.907 回答