Обединяване на две карти с Java 8

1. Въведение

В този бърз урок ще демонстрираме как да обединим две карти, използвайки възможностите на Java 8 .

За да бъдем по-конкретни, ще разгледаме различни сценарии за сливане, включително карти с дублиращи се записи.

2. Инициализация

Като начало, нека дефинираме два екземпляра на Map :

private static Map map1 = new HashMap(); private static Map map2 = new HashMap();

Класът Employee изглежда така:

public class Employee {       private Long id;     private String name;       // constructor, getters, setters }

След това можем да вкараме някои данни в екземплярите на Map :

Employee employee1 = new Employee(1L, "Henry"); map1.put(employee1.getName(), employee1); Employee employee2 = new Employee(22L, "Annie"); map1.put(employee2.getName(), employee2); Employee employee3 = new Employee(8L, "John"); map1.put(employee3.getName(), employee3); Employee employee4 = new Employee(2L, "George"); map2.put(employee4.getName(), employee4); Employee employee5 = new Employee(3L, "Henry"); map2.put(employee5.getName(), employee5);

Имайте предвид, че в нашите карти имаме еднакви ключове за записи на служител1 и служител5, които ще използваме по-късно.

3. Map.merge ()

Java 8 добавя нова функция merge () в интерфейса java.util.Map .

Ето как работи функцията merge () : Ако посоченият ключ вече не е свързан със стойност или стойността е нула, тя свързва ключа с дадената стойност.

В противен случай той замества стойността с резултатите от дадената функция за пренасочване. Ако резултатът от функцията за преназначаване е нула, той премахва резултата.

Първо, нека конструираме нова HashMap, като копираме всички записи от map1 :

Map map3 = new HashMap(map1);

След това нека въведем функцията merge () заедно с правилото за сливане:

map3.merge(key, value, (v1, v2) -> new Employee(v1.getId(),v2.getName())

Накрая ще прегледаме картата2 и ще обединим записите в map3 :

map2.forEach( (key, value) -> map3.merge(key, value, (v1, v2) -> new Employee(v1.getId(),v2.getName())));

Нека стартираме програмата и отпечатаме съдържанието на map3 :

John=Employee{id=8, name="John"} Annie=Employee{id=22, name="Annie"} George=Employee{id=2, name="George"} Henry=Employee{id=1, name="Henry"}

В резултат на това нашата комбинирана карта има всички елементи на предишните записи на HashMap . Записите с дублирани ключове са обединени в един запис .

Също така забелязваме, че обектът Employee от последния запис има идентификатора от map1 , а стойността се избира от map2 .

Това е заради правилото, което дефинирахме в нашата функция за сливане:

(v1, v2) -> new Employee(v1.getId(), v2.getName())

4. Stream.concat ()

The Stream API в Java 8 може да осигури и лесно решение на нашия проблем. Първо, трябва да комбинираме нашите екземпляри на Карта в един поток . Точно това прави операцията Stream.concat () :

Stream combined = Stream.concat(map1.entrySet().stream(), map2.entrySet().stream());

Тук предаваме наборите за въвеждане на карта като параметри. След това трябва да съберем резултата си в нова карта . За това можем да използваме Collectors.toMap () :

Map result = combined.collect( Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

В резултат колекторът ще използва съществуващите ключове и стойности на нашите карти. Но това решение далеч не е перфектно. Веднага щом нашият колекционер срещне записи с дублиращи се ключове, той ще хвърли IllegalStateException .

За да се справим с този проблем, ние просто добавяме трети ламбда параметър „сливане“ в нашия колектор:

(value1, value2) -> new Employee(value2.getId(), value1.getName())

Той ще използва ламбда израза всеки път, когато бъде открит дублиран ключ.

И накрая, всички заедно:

Map result = Stream.concat(map1.entrySet().stream(), map2.entrySet().stream()) .collect(Collectors.toMap( Map.Entry::getKey, Map.Entry::getValue, (value1, value2) -> new Employee(value2.getId(), value1.getName())));

И накрая, нека пуснем кода и да видим резултатите:

George=Employee{id=2, name="George"} John=Employee{id=8, name="John"} Annie=Employee{id=22, name="Annie"} Henry=Employee{id=3, name="Henry"}

Както виждаме, дублираните записи с ключа „Хенри“ бяха обединени в нова двойка ключ-стойност, където идентификаторът на новия служител беше избран от map2 и стойността от map1 .

5. Stream.of ()

За да продължим да използваме API на Stream , можем да превърнем нашите екземпляри на Map в единен поток с помощта на Stream.of () .

Тук не е нужно да създаваме допълнителна колекция за работа с потоците:

Map map3 = Stream.of(map1, map2) .flatMap(map -> map.entrySet().stream()) .collect(Collectors.toMap( Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> new Employee(v1.getId(), v2.getName())));

На първо място, ние се превърне map1 и МАР2 в един поток . След това преобразуваме потока в картата. Както виждаме, последният аргумент на toMap () е функция за сливане. Той решава проблема с дублиращите се ключове, като избира полето id от v1 запис и името от v2 .

Отпечатаният екземпляр map3 след стартиране на програмата:

George=Employee{id=2, name="George"} John=Employee{id=8, name="John"} Annie=Employee{id=22, name="Annie"} Henry=Employee{id=1, name="Henry"}

6. Просто поточно предаване

Освен това можем да използваме тръбопровод stream () , за да съберем нашите записи в картата. Кодът по-долу показва как да добавите записите от МАР2 и map1 от игнориране на дублиращи се записи:

Map map3 = map2.entrySet() .stream() .collect(Collectors.toMap( Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> new Employee(v1.getId(), v2.getName()), () -> new HashMap(map1)));

Както очакваме, резултатите след сливането са:

{John=Employee{id=8, name="John"}, Annie=Employee{id=22, name="Annie"}, George=Employee{id=2, name="George"}, Henry=Employee{id=1, name="Henry"}}

7. StreamEx

In addition to solutions that are provided by the JDK, we can also use the popular StreamEx library.

Simply put, StreamEx is an enhancement for the Stream API and provides many additional useful methods. We'll use an EntryStream instance to operate on key-value pairs:

Map map3 = EntryStream.of(map1) .append(EntryStream.of(map2)) .toMap((e1, e2) -> e1);

The idea is to merge the streams of our maps into one. Then we collect the entries into the new map3 instance. Important to mention, the (e1, e2) -> e1 expression as it helps to define the rule for dealing with the duplicate keys. Without it, our code will throw an IllegalStateException.

And now, the results:

{George=Employee{id=2, name="George"}, John=Employee{id=8, name="John"}, Annie=Employee{id=22, name="Annie"}, Henry=Employee{id=1, name="Henry"}}

8. Summary

В тази кратка статия научихме различни начини за обединяване на карти в Java 8. По-конкретно, използвахме Map.merge (), Stream API, библиотека StreamEx .

Както винаги, кодът, използван по време на дискусията, може да бъде намерен в GitHub.