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


237

我确信这是有充分理由的,但是有人可以解释一下为什么java.util.Set缺少接口get(int Index)或任何类似get()方法吗?

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

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

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

我敢肯定,将其排除在API之外的事实意味着有充分的理由不这样做-有人可以启发我吗?

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

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

set和list有什么区别

感谢所有以下出色的答案。


1
为什么从索引集中获取元素?您是否正在尝试将集合用作排序数组?
MSN

这里的特定实例是针对从休眠调用返回的Set的dbUnit测试。在我的测试中,由于我用来设置它的IDataSet,因此可以合理地假设(因为我断言)返回的对象是特定顺序的。这是非典型的情况,但是引起了我对API的好奇。
马蒂·皮特

1
除非您使用自定义Set实现,否则按特定顺序添加内容并不意味着它们会保持这种状态。
迈克尔·迈尔斯

1
“如果我知道我想要第一项,则可以使用set.iterator()。next()”-这行实际上没有任何意义。您实际上是在说:“如果我知道我想要第一项,那么根据实现对第一项的定义,那么我可以...”。集本身是无序的,因此索引访问没有意义。现在,如果有一个ArrayListSet,那就更有意义了(只需强制转换为“ List”即可)。也许您可以为这个问题提供更多背景信息?
jsight

集不是无序的!它的某些实现是,但是某些实现以特定方式显式排序。
reinierpost

Answers:


176

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

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


10
@matt b:不,我认为他应该考虑。思维是好的。;)
Michael Myers

10
考虑一下,然后再做。
乔·菲利普斯

21
“考虑”是正确的措词。存在两个可能的问题:(a)他在应该使用其他东西时正在使用电视机,或者(b)他正在尝试使用Set不支持但可以用其他方式进行操作的事情。最好考虑其中的哪种情况。
kenj0418

6
可能更简单的答案是使用排序集。(我认为唯一性在选择集合时起了作用)。但是我有一个问题,因为SortedSet是有序的,为什么在api中没有get方法。
uncaught_exceptions

5
@HDave:不,数据结构的多个实现共享一个属性的事实并不会使它成为数据结构本身的属性。List的三个常用实现中的两个(ArrayList和Vector)是随机访问的,但这并不能使随机访问成为Lists的属性。
迈克尔·迈尔斯

74

实际上,在编写使用对象关系映射(例如,使用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中。


3
就目前而言,这是一个很好的答案,但与此同时我们该怎么办?
HDave 2012年

当然是啦。我之前在休眠状态下使用了清单tomtomany和onetomany ORM。当涉及超过3个相关实体的左联接查询时,我遇到了麻烦(或缺陷),引发了异常。在此处查看更多详细信息(jroller.com/eyallupu/entry/…)。要解决此问题,必须使用set as ORM映射集合。但说实话,set在编程中以及需要订购集合时不方便访问。我们真正需要的是“索引集”,如Sorin Postelnicu所说的那样,SORT和UNIQUE
horaceman'Oct 21'2012

2
Apache Commons Collections具有ListOrderedSet7年前OP所需要的(今天我也需要)。
Paul

@Paul:确实确实看起来不错。不幸的是,它仍然具有3个缺点:1)它是一个类,而不是一个接口。2)它不在JDK中。3)这不是Hibernate查询返回的内容。
Sorin Postelnicu

是的,但是除了这三个主要缺点之外,它是完美的!:)回想起来,我应该对这个问题发表评论,而不是对您的回答发表评论-我取消了评论并予以What is needed is an IndexedSet (or ListSet, or OrderedSet, or UniqueList)...忽略...interface。对于那个很抱歉!
Paul

29

只是添加了mmyers的答案中未提及的一点。

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

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

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

SortedSet是一个Set(即元素是唯一的),它通过元素的自然排序或使用some 保持排序Comparator。您可以使用first()last()方法轻松访问第一个和最后一个项目。SortedSet当您需要保持收藏品无重复且以某种方式订购时,A 偶尔会派上用场。

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


我自己喜欢LinkedHashSet。但这是值得一提的。+1
Michael Myers

谢谢,我稍微调整了答案。(似乎我将TreeSet的某些方面与LinkedHashSet的那些方面混淆了。)
Jonik,

25

这种情况导致您何时应该使用集合和何时应该使用列表的问题。通常,建议是:

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

经常出现的第四种情况是您不需要。在这种情况下,您会看到有些程序员使用列表,有些则使用集合。就我个人而言,我发现将其视为列表而不进行订购非常有害-因为它确实是另一种野兽。除非您需要诸如设置唯一性或设置相等性之类的东西,否则请始终偏爱列表。


2
如果不确定,请接受Collection <T>甚至Iterable <T>并初始化为List。
安德烈亚斯·彼得森

这将是一个袋子或多件套。但是Java不支持这些。他们说您应该直接使用Collection <T>。
机械蜗牛

4.您需要非唯一数据,并且不关心顺序。您不能使用一套。列表,袋或多件套将起作用。
Andrew Gallasch 2015年

17

我不确定是否有人用这种方式将其拼写清楚,但是您需要了解以下内容:

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

