Як можна комбінувати два об'єкти HashMap, що містять однакові типи?


241

У мене два HashMapоб'єкти визначені так:

HashMap<String, Integer> map1 = new HashMap<String, Integer>();
HashMap<String, Integer> map2 = new HashMap<String, Integer>();

Також у мене є третій HashMapоб’єкт:

HashMap<String, Integer> map3;

Як я можу злитися map1і map2разом у map3?


16
Ви не заявили, що ви хочете статися, якщо ключ існує в обох картах.
Майкл Шепер

Відповіді:


344
map3 = new HashMap<>();

map3.putAll(map1);
map3.putAll(map2);

1
дякую, я зливаю Карти в циклі for, який використовує метод повернення карти і мені потрібно об'єднати її з іншою картою і застосувати той самий метод agian. Для цього я отримую нульовий виняток вказівника методом putAll. це не допомагає використовувати блок "try / catch". що я повинен зробити? Я застосовую, якщо умова, що якщо розмір == o, тоді не застосовувати putAll інше застосувати його і так далі ....
Mavin

1
Якщо ви отримали NPE, то, очевидно, ви не ініціалізували належним чином один із своїх об'єктів. Ви друкуєте стек-трек у блоці лову? Тож ви знаєте, звідки виникає проблема. Але якщо ви не опублікуєте повний і точний код, включаючи слід стека, вам потрібно буде відстежити це самостійно.
a_horse_with_no_name

95
Зауважте, що за допомогою цього рішення, якщо ключ існує в обох картах, значення в map2 буде збережено, а значення в map1 втрачено.
Майкл Шепер

5
@MichaelScheper: чого ще ти очікуєш? Ключі в а Mapза визначенням унікальні
a_horse_with_no_name

42
Я не знаю, чого очікує ОПЕР. Можливо, він очікує, що значення map1 матимуть перевагу, або буде виключено виняток, або буде виконано деяку операцію "злиття" на пересічних цілих числах. А може, оскільки це питання для початківців, це випадок, який ОПЕР не розглядав, і в цьому випадку мій коментар, сподіваюся, буде корисним.
Майкл Шепер

109

Якщо ви знаєте, що у вас немає дублікатів ключів, або ви хочете, щоб значення map2перезаписували значення map1для дублюючих ключів, ви можете просто написати

map3 = new HashMap<>(map1);
map3.putAll(map2);

Якщо вам потрібен додатковий контроль над поєднанням значень, ви можете використовувати Map.merge, додані в Java 8, що використовує надані користувачем BiFunctionдля об'єднання значень для дублікатів ключів. mergeдіє на окремих ключів і значень, так що вам потрібно використовувати цикл або Map.forEach. Тут ми об'єднуємо рядки для повторюваних ключів:

map3 = new HashMap<>(map1);
for (Map.Entry<String, String> e : map2.entrySet())
    map3.merge(e.getKey(), e.getValue(), String::concat);
//or instead of the above loop
map2.forEach((k, v) -> map3.merge(k, v, String::concat));

Якщо ви знаєте, що у вас немає дублікатів ключів, і хочете їх застосувати, ви можете використовувати функцію злиття, яка видає AssertionError:

map2.forEach((k, v) ->
    map3.merge(k, v, (v1, v2) ->
        {throw new AssertionError("duplicate values for key: "+k);}));

Відступивши від цього конкретного питання, бібліотека потоків Java 8 надає toMapі groupingBy колектори . Якщо ви неодноразово об’єднуєте карти в циклі, можливо, ви зможете реструктурувати обчислення для використання потоків, які можуть як уточнити ваш код, так і забезпечити простий паралелізм, використовуючи паралельний потік і одночасний колектор.


46

Один лайнер за допомогою API Java 8 Stream:

map3 = Stream.of(map1, map2).flatMap(m -> m.entrySet().stream())
       .collect(Collectors.toMap(Entry::getKey, Entry::getValue))

Серед переваг цього методу є можливість передачі функції злиття, яка матиме справу зі значеннями, які мають той самий ключ, наприклад:

