假设我有一个这样的列表:
Suppose I have a list like this :
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]是否可以使用Java 8流来获取每一个元素此列表获取以下内容?
Is it possible to use a Java 8 stream to take every second element from this list to obtain the following?
[1, 3, 5, 7, 9]或者甚至每隔三个元素?
Or maybe even every third element?
[1, 4, 7, 10]基本上,我正在寻找一个功能获取流的每个第n个元素:
Basically, I'm looking for a function to take every nth element of a stream:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); List<Integer> list2 = list.stream().takenth(3).collect(Collectors.toList()); System.out.println(list2); // => [1, 4, 7, 10]推荐答案
其中一个引入Java流的主要动机是允许并行操作。这导致要求对Java流(例如 map 和 filter )的操作独立于项目的位置流或周围的项目。这具有使分割流易于并行处理的优点。它的缺点是使某些操作更复杂。
One of the prime motivations for the introduction of Java streams was to allow parallel operations. This led to a requirement that operations on Java streams such as map and filter be independent of the position of the item in the stream or the items around it. This has the advantage of making it easy to split streams for parallel processing. It has the disadvantage of making certain operations more complex.
所以简单的答案就是没有简单的方法来做一些事情,比如每个项目或每个项目的映射以前所有项目的总和。
So the simple answer is that there is no easy way to do things such as take every nth item or map each item to the sum of all previous items.
实现您的要求的最简单方法是使用您要传输的列表的索引:
The most straightforward way to implement your requirement is to use the index of the list you are streaming from:
List<String> list = ...; return IntStream.range(0, list.size()) .filter(n -> n % 3 == 0) .mapToObj(list::get) .collect(Collectors.toList());更复杂的解决方案是创建一个自定义收集器,将每个第n个项目收集到一个列表中。
A more complicated solution would be to create a custom collector that collects every nth item into a list.
class EveryNth<C> { private final int nth; private final List<List<C>> lists = new ArrayList<>(); private int next = 0; private EveryNth(int nth) { this.nth = nth; IntStream.range(0, nth).forEach(i -> lists.add(new ArrayList<>())); } private void accept(C item) { lists.get(next++ % nth).add(item); } private EveryNth<C> combine(EveryNth<C> other) { other.lists.forEach(l -> lists.get(next++ % nth).addAll(l)); next += other.next; return this; } private List<C> getResult() { return lists.get(0); } public static Collector<Integer, ?, List<Integer>> collector(int nth) { return Collector.of(() -> new EveryNth(nth), EveryNth::accept, EveryNth::combine, EveryNth::getResult)); }可以按如下方式使用:
List<String> list = Arrays.asList("Anne", "Bill", "Chris", "Dean", "Eve", "Fred", "George"); list.stream().parallel().collect(EveryNth.collector(3)).forEach(System.out::println);返回您期望的结果。
即使使用并行处理,这也是一种非常低效的算法。它将它接受的所有项目拆分为n个列表,然后只返回第一个。不幸的是,它必须通过累积过程保留所有项目,因为直到它们组合起来它才知道哪个列表是第n个。鉴于其复杂性和低效率,我肯定会建议坚持使用上面基于指数的解决方案而不是这个。
This is a very inefficient algorithm even with parallel processing. It splits all items it accepts into n lists and then just returns the first. Unfortunately it has to keep all items through the accumulation process because it's not until they are combined that it knows which list is the nth one. Given its complexity and inefficiency I would definitely recommending sticking with the indices based solution above in preference to this.
更多推荐
从Java 8流中获取每个第n个元素
发布评论