Jak mogę połączyć dwa obiekty HashMap zawierające te same typy?


241

Mam dwa HashMapzdefiniowane w ten sposób obiekty:

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

Mam również trzeci HashMapprzedmiot:

HashMap<String, Integer> map3;

Jak mogę połączyć map1i map2razem się map3?


16
Nie podałeś, co chcesz zrobić, jeśli na obu mapach istnieje klucz.
Michael Scheper,

Odpowiedzi:


344
map3 = new HashMap<>();

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

1
dziękuję, łączę Mapy w pętlę for, która korzysta z metody zwrócenia mapy i konieczności scalenia jej z inną mapą i zastosowania tej samej metody. W tym celu otrzymuję wyjątek wskaźnika zerowego z metodą putAll. nie pomaga użycie bloku try / catch. co powinienem zrobić? Zgadzam się, jeśli warunek, że jeśli rozmiar == o to nie stosuj put Wszystkie inne stosują to i tak dalej ....
Mavin

1
Jeśli otrzymasz NPE, to najwyraźniej nie zainicjowałeś poprawnie jednego ze swoich obiektów. Czy drukujesz ślad stosu w bloku catch? Więc wiesz, gdzie pojawia się problem. Ale jeśli nie opublikujesz pełnego i dokładnego kodu, w tym śledzenia stosu, musisz go wyśledzić na własną rękę.
a_horse_w_no_name

95
Zauważ, że dzięki temu rozwiązaniu, jeśli klucz istnieje w obu mapach, wartość w map2 zostanie zachowana, a wartość w map1 utracona.
Michael Scheper,

5
@MichaelScheper: czego jeszcze oczekujesz? Klucze w Mapsą z definicji unikalne
a_horse_w_n_nazwie

42
Nie wiem, czego oczekuje OPer. Być może spodziewa się, że wartości map1 będą miały pierwszeństwo lub wyjątek zostanie zgłoszony, lub że zostanie przeprowadzona jakaś operacja łączenia na przecinających się liczbach całkowitych. A może, ponieważ jest to pytanie dla początkujących, jest to przypadek, który OPer nie wziął pod uwagę, w którym to przypadku mam nadzieję, że mój komentarz byłby pomocny.
Michael Scheper

109

Jeśli wiesz, że nie masz zduplikowanych kluczy lub chcesz, aby wartości map2zastąpiły wartości map1dla zduplikowanych kluczy, możesz po prostu napisać

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

Jeśli potrzebujesz większej kontroli nad sposobem łączenia wartości, możesz użyć Map.mergedodanej w Javie 8, która wykorzystuje dane dostarczone przez użytkownika BiFunctiondo scalenia wartości dla duplikatów kluczy. mergedziała na poszczególnych kluczach i wartościach, więc musisz użyć pętli lub Map.forEach. Tutaj łączymy ciągi dla duplikatów kluczy:

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));

Jeśli wiesz, że nie masz zduplikowanych kluczy i chcesz je wymusić, możesz użyć funkcji scalania, która generuje AssertionError:

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

Cofając się od tego konkretnego pytania, biblioteka strumieni Java 8 zapewnia toMapi groupingBy Kolektory . Jeśli wielokrotnie łączysz mapy w pętli, możesz być w stanie zrestrukturyzować swoje obliczenia, aby używać strumieni, które mogą zarówno wyjaśnić kod, jak i umożliwić łatwą równoległość za pomocą strumienia równoległego i równoległego kolektora.


46

Jednowierszowy korzystający z interfejsu API Java 8 Stream:

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

Jedną z zalet tej metody jest możliwość przekazania funkcji scalania, która będzie zajmować się wartościami o tym samym kluczu, na przykład:

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

1
spowoduje to zgłoszenie wyjątku IllegalStateException dla duplikatów kluczy
Arpit J.

1
@ArpitJ. to jest sedno drugiej odmiany. Czasami chcesz wyjątku, czasem nie.
Alex R

36

Alternatywny jednowierszowy Java 8 do łączenia dwóch map:

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

To samo dotyczy odwołania do metody:

defaultMap.forEach(destMap::putIfAbsent);

Lub idemponent dla oryginalnego rozwiązania map z trzecią mapą:

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

A oto sposób na połączenie dwóch map w szybko niezmienną z Guava, który wykonuje najmniej możliwe operacje kopiowania pośredniego:

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();

Zobacz także Scal dwie mapy z Javą 8, aby zapoznać się z przypadkami, gdy wartości obecne w obu mapach muszą być połączone z funkcją mapowania.


32

Jeśli nie potrzebujesz zmienności dla ostatecznej mapy, jest Guava ImmutableMap z jego Builderi putAllmetodą, która w przeciwieństwie do metody interfejsu JavyMap , mogą być powiązane.

Przykład zastosowania:

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

Oczywiście ta metoda może być bardziej ogólna, użyj varargs i zapętl do putAll Maps argumentów itp., Ale chciałem pokazać koncepcję.

Również ImmutableMapi jego Builderposiada kilka ograniczeń (albo może być wyposażony):

  • są zerowe wrogo (rzut NullPointerException - jeśli jakikolwiek klucz lub wartość na mapie jest pusta)
  • Konstruktor nie akceptuje duplikatów kluczy (wyrzuca, IllegalArgumentExceptionjeśli dodano duplikaty kluczy).



11

Ogólne rozwiązanie do łączenia dwóch map, które mogą współdzielić wspólne klucze:

W miejscu:

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));
}

Zwracanie nowej mapy:

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

Mały fragment, którego używam bardzo często do tworzenia mapy z innych map:

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

możesz użyć HashMap<String, List<Integer>>do scalenia obu map skrótów i uniknięcia utraty elementów sparowanych z tym samym kluczem.

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 + "}"));

wynik:

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

2

Bardzo późno, ale pozwólcie, że podzielę się tym, co zrobiłem, gdy miałem ten sam problem.

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));

Daje następujące dane wyjściowe

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);

Zduplikowane elementy nie zostaną dodane (to znaczy zduplikowane klucze), ponieważ kiedy wydrukujemy hs3, otrzymamy tylko jedną wartość dla klucza 5, który będzie ostatnią wartością dodaną i będzie to szczur. ** [Zestaw ma właściwość polegającą na niedozwoleniu na duplikat klucza, ale wartości mogą być duplikowane]


0

Metoda 1: Umieść mapy na liście, a następnie dołącz

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


}

Metoda 2: Normalne scalenie mapy

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

Możesz użyć funkcji putAll dla mapy, jak wyjaśniono w poniższym kodzie

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

Poniżej fragment zajmuje więcej niż jedną mapę i łączy je.

 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;
    }

Przykładowy link demonstracyjny .


-1

możesz użyć metody addAll

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

Ale zawsze istnieje problem, że - jeśli dwie mapy skrótów mają taki sam klucz - wówczas zastąpi wartość klucza z pierwszej mapy skrótu wartością klucza z drugiej mapy skrótu.

Aby być bezpieczniejszym - zmień wartości kluczy - możesz użyć przedrostka lub sufiksu na klawiszach - (inny prefiks / sufiks dla pierwszej mapy hash i inny prefiks / sufiks dla drugiej mapy hash)

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.