在一行中获取流/列表的最后一个元素

时间:2022-12-27 22:12:40

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

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

Where data.careas is a List<CArea>:

其中data.careas是List

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.

正如您所看到的,使用某个过滤器获取第一个元素并不难。

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)
  • 我似乎无法直接从Stream获取它。 (它只对有限流有意义)
  • It also seems that you cannot get things like first() and last() from the List interface, which is really a pain.
  • 似乎你不能从List界面获得像first()和last()这样的东西,这真的很痛苦。

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

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

But as per original answer: How to get the last element of a finite 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 indexOf on every element, which is most likely not you generally want as it can impair performance.

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

7 个解决方案

#1


122  

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 unordered streams it is for obvious reasons unspecified which element will be returned.

此实现适用于所有有序流(包括从列表创建的流)。对于无序流,明显的原因是未指定将返回哪个元素。

The implementation works for both sequential and 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::reduce states, that it "is not constrained to execute sequentially".
  • 方法Stream :: reduce状态的Javadoc,它“不限于按顺序执行”。
  • The Javadoc also requires that the "accumulator function must be an associative, non-interfering, stateless function for combining two values", which is obviously the case for the lambda expression (first, second) -> second.
  • Javadoc还要求“累加器函数必须是用于组合两个值的关联,非干扰,无状态函数”,这显然是lambda表达式(第一个,第二个) - >第二个的情况。
  • The Javadoc for reduction operations states: "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 associative and stateless."
  • 减少操作的Javadoc声明:“流类有多种形式的通用约简操作,称为reduce()和collect()[..]”和“正确构造的reduce操作本质上是可并行化的,只要函数(s)用于处理元素是联想和无状态的。“

The documentation for the closely related Collectors is even more explicit: "To ensure that sequential and parallel executions produce equivalent results, the collector functions must satisfy an identity and an associativity constraints."

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


Back to the original question: The following code stores a reference to the last element in the variable last and 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();

#2


24  

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

如果您有一个Collection(或更一般的Iterable),您可以使用Google Guava

Iterables.getLast(myIterable)

as handy oneliner.

作为方便的oneliner。

#3


7  

One liner (no need for stream;):

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

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

#4


0  

As it is unclear whether it is part of the API specification for reduce to obey encounter order, what about:

由于尚不清楚它是否是减少服从遭遇顺序的API规范的一部分,那么:

CArea last = data.careas.stream()
    .filter(c -> c.bbox.orientationHorizontal)
    .max((e1, e2) -> data.careas.indexOf(e1) - data.careas.indexOf(e2)).get();

#5


0  

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.

它使用起来非常简单。

#6


-6  

As mentioned by @nosid, it can be done using reduce. Another way of doing this would be to reverse the stream and then get the first element

正如@nosid所提到的,可以使用reduce来完成。另一种方法是反转流然后获取第一个元素

CArea last = data.careas.stream()
.filter(c -> c.bbox.orientationHorizontal)
.sorted((a, b)-> -1)
.findFirst().get();

#7


-7  

You can use java.util.Collections using the following static method:

您可以使用以下静态方法使用java.util.Collections:

Collections.max(yourList);

From docs:

来自docs:

Returns the maximum element of the given collection, according to the * natural ordering of its elements. [...]

根据元素的自然顺序返回给定集合的最大元素。 [...]

#1


122  

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 unordered streams it is for obvious reasons unspecified which element will be returned.

此实现适用于所有有序流(包括从列表创建的流)。对于无序流,明显的原因是未指定将返回哪个元素。

The implementation works for both sequential and 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::reduce states, that it "is not constrained to execute sequentially".
  • 方法Stream :: reduce状态的Javadoc,它“不限于按顺序执行”。
  • The Javadoc also requires that the "accumulator function must be an associative, non-interfering, stateless function for combining two values", which is obviously the case for the lambda expression (first, second) -> second.
  • Javadoc还要求“累加器函数必须是用于组合两个值的关联,非干扰,无状态函数”,这显然是lambda表达式(第一个,第二个) - >第二个的情况。
  • The Javadoc for reduction operations states: "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 associative and stateless."
  • 减少操作的Javadoc声明:“流类有多种形式的通用约简操作,称为reduce()和collect()[..]”和“正确构造的reduce操作本质上是可并行化的,只要函数(s)用于处理元素是联想和无状态的。“

The documentation for the closely related Collectors is even more explicit: "To ensure that sequential and parallel executions produce equivalent results, the collector functions must satisfy an identity and an associativity constraints."

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


Back to the original question: The following code stores a reference to the last element in the variable last and 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();

#2


24  

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

如果您有一个Collection(或更一般的Iterable),您可以使用Google Guava

Iterables.getLast(myIterable)

as handy oneliner.

作为方便的oneliner。

#3


7  

One liner (no need for stream;):

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

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

#4


0  

As it is unclear whether it is part of the API specification for reduce to obey encounter order, what about:

由于尚不清楚它是否是减少服从遭遇顺序的API规范的一部分,那么:

CArea last = data.careas.stream()
    .filter(c -> c.bbox.orientationHorizontal)
    .max((e1, e2) -> data.careas.indexOf(e1) - data.careas.indexOf(e2)).get();

#5


0  

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.

它使用起来非常简单。

#6


-6  

As mentioned by @nosid, it can be done using reduce. Another way of doing this would be to reverse the stream and then get the first element

正如@nosid所提到的,可以使用reduce来完成。另一种方法是反转流然后获取第一个元素

CArea last = data.careas.stream()
.filter(c -> c.bbox.orientationHorizontal)
.sorted((a, b)-> -1)
.findFirst().get();

#7


-7  

You can use java.util.Collections using the following static method:

您可以使用以下静态方法使用java.util.Collections:

Collections.max(yourList);

From docs:

来自docs:

Returns the maximum element of the given collection, according to the * natural ordering of its elements. [...]

根据元素的自然顺序返回给定集合的最大元素。 [...]