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

时间:2014-01-29 09:25:07

标签: java list java-8 java-stream

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

其中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?

正如您所看到的那样,获得第一个元素并使用某个filter并不难。

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

  • 我似乎无法直接从Stream获取它。 (这只对有限流有意义)
  • 您似乎无法从first()界面获取last()List等内容,这真的很痛苦。

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

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

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

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

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

8 个答案:

答案 0 :(得分:154)

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

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

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

该实施适用于顺序并行流。乍一看可能会令人惊讶,遗憾的是文档没有明确说明。但是,它是流的一个重要特征,我试着澄清它:

  • 方法Stream::reduce的Javadoc指出,约束执行顺序
  • Javadoc还要求“累加器功能必须是关联非干扰无状态功能才能合并两个值“,这显然是lambda表达式(first, second) -> second的情况。
  • reduction operations状态的Javadoc:“流类有多种形式的常规缩减操作,称为reduce()collect() [..]”“正确构造的reduce操作固有可并行化,只要用于处理元素的函数是associativestateless。”

密切相关的Collectors的文档更加明确:“确保顺序并行执行生成等效结果,收集器函数必须满足标识和associativity约束。“


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

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

答案 1 :(得分:36)

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

Iterables.getLast(myIterable)

作为一个方便的oneliner。

答案 2 :(得分:9)

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

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

答案 3 :(得分:0)

番石榴针对这种情况有专门的方法:

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

它等效于stream.reduce((a, b) -> b),但创作者声称它的性能要好得多。

来自documentation

  

此方法的运行时将在O(log n)和O(n)之间,执行   在可有效拆分的流上效果更好。

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

答案 4 :(得分:0)

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

var delegate: ExploreDelegate?

另一种选择是使用将身份作为队列的reduce操作。

CategoryItem

答案 5 :(得分:0)

获取最后一个元素的另一种方法是使用sort。

    Optional<CArea> num=data.careas.stream().sorted((a,b)->-1).findFirst();

答案 6 :(得分:0)

list.stream().sorted(Comparator.comparing(obj::getSequence).reversed()).findFirst().get();

颠倒顺序并从列表中获取第一个元素。这里的对象有序列号,Comparator 提供了多种功能,可以按逻辑使用。

答案 7 :(得分:-1)

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

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

使用非常简单。