map3 = Stream.of(map1, map2).flatMap(m -> m.entrySet().stream())
       .collect(Collectors.toMap(Entry::getKey, Entry::getValue, Math::max))

1
це підкине IllegalStateException для дублікатів ключів
Arpit J.

1
@ArpitJ. у цьому вся суть другої варіації. Іноді ти хочеш винятку, іноді цього не робиш.
Алекс R

36

Альтернативний Java-8-макет для об'єднання двох карт:

defaultMap.forEach((k, v) -> destMap.putIfAbsent(k, v));

Те саме з посиланням на метод:

defaultMap.forEach(destMap::putIfAbsent);

Або ідентифікатор для оригінального рішення карт з третьою картою:

Map<String, Integer> map3 = new HashMap<String, Integer>(map2);
map1.forEach(map3::putIfAbsent);

І ось спосіб об’єднати дві карти у швидку незмінну одну з Гуавою що робить найменш можливі операції з проміжним копіюванням:

ImmutableMap.Builder<String, Integer> builder = ImmutableMap.<String, Integer>builder();
builder.putAll(map1);
map2.forEach((k, v) -> {if (!map1.containsKey(k)) builder.put(k, v);});
ImmutableMap<String, Integer> map3 = builder.build();

Дивіться також Об'єднання двох карт з Java 8 для випадків, коли значення, присутні в обох картах, потрібно поєднувати з функцією відображення.


32

Якщо вам не потрібна мінливість для підсумкової карти, є гуави - й ImmutableMap з його Builderі putAllметодом , який, на відміну від Яви Mapметоди інтерфейсу , може бути прикутий.

Приклад використання:

Map<String, Integer> mergeMyTwoMaps(Map<String, Integer> map1, Map<String, Integer> map2) {
  return ImmutableMap.<String, Integer>builder()
      .putAll(map1)
      .putAll(map2)
      .build();
}

Звичайно, цей метод може бути більш загальним, використовувати varargs та цикл для putAll Mapsаргументів тощо, але я хотів показати концепцію.

Крім того, ImmutableMapі його Builderмало обмежень (або, можливо, функцій?):

  • вони є нульовими ворожими (кинути NullPointerException- якщо якийсь ключ або значення на карті є нульовим)
  • Builder не приймає повторювані ключі (кидає, IllegalArgumentExceptionякщо були додані повторювані ключі).


21

Ви можете використовувати Collection.addAll () для інших типів, наприклад List, Setі т.д. Для Map, ви можете використовувати putAll.


11

Загальне рішення для комбінування двох карт, на яких можливо спільні ключі:

На місці:

public static <K, V> void mergeInPlace(Map<K, V> map1, Map<K, V> map2,
        BinaryOperator<V> combiner) {
    map2.forEach((k, v) -> map1.merge(k, v, combiner::apply));
}

Повернення нової карти:

public static <K, V> Map<K, V> merge(Map<K, V> map1, Map<K, V> map2,
        BinaryOperator<V> combiner) {
    Map<K, V> map3 = new HashMap<>(map1);
    map2.forEach((k, v) -> map3.merge(k, v, combiner::apply));
    return map3;
}

2

Невеликий фрагмент, який я дуже часто використовую для створення карти з інших карт:

static public <K, V> Map<K, V> merge(Map<K, V>... args) {
    final Map<K, V> buffer = new HashMap<>();

    for (Map m : args) {
        buffer.putAll(m);
    }

    return buffer;
}

2

ви можете використовувати HashMap<String, List<Integer>>для об'єднання обох хешмапів та уникнення втрати елементів у парі з одним ключем.

HashMap<String, Integer> map1 = new HashMap<>();
HashMap<String, Integer> map2 = new HashMap<>();
map1.put("key1", 1);
map1.put("key2", 2);
map1.put("key3", 3);
map2.put("key1", 4);
map2.put("key2", 5);
map2.put("key3", 6);
HashMap<String, List<Integer>> map3 = new HashMap<>();
map1.forEach((str, num) -> map3.put(str, new ArrayList<>(Arrays.asList(num))));
//checking for each key if its already in the map, and if so, you just add the integer to the list paired with this key
for (Map.Entry<String, Integer> entry : map2.entrySet()) {
    Integer value = entry.getValue();
    String key = entry.getKey();
    if (map3.containsKey(key)) {
        map3.get(key).add(value);
    } else {
        map3.put(key, new ArrayList<>(Arrays.asList(value)));
    }
}
map3.forEach((str, list) -> System.out.println("{" + str + ": " + list + "}"));

