Java 以单行方式获取 Stream/List 的最后一个元素

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

Get last element of Stream/List in a one-liner

javalistjava-8java-stream

提问by skiwi

How can I get the last element of a stream or list in the following code?

如何在以下代码中获取流或列表的最后一个元素?

Where data.careasis a List<CArea>:

哪里data.careasList<CArea>

CArea first = data.careas.stream()
                  .filter(c -> c.bbox.orientationHorizontal).findFirst().get();

CArea last = data.careas.stream()
                 .filter(c -> c.bbox.orientationHorizontal)
                 .collect(Collectors.toList()).; //how to?

As you can see getting the first element, with a certain filter, is not hard.

如您所见,获取第一个元素并带有某个filter并不难。

However getting the last element in a one-liner is a real pain:

然而,在单行中获取最后一个元素是一个真正的痛苦:

  • It seems I cannot obtain it directly from a Stream. (It would only make sense for finite streams)
  • It also seems that you cannot get things like first()and last()from the Listinterface, which is really a pain.
  • 似乎我无法直接从Stream. (它只对有限流有意义)
  • 似乎也无法从界面中获取first()和获取类似的东西,这真的很痛苦。last()List

I do not see any argument for not providing a first()and last()method in the Listinterface, as the elements in there, are ordered, and moreover the size is known.

我没有看到在接口中不提供first()andlast()方法的任何论据List,因为那里的元素是有序的,而且大小是已知的。

But as per the original answer: How to get the last element of a finite Stream?

但根据原始答案:如何获得有限的最后一个元素Stream

Personally, this is the closest I could get:

就个人而言,这是我能得到的最接近的:

int lastIndex = data.careas.stream()
        .filter(c -> c.bbox.orientationHorizontal)
        .mapToInt(c -> data.careas.indexOf(c)).max().getAsInt();
CArea last = data.careas.get(lastIndex);

However it does involve, using an indexOfon every element, which is most likely not you generally want as it can impair performance.

但是,它确实涉及indexOf在每个元素上使用,这很可能不是您通常想要的,因为它会损害性能。

采纳答案by nosid

It is possible to get the last element with the method Stream::reduce. The following listing contains a minimal example for the general case:

可以使用Stream::reduce方法获取最后一个元素。以下清单包含一般情况的最小示例:

Stream<T> stream = ...; // sequential or parallel stream
Optional<T> last = stream.reduce((first, second) -> second);

This implementations works for all ordered streams(including streams created from Lists). For unorderedstreams it is for obvious reasons unspecified which element will be returned.

此实现适用于所有有序流(包括从Lists创建的流)。对于无序流,由于显而易见的原因,未指定将返回哪个元素。

The implementation works for both sequentialand parallel streams. That might be surprising at first glance, and unfortunately the documentation doesn't state it explicitly. However, it is an important feature of streams, and I try to clarify it:

该实现适用于顺序并行流。乍一看这可能令人惊讶,不幸的是文档没有明确说明。然而,它是流的一个重要特性,我试图澄清它:

  • The Javadoc for the method Stream::reducestates, that it "is notconstrained to execute sequentially".
  • The Javadoc also requires that the "accumulator function must be an associative, non-interfering, statelessfunction for combining two values", which is obviously the case for the lambda expression (first, second) -> second.
  • The Javadoc for reduction operationsstates: "The streams classes have multiple forms of general reduction operations, called reduce()and collect()[..]"and "a properly constructed reduce operation is inherently parallelizable, so long as the function(s) used to process the elements are associativeand stateless."
  • Stream::reduce方法的 Javadoc指出,它不受限于顺序执行”
  • Javadoc 还要求“累加器函数必须是用于组合两个值的关联的无干扰的无状态的函数”,这显然是 lambda 表达式的情况(first, second) -> second
  • 简化操作的 Javadoc指出:“流类具有多种形式的通用归约操作,称为reduce()collect()[..]”“正确构造的归约操作本质上是可并行化的,只要函数) 用于处理的元素是关联的无状态的。”

The documentation for the closely related Collectorsis even more explicit: "To ensure that sequentialand parallel executionsproduce equivalent results, the collector functions must satisfy an identity and an associativityconstraints."

密切相关的收集器的文档更加明确:“为了确保顺序并行执行产生相同的结果,收集器函数必须满足同一性和关联性约束。”



Back to the original question: The following code stores a reference to the last element in the variable lastand throws an exception if the stream is empty. The complexity is linear in the length of the stream.

回到最初的问题:以下代码存储对变量中最后一个元素的引用,last如果流为空则抛出异常。复杂度与流的长度呈线性关系。

CArea last = data.careas
                 .stream()
                 .filter(c -> c.bbox.orientationHorizontal)
                 .reduce((first, second) -> second).get();

回答by Peti

If you have a Collection (or more general an Iterable) you can use Google Guava's

如果你有一个集合(或更一般的迭代),你可以使用谷歌番石榴

Iterables.getLast(myIterable)

as handy oneliner.

作为方便的oneliner。

回答by nimo23

One liner (no need for stream;):

一个班轮(不需要流;):

Object lastElement = list.get(list.size()-1);

回答by Parag Vaidya

You can also use skip() function as below...

您还可以使用 skip() 函数,如下所示...

long count = data.careas.count();
CArea last = data.careas.stream().skip(count - 1).findFirst().get();

it's super simple to use.

使用起来超级简单。

回答by k13i

Guava has dedicated method for this case:

Guava 有专门的方法来处理这种情况:

Stream<T> stream = ...;
Optional<T> lastItem = Streams.findLast(stream);

It's equivalent to stream.reduce((a, b) -> b)but creators claim it has much better performance.

它相当于stream.reduce((a, b) -> b)但创作者声称它具有更好的性能。

From documentation:

文档

This method's runtime will be between O(log n) and O(n), performing better on efficiently splittable streams.

此方法的运行时将介于 O(log n) 和 O(n) 之间,在高效可拆分流上表现更好。

It's worth to mention that if stream is unordered this method behaves like findAny().

值得一提的是,如果流是无序的,则此方法的行为类似于findAny().

回答by Himanshu Ahire

If you need to get the last N number of elements. Closure can be used. The below code maintains an external queue of fixed size until, the stream reaches the end.

如果需要获取最后 N 个元素。可以使用闭包。下面的代码维护一个固定大小的外部队列,直到流到达末尾。

    final Queue<Integer> queue = new LinkedList<>();
    final int N=5;
    list.stream().peek((z) -> {
        queue.offer(z);
        if (queue.size() > N)
            queue.poll();
    }).count();

Another option could be to use reduce operation using identity as a Queue.

另一种选择可能是使用使用身份作为队列的减少操作。

    final int lastN=3;
    Queue<Integer> reduce1 = list.stream()
    .reduce( 
        (Queue<Integer>)new LinkedList<Integer>(), 
        (m, n) -> {
            m.offer(n);
            if (m.size() > lastN)
               m.poll();
            return m;
    }, (m, n) -> m);

    System.out.println("reduce1 = " + reduce1);