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

ski*_*iwi 100 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?
Run Code Online (Sandbox Code Playgroud)

正如你所看到的那样,获得第一个元素filter并不难.

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

  • 好像我不能直接从a获得它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);
Run Code Online (Sandbox Code Playgroud)

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

nos*_*sid 167

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

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

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

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

  • 该方法的Javadoc 流::减少的状态,它"是没有限制的执行顺序 ".
  • Javadoc还要求"累加器函数必须是用于组合两个值的关联,非干扰,无状态函数",这显然是lambda表达式的情况(first, second) -> second.
  • 用于约简操作的Javadoc 声明:"流类具有多种形式的通用约简操作,称为reduce()collect() [...]""正确构造的reduce操作本质上是可并行化的,只要函数(s)用于处理元素是关联无状态的."

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


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

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

  • @skiwi可以使用任何合法的变量名称,例如:`.reduce(($,current)-&gt; current)或`.reduce((__,current)-&gt; current)`(双下划线)。 (2认同)
  • 从技术上讲,它可能不适用于任何流.您指向的文档以及`Stream.reduce(BinaryOperator <T>)`没有提及`reduce`是否遵守订单顺序,并且终端操作可以自由忽略遇到订单,即使订购了流也是如此.顺便说一句,"交换"这个词并没有出现在Stream javadocs中,所以它的缺席并没有告诉我们多少. (2认同)
  • @AleksandrDubinsky:确切地说,文档没有提到_commutative_,因为它与_reduce_操作无关.重要的部分是:"[..]正确构造的reduce操作本质上是可并行化的,只要用于处理元素的函数是关联的[...]." (2认同)
  • @Aleksandr Dubinsky:当然,这不是“规范的理论问题”。它使 `reduce((a,b)-&gt;b)` 成为获取最后一个元素(当然是有序流的)与否的正确解决方案之间的区别。Brian Goetz 的声明说明了一点,进一步 [API 文档](https://docs.oracle.com/javase/8/docs/api/?java/util/stream/package-summary.html) 指出` reduce("", String::concat)` 是字符串连接的一种低效但正确的解决方案,这意味着维护遇到的顺序。意图众所周知,文档必须赶上。 (2认同)

Pet*_*eti 37

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

Iterables.getLast(myIterable)
Run Code Online (Sandbox Code Playgroud)

作为方便的oneliner.


k13*_*13i 12

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

Stream<T> stream = ...;
Optional<T> lastItem = Streams.findLast(stream);
Run Code Online (Sandbox Code Playgroud)

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

文档

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

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


nim*_*o23 9

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

Object lastElement = list.get(list.size()-1);
Run Code Online (Sandbox Code Playgroud)

  • 如果list为空,则此代码将抛出`ArrayIndexOutOfBoundsException`. (21认同)

ele*_*esg 5

list.stream().sorted(Comparator.comparing(obj::getSequence).reversed()).findFirst().get();
Run Code Online (Sandbox Code Playgroud)

反转顺序并获取列表中的第一个元素。这里的对象有序列号,比较器提供了可以根据逻辑使用的多种功能。