나는지도를 가지고 있으며 Map<K, V>
목표는 중복 된 값을 제거하고 동일한 구조를 Map<K, V>
다시 출력하는 것 입니다. 중복 값이 발견되는 경우, 하나의 키 (이 선택해야합니다 k
두 개의 키 (에서) k1
하고 k1
이 값을 유지),이 이유로 가정 BinaryOperator<K>
주는 k
에서 k1
하고 k2
사용할 수 있습니다.
입력 및 출력 예 :
// 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
사용하여 내 시도 Stream::collect(Supplier, BiConsumer, BiConsumer)
입니다 조금 아주 서투른와 같은 가변 작업을 포함 Map::put
하고 Map::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
);
Collectors
한 번의 Stream::collect
통화 내에서 적절한 조합을 사용하는 솔루션이 있습니까 (예 : 변경 가능한 작업 없음)?
답변
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));
}
답변
이것을보십시오 : 간단한 방법은 키와 값의 역수 toMap()
입니다. 병합 기능으로 수집기 를 사용하십시오 .
map.entrySet().stream()
.map(entry -> new AbstractMap.SimpleEntry<>(entry.getValue(), entry.getKey()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, reducingKeysBinaryOperator));
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));
답변
비 스트림 솔루션이 더 표현력이 좋습니다.
BinaryOperator<K> reducingKeysBinaryOperator = (k1, k2) -> k1 > k2 ? k1 : k2;
Map<V, K> reverse = new LinkedHashMap<>(map.size());
map.forEach((k, v) -> reverse.merge(v, k, reducingKeysBinaryOperator));
Map<K, V> result = new LinkedHashMap<>(reverse.size());
reverse.forEach((v, k) -> result.put(k, v));
이 Map.merge
기능은 축소 기능과 함께 사용 LinkedHashMap
되며 원래 입력 순서를 유지하는 데 사용 됩니다.
답변
Collectors
반환 된 Map을 다시 수집하고 추가 처리 할 필요없이 사용하는 방법을 찾았 습니다. 아이디어는 다음과 같습니다.
-
그룹
Map<K, V>
에Map<V, List<K>
.Map<K, V> distinctValuesMap = this.stream.collect( Collectors.collectingAndThen( Collectors.groupingBy(Entry::getValue), groupingDownstream ) );
{사과 = [1, 5, 3], 주황색 = [4, 2]}
-
새 키 (
List<K>
)를K
사용으로 줄BinaryOperator<K>
입니다.Function<Entry<V, List<Entry<K, V>>>, K> keyMapFunction = e -> e.getValue().stream() .map(Entry::getKey) .collect(Collectors.collectingAndThen( Collectors.reducing(reducingKeysBinaryOperator), Optional::get ) );
{사과 = 5, 주황색 = 4}
-
역
Map<V, K>
에 다시Map<K, V>
다시 구조 – 키와 값을 모두 별개로 보장되기 때문에 안전하다.Function<Map<V, List<Entry<K,V>>>, Map<K, V>> groupingDownstream = m -> m.entrySet() .stream() .collect(Collectors.toMap( keyMapFunction, Entry::getKey ) );
{5 = 사과, 4 = 오렌지}
최종 코드 :
final BinaryOperator<K> reducingKeysBinaryOperator = ...
final Map<K, V> distinctValuesMap = map.entrySet().stream().collect(
Collectors.collectingAndThen(
Collectors.groupingBy(Entry::getValue),
m -> m.entrySet().stream().collect(
Collectors.toMap(
e -> e.getValue().stream().map(Entry::getKey).collect(
Collectors.collectingAndThen(
Collectors.reducing(reducingKeysBinaryOperator),
Optional::get
)
),
Entry::getKey
)
)
)
);
답변
“Stream and Collectors.groupingBy”로 원하는 결과를 얻는 또 다른 접근법.
map = map.entrySet().stream()
.collect(Collectors.groupingBy(
Entry::getValue,
Collectors.maxBy(Comparator.comparing(Entry::getKey))
)
)
.entrySet().stream()
.collect(Collectors.toMap(
k -> {
return k.getValue().get().getKey();
},
Entry::getKey));