Java:如何从集合中获取n个元素

18

我试图找到从集合中从x开始获取n个元素的最优雅的方法。我的结论是使用流(streams):

Set<T> s;
Set<T> subS = s.stream().skip(x).limit(n).collect(Collectors.toSet());

这是做事的最佳方式吗?有什么缺点吗?


4
这个问题属于http://codereview.stackexchange.com/。同时,您需要更加准确地说明您的 Set 是什么,因为标准的 Set 只使用一个泛型类型。 - Pshemo
肯定更适合代码审查。在我看来看起来不错。 - beresfordt
是的,我不确定应该在哪里发布这个。如果可以的话,请随意移动它。 - Diaa
没有任何一种方法可行,因为集合是无序的,并且迭代顺序不能保证随时间而变化。假设元素0是“foo”,元素1是“bar”。您将“baz”添加到集合中,现在元素0可能是“bar”(因为内部哈希表被扩展和重新哈希)。另外,重用迭代器并依赖于ConcurrentModificationException也不能保证始终有效。 - Mark Jeronimus
7个回答

12

与 Steve Kuo 的回答类似,但也跳过前 x 个元素:

Iterables.limit(Iterables.skip(s, x), n);

Guava Iterables


9

使用Guava,Iterables.limit(s,20)


这不会跳过前x个元素。 - JamesB

6
你的代码无法运行。
Set<T,C> s;
Set<T,C> subS = s.stream().skip(x).limit(n).collect(Collectors.toSet());
什么是Set<T,C>?一个Set包含给定类型的元素,那么这两个类型参数应该代表什么意思呢?
此外,如果您有一个Set<T>,您没有定义顺序。在Set的上下文中,“从x开始的n个元素”没有意义。有一些专门的Set实现具有顺序,例如排序或保留插入顺序,但由于您的代码没有声明这种先决条件,似乎应该在任意Set上运行,所以必须被视为不可用。
如果您想根据顺序处理Set的一部分,您必须首先冻结顺序:
Set<T> s;
List<T> frozenOrder=new ArrayList<>(s);
  • 列表将按照Set的顺序排列,如果有的话,否则将按照创建ArrayList时固定的任意顺序排列,之后不会更改。
  • 因此,提取其中的一部分很容易:
List<T> sub=frozenOrder.subList(x, Math.min(s.size(), x+n));

如果您愿意,您还可以将其转换回一个 Set

Set<T> subSet=new HashSet<>(sub);

话虽如此,但按位置指定处理Set的一部分并不常见。


2
你可以在集合上进行迭代并收集前n个元素:
int n = 0;
Iterator<T> iter = set.iterator();
while (n < 8  && iter.hasNext()) {
 T t = iter.next();
 list.add(t);
 n++;
}

优点是它应该比更通用的解决方案更快。
缺点是它比您提出的解决方案更冗长。

你也可以使用 for 循环:for (int n = 0; n < NUMBER && iter.hasNext(); n++) - anon
太好了,谢谢你。 - Ivan Mushketyk
在你进行自己的编辑之前,我会很感激你接受我的建议性编辑 ;) (我将从接受中获得两个声望) - anon
问题是“这样做是最好的方式吗?有什么缺点吗?”您能解释一下您的答案比OP已经拥有的更好吗? - Pshemo
很棒且高效的回答。 - Ajay Kumar

2

Stream 的使用是不错的。我唯一能看到的缺点是不是所有的 Set 实现都是有序的,例如 HashSet 是无序的,但 LinkedHashSet 是有序的。因此,在不同的运行中可能会得到不同的结果集。


“首选接口”会是SortedSet吗? - Johannes
我认为不需要使用SortedSet。这里的主要问题是保留顺序(无论是否排序)。实际上,这主要取决于实现方式。 - barunsthakur
一个排序集合实际上是解决多次运行问题的相当不错的替代方案。 - Diaa
如果这是您的要求。但是,当我不想让我的集合排序,而是保持元素的插入顺序时会发生什么? - barunsthakur
如果您想要插入顺序排序,那么您需要使用 LinkedHashSet - tddmonkey

1
首先,集合不是用来获取其中特定元素的 - 您应该使用sortedSet或ArrayList。但是,如果您必须获取集合中的元素,可以使用以下代码迭代集合:
int c = 0;
int n = 50; //Number of elements to get
Iterator<T> iter = set.iterator();
while (c<n  && iter.hasNext()) {
   T t = iter.next();
   list.add(t);
   c++;
}

1

集合本质上不是有序的,所以不能从元素x开始。如果您需要排序的集合,可以使用SortedSet。

我会先将其转换为List,例如

    new ArrayList(s).subList(<index of x>, <index of x + n>);

但这可能会对性能产生非常不良的影响。在这种情况下,ArrayList必须被存储以检索下一个子列表,因为没有明确的顺序,而隐含的顺序可能会在下一次调用new ArrayList(s)时发生更改。


1
是的,这将产生与流相同的输出,但可能会有更差的性能。 - Diaa
我还没有研究过Java8的streams,但这些新类似乎提供了类似迭代器的功能,结合更强大的语法和一些聚合函数。ArrayList变体似乎会在OP的代码中引起较少的更改,因为将Set更改为SortedSet是一项艰巨的工作(个人经验)。如果不打算解决“根本问题”,则流变量肯定更优雅。 - Johannes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接