Как получить ключ в коллекторах.функция слияния toMap?
когда дубликат ключевой записи найден во время Collectors.toMap()
функция слияния (o1, o2)
называется.
вопрос: как я могу получить ключ, что вызвало дублирование?
String keyvalp = "test=onentest2=twontest2=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));
внутри функции слияния я хочу принять решение на основе ключ который, если я отменю сопоставление или продолжу и возьму на себя эти значения.
3 ответов
вам нужно использовать пользовательский коллектор или использовать другой подход.
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]));
написание пользовательского коллектора довольно сложнее. Вам нужен TriConsumer (ключ и два значения) похож, которого нет в JDK, поэтому я уверен, что нет встроенной функции, которая использует. ;)
функция слияния не имеет шансов получить ключ, который является той же проблемой, встроенная функция имеет, когда вы опускаете функцию слияния.
решение состоит в том, чтобы использовать другой toMap
реализация, которая не зависит от Map.merge
:
public static <T, K, V> Collector<T, ?, Map<K,V>>
toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends V> valueMapper) {
return Collector.of(HashMap::new,
(m, t) -> {
K k = keyMapper.apply(t);
V v = Objects.requireNonNull(valueMapper.apply(t));
if(m.putIfAbsent(k, v) != null) throw duplicateKey(k, m.get(k), v);
},
(m1, m2) -> {
m2.forEach((k,v) -> {
if(m1.putIfAbsent(k, v)!=null) throw duplicateKey(k, m1.get(k), v);
});
return m1;
});
}
private static IllegalStateException duplicateKey(Object k, Object v1, Object v2) {
return new IllegalStateException("Duplicate key "+k+" (values "+v1+" and "+v2+')');
}
(это в основном то, что реализация Java 9 toMap
без функции слияния обойдется)
поэтому все, что вам нужно сделать в коде, чтобы перенаправить toMap
вызовите и опустите функция слияния:
String keyvalp = "test=one\ntest2=two\ntest2=three";
Map<String, String> map = Pattern.compile("\n")
.splitAsStream(keyvalp)
.map(entry -> entry.split("="))
.collect(toMap(split -> split[0], split -> split[1]));
(или ContainingClass.toMap
если его ни в том же классе, ни статический импорт)
сборник поддерживает параллельную обработку как оригинал toMap
collector, хотя это не очень вероятно, чтобы получить выгоду от параллельной обработки здесь, даже с большим количеством элементов для обработки.
если, если я правильно понял, вы хотите выбрать только старое или новое значение в функции слияния на основе фактического ключа, вы можете это сделать с ключом Predicate
такой
public static <T, K, V> Collector<T, ?, Map<K,V>>
toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends V> valueMapper,
Predicate<? super K> useOlder) {
return Collector.of(HashMap::new,
(m, t) -> {
K k = keyMapper.apply(t);
m.merge(k, valueMapper.apply(t), (a,b) -> useOlder.test(k)? a: b);
},
(m1, m2) -> {
m2.forEach((k,v) -> m1.merge(k, v, (a,b) -> useOlder.test(k)? a: b));
return m1;
});
}
Map<String, String> map = Pattern.compile("\n")
.splitAsStream(keyvalp)
.map(entry -> entry.split("="))
.collect(toMap(split -> split[0], split -> split[1], key -> condition));
существует несколько способов настройки этого коллектора...
есть, конечно, простой и тривиальный трюк-сохранение ключа в функции "key mapper" и получение ключа в функции "merge". Таким образом, код может выглядеть следующим образом (при условии, что ключ целочисленный):
final AtomicInteger key = new AtomicInteger();
...collect( Collectors.toMap(
item -> { key.set(item.getKey()); return item.getKey(); }, // key mapper
item -> ..., // value mapper
(v1, v2) -> { log(key.get(), v1, v2); return v1; } // merge function
);
Примечание: это не хорошо для параллельной обработки.