因为,正如其他人所说,集合没有排序。集合是一个数学概念,专门不包含排序。

当然,您的计算机不能真正保留未在内存中排序的内容的列表。它必须有一些顺序。在内部,它是一个数组或一个链表或其他东西。但是您真的不知道它是什么,并且它实际上没有第一个元素。出现“第一”的元素是通过偶然的方式出现的,下次可能不会出现。即使您采取步骤“保证”一个特定的第一个元素,它仍然是偶然出现的,因为您碰巧将它正确地用于Set的一个特定实现。一个不同的实现可能对您所做的工作无效。而且,实际上,您可能不知道您所使用的实现以及您认为的实现。

人们碰到这一切。的。时间。与RDBMS系统并不清楚。RDBMS查询返回一组记录。这与数学上的集合类型相同:无序的项目集合,仅在这种情况下,项目才是记录。除非您使用ORDER BY子句,否则RDBMS查询结果根本没有保证的顺序,但是人们总是以为它会一直保证它的顺序,然后在数据或代码的形状稍有改变并触发查询优化器工作的某天跳闸换一种方式,结果突然没有按他们期望的顺序出现。这些人通常是在数据库类(或阅读文档或教程)时不注意的人,因为他们事先向他们解释了查询结果的排序没有保证。


呵呵,当然,顺序通常会在代码投入生产后立即更改,因为它太慢了,因此它们添加了索引以加快查询速度。现在,代码运行很快,但是给出了错误的答案。如果幸运的话,三到四天没有人注意到。如果您不走运,一个月内没人会注意到...
TMN

我不认为他会错过这一点(也许他对这种记法很草率)。他不想要集合中的第一个元素,而是想要集合中的任意元素。由于Set是,您可以给他一个任意的元素Iterable
Elazar Leibovich

您正在谈论按索引获取(索引)。平等的get(Object)呢?
Kumar Manish

10

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

袋子(类似于套装,但可以多次包含元素)

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

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

如果您需要灵活的数据结构,则可能对Google收藏夹感兴趣


1
Guva是否提供“ UniqueList”?
Mike Rylander 2013年

否,但是您可以拥有一个具有类似属性的java.util.LinkedHashSet。
Andreas Petersson

7

是的,根据Set Collection的定义,Set中的元素没有排序。因此无法通过索引访问它们。

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


7

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

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

但是有两个主要缺点:

  1. 它不是高效的内存,因为需要创建整个数组。
  2. 如果修改了该集,则视图将过时。

5

这是因为Set仅保证唯一性,而未提及最佳访问或使用模式。即,一个集合可以是一个列表或一个地图,它们每个都有非常不同的检索特性。


5

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

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

不正确,访问随机元素怎么办?
杰里米·萨尔文

哈哈。好点:),但是我敢肯定这很容易被滥用。
雨果

3

我遇到了,我其实是想一个情况排序集通过指数(我与其他海报同意,访问一个未排序的设置与索引是没有意义的)访问。一个例子是一棵树,我希望对这些孩子进行排序,并且不允许重复的孩子。

我需要通过索引访问才能显示它们,并且set属性非常有用,可以有效地消除重复项。

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

在有大量孩子的情况下,与我通过Collections.sort进行排序的列表相比,这种性能得到了很大的改善。


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)方法。


手指树(请参阅Haskell Data.Sequence.lookup函数)也允许通过索引(O(1)靠近O(log n)中间的两端,更准确O(min(log(k), log(n-k))))进行访问,二叉树也可以通过索引进行访问(请参见Haskell Data.Set.lookupIndex函数)。因此,您最初的主张“请注意只能通过索引访问2个基本数据结构”是不正确的。
分号

1

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

此处列出的有关变通办法的建议很好。如果需要索引访问,请使用列表。使用带有通用Set的迭代器或toArray时要小心,因为a)不能保证顺序,b)不能保证顺序不会随后续调用或不同的基础实现而改变。如果您需要介于两者之间的内容,则需要使用SortedSet或LinkedHashSet。

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


1

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

如果您确实需要这样做,请编写一种方法来从Set中获取随机元素。


0

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


这将返回一个Set列表,而get(index)返回一个Set。而是,我用过: new ArrayList<T>(t).get(0) 我认为通过索引从Set中获取特定元素的想法是有效的反对。但是,如果Set具有only()成员函数(对于大小为1的Set),可以轻松访问Set中的唯一元素,那将是很好的。这将节省前述内容new ArrayListfor (Foo foo : foos) { return foo; }
Doug Moscrop 2012年


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;
}

所有Sets的实现都HashMap在后台使用来存储值,您可以证实这一主张TreeSet吗?

1
the keys in the HashMap will have the same constant value HashMap遗嘱中的键映射到一个相同的不变对象Object
灰胡子


-3

为了获得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;
}

功能不是问题所要的。我们需要索引,而不是值。无论如何,您的职能是什么?看起来如果它等于其中的一个元素,它只是返回该元素。那contains()没有做什么呢?
Janus Troelsen '02

T定义在哪里?为什么if (true)
量子
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.