java 从流中获取最后 n 个元素

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30476127/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 17:11:23  来源:igfitidea点击:

Get last n elements from stream

javajava-8java-stream

提问by ytterrr

I am wondering is there an alternative to

我想知道有没有替代方案

List<X> lastN = all.subList(Math.max(0, all.size() - n), all.size());

with streamusage?

使用

采纳答案by Tagir Valeev

A custom collector can be written like this:

自定义收集器可以这样编写:

public static <T> Collector<T, ?, List<T>> lastN(int n) {
    return Collector.<T, Deque<T>, List<T>>of(ArrayDeque::new, (acc, t) -> {
        if(acc.size() == n)
            acc.pollFirst();
        acc.add(t);
    }, (acc1, acc2) -> {
        while(acc2.size() < n && !acc1.isEmpty()) {
            acc2.addFirst(acc1.pollLast());
        }
        return acc2;
    }, ArrayList::new);
}

And use it like this:

并像这样使用它:

List<String> lastTen = input.stream().collect(lastN(10));

回答by Jordi Castilla

Use Stream.skip()

使用Stream.skip()

Returns a stream consisting of the remaining elements of this stream after discarding the first n elements of the stream. If this stream contains fewer than n elements then an empty stream will be returned.

丢弃流的前 n 个元素后,返回由该流的其余元素组成的流。如果此流包含少于 n 个元素,则将返回一个空流。

all.stream().skip(Math.max(0, all.size() - n)).forEach(doSomething);

回答by tobias_k

In case the stream has unknown size, there's probably no way around consuming the entire stream and buffering the last nelements encountered so far. You can do this using some kind of deque, or a specialized ring-buffer automatically maintaining its maximum size (see this related questionfor some implementations).

如果流的大小未知,则可能无法消耗整个流并缓冲目前n遇到的最后一个元素。您可以使用某种双端队列或自动保持其最大大小的专用环形缓冲区来执行此操作(有关某些实现,请参阅此相关问题)。

public static <T> List<T> lastN(Stream<T> stream, int n) {
    Deque<T> result = new ArrayDeque<>(n);
    stream.forEachOrdered(x -> {
        if (result.size() == n) {
            result.pop();
        }
        result.add(x);
    });
    return new ArrayList<>(result);
}

All of those operations (size, pop, add) should have complexity of O(1), so the overall complexity for a stream with (unknown) length nwould be O(n).

所有这些操作 ( size, pop, add) 的复杂度应该为O(1),因此(未知)长度为n的流的整体复杂度将为O(n)

回答by Gebezs

Sometimes I need a "oneliner" (in this case a three liner) as creating a collector is just too much fuss.

有时我需要一个“oneliner”(在这种情况下是三个 liner),因为创建一个收集器太麻烦了。

If the stream is small then it is possible to reverse, limitand reverseagain without much sacrificing performance. This will result the last n elements.

如果流很小,则可以reverselimit并且reverse同样不会牺牲太多性能。这将产生最后 n 个元素。

It is useful if filtering is required as in that case it is not possible to specify the size.

如果需要过滤,这很有用,因为在这种情况下无法指定大小。

Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9)
  .filter(i -> i % 2 == 0)
  .sorted(Comparator.reverseOrder())
  .limit(2)
  .sorted(Comparator.reverseOrder())
  .forEach(System.out::println); // prints 8 6