java 8 stream如何找到2个列表的元素之间的最小差异

nik*_*abu 5 java-8 java-stream

我是Streams的新手Java 8,目前正在尝试解决这个问题,我有两个列表如下:

List<Integer> list1 = Arrays.asList(5, 11,17,123);
List<Integer> list2 = Arrays.asList(124,14,80);
Run Code Online (Sandbox Code Playgroud)

我想找到这些列表中所有元素之间存在的绝对最小差异.

预期结果: 1(124-123=1)

使用Java 7实现它不是问题,但我如何使用Java8的Streams实现它?我如何迭代forEach元素List1,也forEach来自List2和保持最小值?

SEY*_*_91 7

试试这个吧

public static void main(String[] args) {
        List<Integer> list1 = Arrays.asList(5, 11,17,123); 
        List<Integer> list2 = Arrays.asList(124,14,80);
        OptionalInt min = list1.stream()
                          .flatMap(n -> list2.stream()
                          .map(r -> n-r > 0? n-r: r-n))
                          .mapToInt(t -> t).min();
        System.out.println(min.getAsInt());
    }
Run Code Online (Sandbox Code Playgroud)

编辑(Holger建议)

 OptionalLong min = list1.stream()
.flatMapToLong(n -> list2.stream()
.mapToLong(r -> Math.abs(r-(long)n))).min();
Run Code Online (Sandbox Code Playgroud)

  • 确切地说,但你不应该推出自己的`abs`功能.顺便说一句,两个`int`值之间的距离可能大于`int`值范围,所以我建议`OptionalLong min = list1.stream().flattToLong(n - > list2.stream().mapToLong (r - > Math.abs(r-(long)n))).min();` (4认同)
  • 当你在前面的步骤中使用`flatMapToInt`和`mapToInt`时,`.mapToInt(t - > t)`变得过时了.此外,我使用缩进来表明`map [ToInt]`属于传递给`flatMap [ToInt]`的函数... (2认同)

Hol*_*ger 6

虽然很容易将"将list1的每个元素与list2的每个元素进行比较"逻辑转换为使用Stream API的代码,并且解决方案的源代码可能很短,但它不是一个有效的解决方案.如果列表相当大,您将最终执行n×m次操作.

此外,请注意两个int值之间的距离最大可达2³,这不符合(带符号)int值范围.long如果您正在寻找一般解决方案,那么您应该用它来表达结果.

所以解决方案可能如下所示:

public static long smallestDistance(List<Integer> list1, List<Integer> list2) {
    int[] list2Sorted = list2.stream().mapToInt(Integer::intValue).sorted().toArray();
    if(list2Sorted.length == 0) throw new IllegalArgumentException("list2 is empty");
    long smallest = Long.MAX_VALUE;
    for(int i: list1) {
        int pos = Arrays.binarySearch(list2Sorted, i);
        if(pos >= 0) return 0;
        pos ^= -1;
        if(pos < list2Sorted.length) {
            long d = Math.abs(list2Sorted[pos] - (long)i);
            if(d < smallest) smallest = d;
        }
        if(pos > 0) {
            long d = Math.abs(list2Sorted[pos-1] - (long)i);
            if(d < smallest) smallest = d;
        }
    }
    if(smallest == Long.MAX_VALUE) throw new IllegalArgumentException("list1 is empty");
    return smallest;
}
Run Code Online (Sandbox Code Playgroud)

通过对一个列表进行排序,我们可以有效地查找其他列表中每个元素的最接近元素.这样,时间复杂度从O(n×m)(所有情况)减少到O((n+m)×log(m))(最坏情况).作为奖励,如果发现匹配,它将立即返回,因为这意味着可能的最小距离为零.

如果要测试它,请考虑示例输入,如偶数列表和奇数列表:

List<Integer> list1
    = IntStream.range(0, 100000).mapToObj(i -> i*2).collect(Collectors.toList());
List<Integer> list2
    = IntStream.range(0, 100000).mapToObj(i -> i*2+1).collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)

由于没有完全匹配,因此不可能进行短切,但不同的时间复杂度将变得明显.