如何从地图中生成具有不同值的地图(并使用 BinaryOperator 使用正确的键)?

Nik*_*las 12 java dictionary java-8 java-stream collectors

我有一张地图Map<K, V>,我的目标是删除重复的值并Map<K, V>再次输出相同的结构。如果重复的值被发现,必须有一个选择键(k从两个键()k1k1)持有这些值,因为这个原因,假设BinaryOperator<K>kk1k2可用。

示例输入和输出:

// Input
Map<Integer, String> map = new HashMap<>();
map.put(1, "apple");
map.put(5, "apple");
map.put(4, "orange");
map.put(3, "apple");
map.put(2, "orange");

// Output: {5=apple, 4=orange} // the key is the largest possible
Run Code Online (Sandbox Code Playgroud)

用我的尝试Stream::collect(Supplier, BiConsumer, BiConsumer)非常笨拙,包含可变操作,比如Map::putMap::remove我想避免:

// // the key is the largest integer possible (following the example above)
final BinaryOperator<K> reducingKeysBinaryOperator = (k1, k2) -> k1 > k2 ? k1 : k2;

Map<K, V> distinctValuesMap = map.entrySet().stream().collect(
    HashMap::new,                                                              // A new map to return (supplier)
    (map, entry) -> {                                                          // Accumulator
        final K key = entry.getKey();
        final V value = entry.getValue();
        final Entry<K, V> editedEntry = Optional.of(map)                       // New edited Value
            .filter(HashMap::isEmpty)
            .map(m -> new SimpleEntry<>(key, value))                           // If a first entry, use it
            .orElseGet(() -> map.entrySet()                                    // otherwise check for a duplicate
                    .stream() 
                    .filter(e -> value.equals(e.getValue()))
                    .findFirst()
                    .map(e -> new SimpleEntry<>(                               // .. if found, replace
                            reducingKeysBinaryOperator.apply(e.getKey(), key), 
                            map.remove(e.getKey())))
                    .orElse(new SimpleEntry<>(key, value)));                   // .. or else leave
        map.put(editedEntry.getKey(), editedEntry.getValue());                 // put it to the map
    },
    (m1, m2) -> {}                                                             // Combiner
);
Run Code Online (Sandbox Code Playgroud)

是否有Collectors在一次Stream::collect调用中使用适当组合的解决方案(例如,没有可变操作)?

Mik*_*Hay 11

您可以使用Collectors.toMap

private Map<Integer, String> deduplicateValues(Map<Integer, String> map) {
    Map<String, Integer> inverse = map.entrySet().stream().collect(toMap(
            Map.Entry::getValue,
            Map.Entry::getKey,
            Math::max) // take the highest key on duplicate values
    );

    return inverse.entrySet().stream().collect(toMap(Map.Entry::getValue, Map.Entry::getKey));
}
Run Code Online (Sandbox Code Playgroud)


Had*_*i J 8

试试这个:简单的方法是反转键和值,然后使用toMap()带有合并功能的收集器。

map.entrySet().stream()
        .map(entry -> new AbstractMap.SimpleEntry<>(entry.getValue(), entry.getKey()))
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, reducingKeysBinaryOperator));
Run Code Online (Sandbox Code Playgroud)
Map<K, V> output = map.entrySet().stream()
        .collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey, reducingKeysBinaryOperator))
        .entrySet().stream()
        .collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
Run Code Online (Sandbox Code Playgroud)

  • @GPI 和 Michael,这是因为他必须合并密钥,因此反转对将合并密钥。那么缺少的是第二个反转。 (3认同)
  • 我看不出中间的“map”操作能买什么。您似乎交换了键和值,这一点很清楚,但是有什么意义,您可以在收集步骤中这样做吗? (2认同)
  • @HadiJ 不!反转是正确的!但还需要第二次才能回来。合并用于合并键,但合并只能用于值...... (2认同)
  • 擅自使用您共享的代码中问题中引入的局部变量。如果在做出答案时与意图相冲突,请返回。 (2认同)