如何使用Java 8 Streams API比较HashMap中元素的所有无重复组合并返回符合条件的元素列表
Hey there! Let's tackle this problem of creating unique, non-duplicate element pairs from a HashMap using Java 8 Streams. I totally get why your initial double-forEach approach led to duplicates—you were comparing every pair twice (like (A,B) and (B,A)), which isn't what you want. And while reduce is great for aggregating values into a single result, it's not the right tool here. Instead, we can use IntStream to replicate the index-based pairing logic from your pseudo-code, without the repetition.
Step 1: Convert the Entry Set to a List
First, we need to convert the HashMap's entry set into a List because HashMap entries don't support indexed access. This lets us grab elements by position just like your pseudo-code does:
List<Map.Entry<K, V>> entryList = new ArrayList<>(myHashMap.entrySet());
Step 2: Generate Index Pairs Without Duplicates
We'll use IntStream.range to create our outer loop indices (from 0 to the list size minus 2). For each index i, we generate an inner stream of indices j starting at i+1—this ensures we never compare an element with itself, and never repeat a pair in reverse order:
IntStream.range(0, entryList.size()) .flatMap(i -> IntStream.range(i + 1, entryList.size()) // ... processing logic here )
Step 3: Filter Valid Pairs and Collect Results
For each (i,j) index pair, we fetch the corresponding entries, run your myComparison check, and collect the valid pairs into a list. Here's the full code example:
import java.util.*; import java.util.function.BiPredicate; import java.util.stream.Collectors; import java.util.stream.IntStream; public class UniquePairFinder { public static <K, V> List<Map.Entry<K, V>[]> findValidPairs( HashMap<K, V> myHashMap, BiPredicate<Map.Entry<K, V>, Map.Entry<K, V>> myComparison ) { List<Map.Entry<K, V>> entryList = new ArrayList<>(myHashMap.entrySet()); return IntStream.range(0, entryList.size()) .flatMap(i -> IntStream.range(i + 1, entryList.size()) .mapToObj(j -> new Map.Entry[]{entryList.get(i), entryList.get(j)}) .filter(pair -> myComparison.test(pair[0], pair[1])) ) .collect(Collectors.toList()); } // Example usage public static void main(String[] args) { HashMap<String, Integer> map = new HashMap<>(); map.put("A", 1); map.put("B", 2); map.put("C", 3); // Example comparison: check if first entry's value is less than second's BiPredicate<Map.Entry<String, Integer>, Map.Entry<String, Integer>> comparison = (e1, e2) -> e1.getValue() < e2.getValue(); List<Map.Entry<String, Integer>[]> validPairs = findValidPairs(map, comparison); // Print results validPairs.forEach(pair -> System.out.println(pair[0] + " and " + pair[1]) ); } }
Key Notes:
- No Duplicate Comparisons: By starting
jati+1, we ensure each pair is only checked once—no (B,A) when we already checked (A,B). - Why Not Reduce?:
reduceis meant for aggregating stream elements into a single result (like summing numbers). Since we need to generate multiple pairs,flatMapwith index streams is a much better fit. - Customization: If you're using a custom
myClassinstead ofMap.Entry, just replaceMap.Entry<K,V>with your class type, and ensureentryListis a list of your custom objects.
内容的提问来源于stack exchange,提问作者Guilty

