如何优化Java 8 Stream API实现列表中和为指定值的数对查找
问题:Java 8 Stream查找和为指定值的不重复数对
我尝试使用Java 8 Stream API查找列表中和为指定值(此处为16)的数对,但当前输出出现了重复的反向数对(如12 4与4 12、16 0与0 16),期望得到无重复的目标数对结果,请问该如何优化现有逻辑?
原实现代码
List<Integer> listOfNumbers = Arrays.asList(new Integer []{15, 12, 4, 16, 9, 8, 24, 0}); Set<Integer[]> sumPair = listOfNumbers.stream() .flatMap(i -> listOfNumbers.stream() .filter(p -> (i + p) == 16 && listOfNumbers.indexOf(p) != listOfNumbers.indexOf(i)) .map(p -> new Integer[] { i, p })) .collect(Collectors.toSet()); for (Integer[] integers : sumPair) { for (Integer val : integers) { System.out.print(val + " "); } System.out.println(""); }
实际输出
16 0 4 12 0 16 12 4
期望输出
16 0 12 4
优化方案
方法一:通过索引遍历避免反向数对
原逻辑遍历所有元素两两组合,导致正向和反向对都被收集。可以改为遍历索引,只处理当前索引之后的元素,这样每个数对只会被匹配一次:
List<Integer> listOfNumbers = Arrays.asList(15, 12, 4, 16, 9, 8, 24, 0); int targetSum = 16; List<int[]> sumPair = IntStream.range(0, listOfNumbers.size()) .boxed() .flatMap(i -> IntStream.range(i + 1, listOfNumbers.size()) .filter(j -> listOfNumbers.get(i) + listOfNumbers.get(j) == targetSum) .mapToObj(j -> new int[]{listOfNumbers.get(i), listOfNumbers.get(j)})) .collect(Collectors.toList()); sumPair.forEach(pair -> System.out.println(pair[0] + " " + pair[1]));
方法二:使用自定义不可变数对类实现去重
Integer[]作为集合元素时,数组的equals和hashCode是基于对象引用的,无法识别内容相同的反向数组。可以定义一个不可变的数对类,标准化存储顺序并重写判断方法,让(a,b)和(b,a)被视为相等:
import java.util.Objects; class Pair { private final int first; private final int second; public Pair(int first, int second) { // 标准化存储:让较小的数在前,较大的在后 this.first = Math.min(first, second); this.second = Math.max(first, second); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Pair pair = (Pair) o; return first == pair.first && second == pair.second; } @Override public int hashCode() { return Objects.hash(first, second); } @Override public String toString() { return first + " " + second; } }
修改后的Stream逻辑:
List<Integer> listOfNumbers = Arrays.asList(15, 12, 4, 16, 9, 8, 24, 0); int targetSum = 16; Set<Pair> sumPair = listOfNumbers.stream() .flatMap(i -> listOfNumbers.stream() .filter(p -> i + p == targetSum && !i.equals(p)) .map(p -> new Pair(i, p))) .collect(Collectors.toSet()); sumPair.forEach(System.out::println);
方法三:利用Set记录已处理元素
遍历过程中,用Set存储已经检查过的元素,找到符合条件的数对后,把补数加入Set,避免后续反向配对:
List<Integer> listOfNumbers = Arrays.asList(15, 12, 4, 16, 9, 8, 24, 0); int targetSum = 16; Set<Integer> processed = new HashSet<>(); List<int[]> sumPair = listOfNumbers.stream() .filter(num -> !processed.contains(num)) .flatMap(num -> { int complement = targetSum - num; if (listOfNumbers.contains(complement) && !num.equals(complement)) { processed.add(complement); return Stream.of(new int[]{num, complement}); } return Stream.empty(); }) .collect(Collectors.toList()); sumPair.forEach(pair -> System.out.println(pair[0] + " " + pair[1]));
注:如果列表中存在重复元素(如两个8),需根据需求调整逻辑,比如是否允许8,8这类自身配对的数对。
内容的提问来源于stack exchange,提问作者ranjan kumar singh
相关产品推荐
相关产品推荐

