在一行程序中获取 Stream/List 的最后一个元素

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

其中 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获得它。(它只对有限的流有意义)
  • 看起来你也不能从 List接口得到像 first()last()这样的东西,这真的很痛苦。

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

但是根据最初的答案: 如何得到有限 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,这很可能不是您通常想要的,因为它会损害性能。

137296 次浏览

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

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

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

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

  • 方法 流: : reduce的 Javadoc 声明它是 “ is < strong > not 强制执行 < strong > 顺序 ”
  • Javadoc 还要求使用 “累加器函数必须是一个 联合 非干扰 无状态 函数,用于两个值的结合”,显然对于 lambda 表达式 (first, second) -> second就是这种情况。
  • 用于 减少运作状态的 Javadoc: 这些流类具有多种形式的一般约简操作,称为 ref = “ http://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html # reduce-java.util.function. BinaryOperator-”rel = “ noReferrer”> reduce () 和 ref = “ http://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html # Collection-java.util.stream. Collector-”rel = “ noReferrer”> Collection () 。.]"正确构造的 reduce 操作是 < strong > 固有的并行化 ,只要用于处理元素的函数是 < a href = “ http://docs.oracle.com/javase/8/docs/api/java/util/stream/package-Summary y.html # Associative”rel = “ noReferrer”> sociative 和 < a href = “ http://docs.oracle.com/javase/8/docs/api/java/util/stream/package-sumy.html # NonInterfering”rel = “ noReferrer”> statless ”

收藏家密切相关的文档甚至更加明确: “为了确保并行执行 < strong > 连续的 和 < strong > 产生 < strong > 等效的结果 ,收集器函数必须满足一个标识和一个 < a href = “ http://docs.oracle.com/javase/8/docs/api/java/util/stream/package-Summary y.html # Associative”rel = “ noReferrer”> 关联性 约束。”


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

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

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

Iterables.getLast(myIterable)

作为方便的线条。

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

Object lastElement = list.isEmpty() ? null : list.get(list.size()-1);

您还可以使用如下跳过()函数..。

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

用起来超级简单。

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

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

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

来自 文件:

此方法的运行时将在 O (logn)和 O (n)之间,执行 更适合有效地分割流。

值得一提的是,如果 stream 是无序的,那么这个方法的行为类似于 findAny()

如果需要获取最后 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();

另一种选择是使用 reduce 操作,将标识作为 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);

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

    Optional<CArea> num=data.careas.stream().sorted((a,b)->-1).findFirst();
list.stream().sorted(Comparator.comparing(obj::getSequence).reversed()).findFirst().get();

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

还有一个办法,第一个和最后一个要素是:

    List<Object> pair = new ArrayList<>();
dataStream.ForEach(o -> {
if (pair.size() == 0) {
pair.add(o);
pair.add(o);
}
pair.set(1, o);
});