While it is easy to convert a “compare each element of list1 with each element of list2” logic to code using the Stream API and the solution’s source code might be short, it isn’t an efficient solution. If the lists are rather large, you will end up doing n × m operations.
Further, note that the distance between two int
values can be up to 2³², which doesn’t fit into the (signed) int
value range. So you should use long
to express the result, if you’re looking for a general solution.
So a solution may look like this:
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;
}
By sorting one list, we can efficiently look up the closest element(s) for each element of the other list. This way, the time complexity reduces from O(n×m)
(all cases) to O((n+m)×log(m))
(worst case). As a bonus, it will return immediately if it found a match, as that implies the smallest possible distance of zero.
If you want to test it, consider example input like a list of even and a list of odd numbers:
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());
Since there is no exact match, short-cutting is not possible, but the different time complexity will become noticeable.