вихід:

{key1: [1, 4]}
{key2: [2, 5]}
{key3: [3, 6]}

2

Дуже пізно, але дозвольте мені поділитися тим, що я робив, коли у мене був той самий випуск.

Map<String, List<String>> map1 = new HashMap<>();
map1.put("India", Arrays.asList("Virat", "Mahi", "Rohit"));
map1.put("NZ", Arrays.asList("P1","P2","P3"));

Map<String, List<String>> map2 = new HashMap<>();
map2.put("India", Arrays.asList("Virat", "Mahi", "Rohit"));
map2.put("NZ", Arrays.asList("P1","P2","P4"));

Map<String, List<String>> collect4 = Stream.of(map1, map2)
                .flatMap(map -> map.entrySet().stream())
                .collect(
                        Collectors.toMap(
                                Map.Entry::getKey,
                                Map.Entry::getValue,
                                (strings, strings2) -> {
                                    List<String> newList = new ArrayList<>();
                                    newList.addAll(strings);
                                    newList.addAll(strings2);
                                    return newList;
                                }
                        )
                );
collect4.forEach((s, strings) -> System.out.println(s+"->"+strings));

Він дає наступний вихід

NZ->[P1, P2, P3, P1, P2, P4]
India->[Virat, Mahi, Rohit, Virat, Mahi, Rohit]

0
    HashMap<Integer,String> hs1 = new HashMap<>();
    hs1.put(1,"ram");
    hs1.put(2,"sita");
    hs1.put(3,"laxman");
    hs1.put(4,"hanuman");
    hs1.put(5,"geeta");

    HashMap<Integer,String> hs2 = new HashMap<>();
    hs2.put(5,"rat");
    hs2.put(6,"lion");
    hs2.put(7,"tiger");
    hs2.put(8,"fish");
    hs2.put(9,"hen");

    HashMap<Integer,String> hs3 = new HashMap<>();//Map is which we add

    hs3.putAll(hs1);
    hs3.putAll(hs2);

    System.out.println(" hs1 : " + hs1);
    System.out.println(" hs2 : " + hs2);
    System.out.println(" hs3 : " + hs3);

Дублікати елементів не будуть додані (тобто це дублікати ключів), оскільки коли ми надрукуємо hs3, ми отримаємо лише одне значення для ключа 5, яке буде останньою доданою вартістю, і це буде щур. ** [Набір має властивість забороняти повторювати ключ, але значення можуть бути повтореними]


0

Спосіб 1: Покладіть карти до списку, а потім приєднайтесь

public class Test15 {
public static void main(String[] args) {

    Map<String, List<String>> map1 = new HashMap<>();
    map1.put("London", Arrays.asList("A", "B", "C"));
    map1.put("Wales", Arrays.asList("P1", "P2", "P3"));

    Map<String, List<String>> map2 = new HashMap<>();
    map2.put("Calcutta", Arrays.asList("Protijayi", "Gina", "Gini"));
    map2.put("London", Arrays.asList( "P4", "P5", "P6"));
    map2.put("Wales", Arrays.asList( "P111", "P5555", "P677666"));

    System.out.println(map1);System.out.println(map2);



    // put the maps in an ArrayList

    List<Map<String, List<String>>> maplist = new ArrayList<Map<String,List<String>>>();
    maplist.add(map1);
    maplist.add(map2);
    /*
<T,K,U> Collector<T,?,Map<K,U>> toMap(

                                  Function<? super T,? extends K> keyMapper,

                                  Function<? super T,? extends U> valueMapper,

                                  BinaryOperator<U> mergeFunction)
    */

 Map<String, List<String>> collect = maplist.stream()
    .flatMap(ch -> ch.entrySet().stream())
    .collect(
            Collectors.toMap(

            //keyMapper,

            Entry::getKey,

            //valueMapper
            Entry::getValue,

            // mergeFunction
     (list_a,list_b) -> Stream.concat(list_a.stream(), list_b.stream()).collect(Collectors.toList())

            ));



    System.out.println("Final Result(Map after join) => " + collect);
    /*
    {Wales=[P1, P2, P3], London=[A, B, C]}
{Calcutta=[Protijayi, Gina, Gini], Wales=[P111, P5555, P677666], London=[P4, P5, P6]}
Final Result(Map after join) => {Calcutta=[Protijayi, Gina, Gini], Wales=[P1, P2, P3, P111, P5555, P677666], London=[A, B, C, P4, P5, P6]}
*/

}//main


}

