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

2022-08-31 07:42:55

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

其中 是 :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);

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


答案 1

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

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

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

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

  • Stream::reduce 方法的 Javadoc 指出,它不受按顺序执行的约束”。
  • Javadoc还要求“累加器函数必须是用于组合两个值的关联非干扰无状态函数”,这显然是lambda表达式的情况。(first, second) -> second
  • 用于约简操作的 Javadoc 指出:“流类具有多种形式的一般约简操作,称为 reduce()collect() [..]”“只要用于处理元素的函数是关联无状态的,正确构造的 reduce 操作本质上是可并行化的

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


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

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

答案 2

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

Iterables.getLast(myIterable)

作为方便的一个衬里。