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

Ole*_*lov 14 java java-stream

我有一个Collection任意类的元素.我想迭代遍历集合并使用元素和集合中的每个其他元素逐个执行一些操作(不包括元素本身).让它List<Integer>为简单起见:

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Run Code Online (Sandbox Code Playgroud)

有了for循环,它将是:

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

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

这就是我所得到的:

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

但它看起来并不比嵌套循环好.有更优雅的方式吗?

Tun*_*aki 14

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

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

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

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


Hol*_*ger 6

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

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

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

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);
Run Code Online (Sandbox Code Playgroud)

如果你不想计算的数值,但创造了对一个对象,它变得更复杂一些,由于这样的事实IntStream没有任何flatMapToObj操作,所以我们需要的组合mapToObj以及flatMap(除非我们使用boxed).因此,构建一个双长度数组示例如下:

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]));
Run Code Online (Sandbox Code Playgroud)

当然,对于一个简单的列表[ 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);
Run Code Online (Sandbox Code Playgroud)