在上一个问题中如何在Java 8中动态进行过滤?StuartMarks给出了一个很好的答案,并提供了一些有用的实用程序来处理从流中选择topN和topPercent。
我将从他的原始答案中将它们包括在这里:
@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)); }; }
我的问题是:
[1]如何从具有一定数量项目的流中获取3到7的顶级项目,因此,如果流中有A1,A2 .... A10中的项目,则调用
topNFromRange(Comparator<Widget> cmp, long from, long to) = topNFromRange(comparing(Widget::length), 3L, 7L)
将返回{A3,A4,A5,A6,A7}
我能想到的最简单的方法是从原始文件中获取前7个[T7],从原始文件中获取前3个[T3],然后获取T7-T3。
[2]如何从具有一定数量的商品的流中获取前10%到前30%的热门商品,因此,如果流中有X1,X2 .... X100中的商品,则调用
topPercentFromRange(Comparator<Widget> cmp, double from, double to) = topNFromRange(comparing(Widget::length), 0.10, 0.30)
将返回{X10,X11,X12,…,X29,X30}
我能想到的最简单的方法是从原始文件中获取前30%的[TP30],从原始文件中获取前10%的[TP10],然后获取TP30-TP10。
有什么更好的方法使用Java 8 Lambda来简洁地表达上述情况?
用户skiwi已经回答了问题的第一部分。第二部分是:
(2)如何从具有一定项目量的流中将前10%到前30%的顶级项目获取....
为此,您必须使用与topPercent我对其他问题的回答中类似的技术。也就是说,您必须将元素收集到一个列表中,以便可能在完成一些上游筛选之后获得元素的计数。
topPercent
有了计数后,便可以根据计数和所需的百分比为skip并计算正确的值limit。这样的事情可能会起作用:
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))); }; }
当然,您将必须对from和进行错误检查to。一个更微妙的问题是确定要发射多少个元素。例如,如果您有十个元素,则它们的索引为[0..9],分别对应于0%,10%,20%,…,90%。但是,如果您要求的范围是9%到11%,则上面的代码将根本不发出任何元素,而不像您期望的那样发出10%的元素。因此,可能有必要对百分比计算进行一些修改,以适应您要尝试执行的语义。
from
to
要从中获得一个范围Stream<T>,您可以使用skip(longn)来先跳过一定数量的元素,然后调用limit(long n)以仅接受特定数量的项目。
Stream<T>
考虑一个包含10个元素的流,然后要获得3到7个元素,通常可以从调用List:
list.subList(3, 7);
现在使用Stream,您需要先跳过3个项目,然后取7-3 = 4个项目,所以它变成:
stream.skip(3).limit(4);
作为第二个答案的@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))) ); }
和
IntStream.range(0, 100) .boxed() .collect(topPercentFromRangeCollector(Comparator.comparingInt(i -> i), 0.1d, 0.3d)) .forEach(System.out::println);
这将打印元素10到29。
它通过使用aCollector<T, ?, Stream<T>>来工作,该a从流中获取您的元素,将其转换为List<T>,然后获得a Stream<T>,对其进行排序并对其应用(正确)范围。
aCollector<T, ?, Stream<T>>
List<T>
a Stream<T>