如何使用 Java 8 lambda 从流中获取一系列项目?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/22917270/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to get a range of items from stream using Java 8 lambda?
提问by Frank
In a previous question [ How to dynamically do filtering in Java 8?] Stuart Marks gave a wonderful answer, and provided several useful utilities to handle selection of topN and topPercent from stream.
在上一个问题 [如何在 Java 8 中动态进行过滤?] Stuart Marks 给出了一个很好的答案,并提供了几个有用的实用程序来处理从流中选择 topN 和 topPercent。
I'll include them here from his original answer :
我将从他的原始答案中将它们包括在内:
@FunctionalInterface
public interface Criterion {
Stream<Widget> apply(Stream<Widget> s);
}
Criterion topN(Comparator<Widget> cmp, long n) {
return stream -> stream.sorted(cmp).limit(n);
}
Criterion topPercent(Comparator<Widget> cmp, double pct) {
return stream -> {
List<Widget> temp =
stream.sorted(cmp).collect(toList());
return temp.stream()
.limit((long)(temp.size() * pct));
};
}
My questions here are :
我的问题是:
[1] How to get top items from 3 to 7 from a stream with certain amount of items, so if the stream has items from A1, A2 .. A10, the call to
[1] 如何从具有一定数量项目的流中获取从 3 到 7 的顶级项目,因此如果流具有来自 A1、A2 .. A10 的项目,则调用
topNFromRange(Comparator<Widget> cmp, long from, long to) = topNFromRange(comparing(Widget::length), 3L, 7L)
will return { A3, A4, A5, A6, A7 }
将返回 { A3, A4, A5, A6, A7 }
The simplest way I can think of is get the top 7 [ T7 ] from original, get the top 3 [ T3 ] from original, and then get T7 - T3.
我能想到的最简单的方法是从原始获得前7个[ T7 ],从原始获得前3个[ T3 ],然后获得T7 - T3。
[2] How to get top items from top 10% to top 30% from a stream with certain amount of items, so if the stream has items from X1, X2 .. X100, the call to
[2] 如何从具有一定数量项目的流中获取从前 10% 到前 30% 的顶级项目,因此如果流具有来自 X1、X2 .. X100 的项目,则调用
topPercentFromRange(Comparator<Widget> cmp, double from, double to) = topNFromRange(comparing(Widget::length), 0.10, 0.30)
will return { X10, X11, X12, ..., X29, X30 }
将返回 { X10, X11, X12, ..., X29, X30 }
The simplest way I can think of is get the top 30% [ TP30 ] from original, get the top 10% [ TP10 ] from original, and then get TP30 - TP10.
我能想到的最简单的方法是从原始获取前 30% [ TP30 ],从原始获取前 10% [ TP10 ],然后获取 TP30 - TP10。
What are some better ways to use Java 8 Lambda to concisely express the above situations ?
有什么更好的方法可以使用 Java 8 Lambda 来简洁地表达上述情况?
采纳答案by Stuart Marks
User skiwi already answeredthe first part of the question. The second part is:
用户skiwi 已经回答了问题的第一部分。第二部分是:
(2) How to get top items from top 10% to top 30% from a stream with certain amount of items....
(2) 如何从具有一定数量项目的流中获取从前 10% 到前 30% 的顶级项目....
To do this, you have to use a similar technique as topPercent
in my answerto the other question. That is, you have to collect the elements into a list in order to be able to get a count of the elements, possibly after some upstream filtering has been done.
为此,您必须使用与topPercent
我对另一个问题的回答类似的技术。也就是说,您必须将元素收集到一个列表中,以便能够获得元素的计数,这可能是在某些上游过滤完成之后。
Once you have the count, then you compute the right values for skip
and limit
based on the count and the percentages you want. Something like this might work:
一旦你的计数,然后你计算出正确的值skip
,并limit
基于计数和您想要的百分比。像这样的事情可能会奏效:
Criterion topPercentFromRange(Comparator<Widget> cmp, double from, double to) {
return stream -> {
List<Widget> temp =
stream.sorted(cmp).collect(toList());
return temp.stream()
.skip((long)(temp.size() * from))
.limit((long)(temp.size() * (to - from)));
};
}
Of course you will have to do error checking on from
and to
. A more subtle problem is determining how many elements to emit. For example, if you have ten elements, they are at indexes [0..9], which correspond to 0%, 10%, 20%, ..., 90%. But if you were to ask for a range from 9% to 11%, the above code would emit no elements at all, not the one at 10% like you might expect. So some tinkering with the percentage computations is probably necessary to fit the semantics of what you're trying to do.
当然,您必须对from
和进行错误检查to
。一个更微妙的问题是确定要发射多少个元素。例如,如果您有十个元素,它们位于索引 [0..9] 处,对应于 0%、10%、20%、...、90%。但是,如果您要求 9% 到 11% 的范围,上面的代码将根本不会发出任何元素,而不是像您期望的那样在 10% 处发出任何元素。因此,可能需要对百分比计算进行一些修改以适应您尝试执行的操作的语义。
回答by skiwi
To get a range from a Stream<T>
, you can use skip(long n)
to first skip a set number of elements, and then you can call limit(long n)
to only take a specific amount of items.
要从 a 获取范围Stream<T>
,您可以使用skip(long n)
来首先跳过一定数量的元素,然后您可以调用limit(long n)
只获取特定数量的项目。
Consider a stream with 10 elements, then to get elements 3 to 7, you would normally call from a List
:
考虑一个包含 10 个元素的流,然后要获取元素 3 到 7,您通常会从 a 调用List
:
list.subList(3, 7);
Now with a Stream
, you need to first skip 3 items, and then take 7 - 3 = 4 items, so it becomes:
现在有了 a Stream
,你需要先跳过 3 个项目,然后取 7 - 3 = 4 个项目,所以它变成:
stream.skip(3).limit(4);
As a variant to @StuartMarks' solution to the second answer, I'll offer you the following solution which leaves the possibility to chain intact, it works similar to how @StuartMarks does it:
作为@StuartMarks 对第二个答案的解决方案的一种变体,我将为您提供以下解决方案,它可以保持链接完整的可能性,它的工作方式类似于@StuartMarks 的工作方式:
private <T> Collector<T, ?, Stream<T>> topPercentFromRangeCollector(Comparator<T> comparator, double from, double to) {
return Collectors.collectingAndThen(
Collectors.toList(),
list -> list.stream()
.sorted(comparator)
.skip((long)(list.size() * from))
.limit((long)(list.size() * (to - from)))
);
}
and
和
IntStream.range(0, 100)
.boxed()
.collect(topPercentFromRangeCollector(Comparator.comparingInt(i -> i), 0.1d, 0.3d))
.forEach(System.out::println);
This will print the elements 10 through 29.
这将打印元素 10 到 29。
It works by using a Collector<T, ?, Stream<T>>
that takes in your elements from the stream, transforms them into a List<T>
, then obtains a Stream<T>
, sorts it and applies the (correct) bounds to it.
它的工作原理是使用 aCollector<T, ?, Stream<T>>
从流中接收元素,将它们转换为 a List<T>
,然后获取 a Stream<T>
,对其进行排序并对其应用(正确的)边界。