Ignore duplicates when producing map using streams
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.
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));
The @alaster answer help me a lot, but I would like to add a meaninful information if someone is trying to group the information.
If you have, per example, two Orders
with the same code
but different quantity
of products for each one, and your desire is sum the quantities, you can do:
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 combine addresses:
Map<String, String> phoneBook people.stream().collect(toMap(Person::getName, Person::getAddress, (s, a) -> s + ", " + a));