【问题标题】:java 8 streams how to find min difference between elements of 2 listsjava 8流如何找到2个列表的元素之间的最小差异
【发布时间】:2018-09-14 01:10:00
【问题描述】:

我对 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 实现它?我如何从 List1 迭代 forEach 元素以及从 List2 迭代 forEach 并保持最小值?

【问题讨论】:

  • 请注意,您是否使用 IDE 进行编码?他们中的大多数人应该在您可以使用哪些方法来解决此类问题方面提供很好的帮助。
  • 这可能有助于stackoverflow.com/questions/32131987/… 基本上只是在 2 个列表上的一个 foreach 来创建一个新的差异列表

标签: java-8 java-stream


【解决方案1】:

虽然使用 Stream API 很容易将“比较 list1 的每个元素与 list2 的每个元素”逻辑转换为代码,并且解决方案的源代码可能很短,但这并不是一个有效的解决方案。如果列表相当大,您最终将执行 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());

由于没有精确匹配,所以不能走捷径,但不同的时间复杂度会变得很明显。

【讨论】:

  • 优秀的答案。 pos ^= -1可以换成-pos - 1吗?
  • @user7 是的,pos ^= -1pos = -pos - 1 相同,也与pos = ~pos 相同...
【解决方案2】:

试试这个

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());
    }

编辑(霍尔格建议)

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

【讨论】:

  • 当您在前面的步骤中使用 flatMapToIntmapToInt 时,.mapToInt(t -&gt; t) 已过时。此外,我会使用缩进来明确map[ToInt] 属于传递给flatMap[ToInt] 的函数……
  • @Holger 你的意思是这样的list1.stream().flatMapToInt(n -&gt; list2.stream().mapToInt(r -&gt; n-r &gt; 0? n-r: r-n)).min()
  • 没错,但您不应该推出自己的 abs 函数。顺便说一句,两个int值之间的距离可能大于int值范围,所以我推荐OptionalLong min = list1.stream() .flatMapToLong(n -&gt; list2.stream().mapToLong(r -&gt; Math.abs(r-(long)n))) .min();
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-23
  • 1970-01-01
  • 2015-11-12
  • 2011-01-24
  • 2016-06-19
相关资源
最近更新 更多