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

时间:2018-04-04 12:53:08

标签: java-8 java-stream

我对Java 8的Streams完全不熟悉,目前正在尝试解决此问题,我有以下两个列表:

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

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

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

使用Java 7实现它不是问题,但我如何使用Java8的Streams实现它?我如何迭代来自forEach的{​​{1}}元素和来自List1的{​​{1}}并保留最小值?

2 个答案:

答案 0 :(得分: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());
    }

编辑(Holger建议)

 OptionalLong min = list1.stream()
.flatMapToLong(n -> list2.stream()
.mapToLong(r -> Math.abs(r-(long)n))).min();

答案 1 :(得分: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;
}

通过对一个列表进行排序,我们可以有效地查找另一个列表中每个元素的最接近元素。这样,时间复杂度从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());

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