我对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}}并保留最小值?
答案 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());
由于没有完全匹配,因此不可能进行短切,但不同的时间复杂度会变得明显。