Java 8 流:处理列表中每一对可能的元素

2022-09-03 13:42:41

我有一个任意类的元素。我想循环访问集合,并使用元素和集合中的其他元素(不包括元素本身)执行一些操作。为了简单起见,让它成为:CollectionList<Integer>

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);

使用循环,它将是:for

for (Integer i : list) {
    for (Integer j : list) {
        if (!i.equals(j)) System.out.println(i * 2 + j);  //just for example
    }
}

问题是如何使用流 API 执行此操作?

这就是我得出的结论:

list.stream().forEach(i ->
    list.stream().forEach(j -> {
        if (!i.equals(j)) System.out.println(i * 2 + j);
    })
);

不过,它看起来并不比嵌套循环更好。有没有更优雅的方式?


答案 1

您可以使用 flatMap 操作执行此操作:

list.stream()
    .flatMap(i -> list.stream().filter(j -> !i.equals(j)).map(j -> i * 2 + j))
    .forEach(System.out::println);

此代码正在创建输入列表的流。它将列表的每个元素与由同一列表创建的流进行平面映射,其中当前元素被过滤掉,并且此新列表的每个元素都是操作的结果。i * 2 + j

然后将所有元素打印到控制台。


答案 2

您可以通过事先映射到值来避免对象比较,例如int

list.stream().mapToInt(Integer::intValue)
    .flatMap(i -> list.stream().filter(j -> j!=i).mapToInt(j -> i*2 + j))
    .forEach(System.out::println);

但实际上,您正在对不变的东西执行条件运算和相等性检查。由于整个操作依赖于源列表,而不是在两者之间更改其内容,因此您可以首先简单地生成配对列表索引:

final int end=list.size();
IntStream.range(0, end).flatMap(i ->
    IntStream.concat(IntStream.range(0, i), IntStream.range(i+1, end))
        .map(j -> list.get(i) * 2 + list.get(j)))
    .forEach(System.out::println);

如果您不想计算数值,而是为对创建对象,由于没有操作,因此它会变得更加复杂,因此我们需要和的组合(除非我们使用)。因此,构造一个双长度数组的示例如下所示:IntStreamflatMapToObjmapToObjflatMapboxed

IntStream.range(0, end).mapToObj(i ->
    IntStream.concat(IntStream.range(0, i), IntStream.range(i+1, end))
        .mapToObj(j -> new int[]{ list.get(i), list.get(j)}))
        .flatMap(Function.identity())
    .forEach(a -> System.out.println(a[0]*2+a[1]));

当然,对于像 这样简单的列表,我们可以首先使用,而不处理:[ 1, 2, 3, 4, 5 ]IntStream.rangeClosed(1, 5)List

IntStream.rangeClosed(1, 5).flatMap(i ->
        IntStream.concat(IntStream.range(1, i), IntStream.rangeClosed(i+1, 5))
        .map(j -> i*2 + j))
    .forEach(System.out::println);

推荐