Спосіб 2. Звичайне об'єднання карт

public class Test15 {
public static void main(String[] args) {

    Map<String, List<String>> map1 = new HashMap<>();
    map1.put("London", Arrays.asList("A", "B", "C"));
    map1.put("Wales", Arrays.asList("P1", "P2", "P3"));

    Map<String, List<String>> map2 = new HashMap<>();
    map2.put("Calcutta", Arrays.asList("Protijayi", "Gina", "Gini"));
    map2.put("London", Arrays.asList( "P4", "P5", "P6"));
    map2.put("Wales", Arrays.asList( "P111", "P5555", "P677666"));

    System.out.println(map1);System.out.println(map2);




    /*
<T,K,U> Collector<T,?,Map<K,U>> toMap(

                                  Function<? super T,? extends K> keyMapper,

                                  Function<? super T,? extends U> valueMapper,

                                  BinaryOperator<U> mergeFunction)
    */


Map<String, List<String>> collect = Stream.of(map1,map2)
    .flatMap(ch -> ch.entrySet().stream())
    .collect(
            Collectors.toMap(

            //keyMapper,

            Entry::getKey,

            //valueMapper
            Entry::getValue,

            // mergeFunction
     (list_a,list_b) -> Stream.concat(list_a.stream(), list_b.stream()).collect(Collectors.toList())

            ));



    System.out.println("Final Result(Map after join) => " + collect);
    /*
    {Wales=[P1, P2, P3], London=[A, B, C]}
{Calcutta=[Protijayi, Gina, Gini], Wales=[P111, P5555, P677666], London=[P4, P5, P6]}
Final Result(Map after join) => {Calcutta=[Protijayi, Gina, Gini], Wales=[P1, P2, P3, P111, P5555, P677666], London=[A, B, C, P4, P5, P6]}

*/

}//main


}

0

Ви можете використовувати функцію putAll для Map, як пояснено в коді нижче

HashMap<String, Integer> map1 = new HashMap<String, Integer>();
map1.put("a", 1);
map1.put("b", 2);
map1.put("c", 3);
HashMap<String, Integer> map2 = new HashMap<String, Integer>();
map1.put("aa", 11);
map1.put("bb", 12);
HashMap<String, Integer> map3 = new HashMap<String, Integer>();
map3.putAll(map1);
map3.putAll(map2);
map3.keySet().stream().forEach(System.out::println);
map3.values().stream().forEach(System.out::println);

0

Нижче фрагмент бере кілька карт і поєднує їх.

 private static <K, V> Map<K, V> combineMaps(Map<K, V>... maps) {
        if (maps == null || maps.length == 0) {
            return Collections.EMPTY_MAP;
        }

        Map<K, V> result = new HashMap<>();

        for (Map<K, V> map : maps) {
            result.putAll(map);
        }
        return result;
    }

Демонстраційний приклад посилання.


-1

ви можете використовувати - метод addAll

http://download.oracle.com/javase/6/docs/api/java/util/HashMap.html

Але завжди існує ця проблема, що - якщо у ваших двох хеш-карт однаковий ключ, - це перекриє значення ключа з першої хеш-карти зі значенням ключа з другої хеш-карти.

Для того, щоб бути на безпечнішій стороні - змініть ключові значення - ви можете використовувати префікс або суфікс на клавішах ((різний префікс / суфікс для першої хеш-карти та інший префікс / суфікс для другої хеш-карти)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.