Java 8 Stream:如何比较当前元素和下一个元素?

33

如何在Java 8 Stream中获取列表(List)的下一个元素?

当我遍历一个List时,我想要比较当前元素与列表中的下一个元素。

是否可以在Java 8 Stream中实现这个功能?


2
如何迭代?如何确定当前和下一个元素?为什么你觉得需要使用流来做这件事? - Sotirios Delimanolis
我的目标是比较列表中的两个连续元素,或者说我想检查它们是否相等。 - bhupen
5
这是一个任务,而不是一个目标。你做这件事一定有原因。比如说,如果两个元素相等,你想要做某些事情。有趣的是如何使用流来实现这个操作。类似形式的问题已经被提出过。 - a better oliver
6个回答

17

使用我免费的StreamEx库,您可以使用附加的pairMap中间操作处理流元素对。就像这样:

StreamEx.of(input).pairMap((current, next) -> doSomethingWith(current, next));

其中input是一个Collection、数组或Stream。例如,通过以下方式可以轻松检查输入是否已排序:

boolean isSorted = StreamEx.of(input)
                           .pairMap((current, next) -> next.compareTo(current))
                           .allMatch(cmp -> cmp >= 0);

此外还有 forPairs 终端操作,它是一个类似于 forEach 的操作,用于处理输入元素的所有配对:

StreamEx.of(input).forPairs((current, next) -> doSomethingWith(current, next));

这些功能可以与任何流源(无论是随机访问还是非随机访问)很好地配合,并且完全支持并行流。


太好了。感谢您的解释。 - bhupen

14

一种方法是生成索引的 IntStream,然后通过它们的索引获取 List 元素。这只有在 List 支持随机访问时才有效(即如果您的 List 是一个 LinkedList,那么这将是一个不好的想法,因为 list.get(i) 不需要常数时间)。

例如:

IntStream.range(0,list.size()-1).forEach(i -> {
    doSomething(list.get(i),list.get(i+1));
});

另一种方法是将最后一个元素存储在数组中:

List<Element> list = ...
Element[] arr = new Element[1];
list.stream().forEach(e -> {
    if (arr[0] != null)
        doSomething(arr[0],e); 
    arr[0]=e;
});

这只适用于顺序流。


在流中是否有一种方法可以跳过下一个迭代,即对于某些情况将forEach索引递增到i+2而不是i+1? - Stacky
@Stacky 你可以使用 IntStream.iterate 来实现类似于 for 循环的行为,并按照你想要的任何值增加计数器。例如:IntStream.iterate (1, i -> i < 10, i -> i + 2).forEach (System.out::println); - Eran
如果我需要有条件地递增i怎么办?例如:SomeClass classLevelObj = new SomeClass(); IntStream.iterate (1, i -> i < 10, i -> classLevelObj.cond ? i + 2 : i + 3).forEach(item -> { // do something }); - Stacky

9

Stream.reduce可以根据目标使用。正如您所说,您想比较连续的元素,以下内容将打印“Same 3”:

Stream.of(1,2,3,3).reduce((a,b)->{
    if(a==b) System.out.println("Same "+a);
    return b; // will be "a" for next reduction
});

一个例子可能会有所帮助 :) - jocull

2
如果你有一个整数列表,并且想要检查它们是否按升序排序,你可以像下面这样做:
@Test
public void test_listIsSorted() {

    // create integer list for testing purposes: 0,1,2,3, .., 10
    List<Integer> integerList = IntStream.range(0, 10)
            .boxed()
            .collect(Collectors.toList());

    // stream list and compare item n with n+1
    integerList.stream()
            .reduce((integer1, integer2) -> {
                assert integer1 < integer2 : "ordering must be ascending";
                // return second value (which will be processed as "integer1" in the next iteration
                return integer2;
            });

}

这将比较像(0,1),(1,2)这样的一对对。


0

我不得不做同样的事情,并比较流(原本是一个数组)中元素的差异。 所以我使用了一个方法作为UnaryOperator的参数,这是.map()期望的方式...对我来说,它没有任何特殊的小工具也能正常工作:

import java.util.Arrays;

class streamDiffUtil {
    public static void main(String[] args) {
        int[] elements = {1,2,5};
        int result = Arrays.stream(elements)
                .map(value -> calcMaxDiff(value, elements))
                .max()
                .getAsInt();
        System.out.println(result);
    }

    private static int calcMaxDiff(int j, int[] elements) {
        return Arrays.stream(elements)
                .map(value -> Math.abs(j-value))
                .max().getAsInt();
    }
}

很好的是,知道方法calcMaxDiff如何等同于.map签名中的UnaryIntOperator会很不错。这对我来说有点超出范围。 希望这可以帮到你。


0

你总是做以下之一:

  1. 将你的流转换为一个包含流中最后几个元素“历史”的元素流
  2. 以当前处理的元素视为“下一个”元素,以先前处理的元素视为“当前”元素来处理你的流。

这两种解决方案的实现可以在此线程中看到:是否可能在Stream中获取下一个元素?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接