如何在Collectors.toMap合并函数中获取密钥?

mem*_*und 12 java java-8 collectors

当在期间找到重复的键条目时Collectors.toMap(),(o1, o2)调用合并功能.

问题:如何获取导致重复的密钥?

String keyvalp = "test=one\ntest2=two\ntest2=three";

Pattern.compile("\n")
    .splitAsStream(keyval)
    .map(entry -> entry.split("="))
    .collect(Collectors.toMap(
        split -> split[0],
        split -> split[1],
        (o1, o2) -> {
            //TODO how to access the key that caused the duplicate? o1 and o2 are the values only
            //split[0]; //which is the key, cannot be accessed here
        },
    HashMap::new));
Run Code Online (Sandbox Code Playgroud)

在合并函数内部,我想根据来决定,如果我取消映射,或继续并接受这些值.

Pet*_*rey 6

您需要使用自定义收集器或使用不同的方法.

Map<String, String> map = new Hashmap<>();
Pattern.compile("\n")
    .splitAsStream(keyval)
    .map(entry -> entry.split("="))
    .forEach(arr -> map.merge(arr[0], arr[1], (o1, o2) -> /* use arr[0]));
Run Code Online (Sandbox Code Playgroud)

编写自定义收集器相当复杂.你需要一个TriConsumer(键和两个值)是类似的,它不在JDK中,这就是为什么我很确定没有内置函数使用.;)


Hol*_*ger 6

当您省略合并函数时,合并函数没有机会获取密钥,这与内置函数有同样的问题。

\n\n

解决方案是使用不同的toMap实现,它不依赖于Map.merge

\n\n
public static <T, K, V> Collector<T, ?, Map<K,V>>\n    toMap(Function<? super T, ? extends K> keyMapper,\n          Function<? super T, ? extends V> valueMapper) {\n    return Collector.of(HashMap::new,\n        (m, t) -> {\n            K k = keyMapper.apply(t);\n            V v = Objects.requireNonNull(valueMapper.apply(t));\n            if(m.putIfAbsent(k, v) != null) throw duplicateKey(k, m.get(k), v);\n        },\n        (m1, m2) -> {\n            m2.forEach((k,v) -> {\n                if(m1.putIfAbsent(k, v)!=null) throw duplicateKey(k, m1.get(k), v);\n            });\n            return m1;\n        });\n}\nprivate static IllegalStateException duplicateKey(Object k, Object v1, Object v2) {\n    return new IllegalStateException("Duplicate key "+k+" (values "+v1+" and "+v2+\')\');\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

(这基本上就是toMap没有合并功能的 Java\xc2\xa09\xe2\x80\x99s 实现的作用)

\n\n

因此,您在代码中需要做的就是重定向调用toMap并省略合并函数:

\n\n
String keyvalp = "test=one\\ntest2=two\\ntest2=three";\n\nMap<String, String> map = Pattern.compile("\\n")\n        .splitAsStream(keyvalp)\n        .map(entry -> entry.split("="))\n        .collect(toMap(split -> split[0], split -> split[1]));\n
Run Code Online (Sandbox Code Playgroud)\n\n

(或者ContainingClass.toMap如果它既不在同一类中也不在静态导入中)<\\sup>

\n\n

该收集器像原始toMap收集器一样支持并行处理,尽管它\xe2\x80\x99不太可能从此处的并行处理中受益,即使有更多元素要处理。

\n\n

如果,如果我理解正确的话,您只想在基于实际键的合并函数中选择较旧或较新的值,则可以使用Predicate这样的键来完成

\n\n
public static <T, K, V> Collector<T, ?, Map<K,V>>\n    toMap(Function<? super T, ? extends K> keyMapper,\n          Function<? super T, ? extends V> valueMapper,\n          Predicate<? super K> useOlder) {\n    return Collector.of(HashMap::new,\n        (m, t) -> {\n            K k = keyMapper.apply(t);\n            m.merge(k, valueMapper.apply(t), (a,b) -> useOlder.test(k)? a: b);\n        },\n        (m1, m2) -> {\n            m2.forEach((k,v) -> m1.merge(k, v, (a,b) -> useOlder.test(k)? a: b));\n            return m1;\n        });\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n\n\n
Map<String, String> map = Pattern.compile("\\n")\n        .splitAsStream(keyvalp)\n        .map(entry -> entry.split("="))\n        .collect(toMap(split -> split[0], split -> split[1], key -> condition));\n
Run Code Online (Sandbox Code Playgroud)\n\n

有几种方法可以自定义这个收集器\xe2\x80\xa6

\n