java 获取n个的值,从Java 8流中获取每个第n个元素

Suppose I have a list like this :

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

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]

Basically, I'm looking for a function to take every nth element of a stream:

List list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

List list2 = list.stream().takenth(3).collect(Collectors.toList());

System.out.println(list2);

// => [1, 4, 7, 10]

解决方案

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 list = ...;

return IntStream.range(0, list.size())

.filter(n -> n % 3 == 0)

.mapToObj(list::get)

.collect(Collectors.toList());

A more complicated solution would be to create a custom collector that collects every nth item into a list.

class EveryNth {

private final int nth;

private final List> 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 combine(EveryNth other) {

other.lists.forEach(l -> lists.get(next++ % nth).addAll(l));

next += other.next;

return this;

}

private List getResult() {

return lists.get(0);

}

public static Collector> collector(int nth) {

return Collector.of(() -> new EveryNth(nth),

EveryNth::accept, EveryNth::combine, EveryNth::getResult));

}

This could be used as follows:

List list = Arrays.asList("Anne", "Bill", "Chris", "Dean", "Eve", "Fred", "George");

list.stream().parallel().collect(EveryNth.collector(3)).forEach(System.out::println);

Which returns the result you would expect.

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.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值