Ignore duplicates when producing map using streams
Solution 1:
This is possible using the mergeFunction
parameter of Collectors.toMap(keyMapper, valueMapper, mergeFunction)
:
Map<String, String> phoneBook =
people.stream()
.collect(Collectors.toMap(
Person::getName,
Person::getAddress,
(address1, address2) -> {
System.out.println("duplicate key found!");
return address1;
}
));
mergeFunction
is a function that operates on two values associated with the same key. adress1
corresponds to the first address that was encountered when collecting elements and adress2
corresponds to the second address encountered: this lambda just tells to keep the first address and ignores the second.
Solution 2:
As said in JavaDocs:
If the mapped keys contains duplicates (according to
Object.equals(Object)
), anIllegalStateException
is thrown when the collection operation is performed. If the mapped keys may have duplicates, usetoMap(Function keyMapper, Function valueMapper, BinaryOperator mergeFunction)
instead.
So you should use toMap(Function keyMapper, Function valueMapper, BinaryOperator mergeFunction)
instead. Just provide a merge function, that will determine which one of duplicates is put in the map.
For example, if you don't care which one, just call
Map<String, String> phoneBook =
people.stream()
.collect(Collectors.toMap(Person::getName,
Person::getAddress,
(a1, a2) -> a1));
Solution 3:
The answer from alaster helped me a lot, but I would like to add meaningful information if someone is trying to group the data.
If you have, for example, two Orders
with the same code
but different quantity
of products for each one, and your desire is to sum the quantities, you can do the following:
List<Order> listQuantidade = new ArrayList<>();
listOrders.add(new Order("COD_1", 1L));
listOrders.add(new Order("COD_1", 5L));
listOrders.add(new Order("COD_1", 3L));
listOrders.add(new Order("COD_2", 3L));
listOrders.add(new Order("COD_3", 4L));
listOrders.collect(Collectors.toMap(Order::getCode,
o -> o.getQuantity(),
(o1, o2) -> o1 + o2));
Result:
{COD_3=4, COD_2=3, COD_1=9}
Or, from the javadocs, you can combine addresses:
Map<String, String> phoneBook
people.stream().collect(toMap(Person::getName,
Person::getAddress,
(s, a) -> s + ", " + a));