ฉันจะรวมวัตถุ 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
คุณไม่ได้ระบุสิ่งที่คุณต้องการให้เกิดขึ้นหากมีคีย์อยู่ในทั้งสองแผนที่
Michael Scheper

คำตอบ:


344
map3 = new HashMap<>();

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

1
ขอบคุณฉันกำลังผสานแผนที่ใน for for loop ซึ่งใช้วิธีคืนแผนที่และจำเป็นต้องรวมเข้ากับแผนที่อื่นและใช้วิธีการแบบเดียวกัน สำหรับเรื่องนี้ฉันได้รับข้อยกเว้นตัวชี้โมฆะด้วยวิธีการ putAll มันไม่ได้ช่วยในการใช้ try / catch block ฉันควรทำอย่างไรดี? ผมใช้ถ้าเงื่อนไขว่าถ้าขนาด == o แล้วไม่ได้ใช้ putAll อื่นนำไปใช้และอื่น ๆ ....
Mavin

1
หากคุณได้รับ NPE ดูเหมือนว่าคุณไม่ได้เริ่มต้นวัตถุอย่างใดอย่างหนึ่งอย่างถูกต้อง คุณพิมพ์สแต็คเทรซในบล็อก catch หรือไม่? เพื่อให้คุณทราบที่ปัญหาเกิดขึ้น แต่ถ้าคุณไม่โพสต์รหัสเต็มและแน่นอนรวมถึงการติดตามสแต็คคุณจะต้องติดตามมันด้วยตัวคุณเอง
a_horse_with_no_name

95
โปรดทราบว่าด้วยวิธีนี้หากมีคีย์อยู่ในแผนที่ทั้งสองค่าใน map2 จะถูกเก็บไว้และค่าใน map1 จะหายไป
Michael Scheper

5
@MichaelScheper: คุณคาดหวังอะไรอีก คีย์ใน a Mapเป็นคำจำกัดความที่ไม่ซ้ำกัน
a_horse_with_no_name

42
ฉันไม่รู้ว่า OPER คาดหวังอะไร บางทีเขาอาจคาดหวังว่าค่าของ map1 จะมีความสำคัญกว่าหรือมีข้อยกเว้นที่จะถูกโยนทิ้งหรือสำหรับการดำเนินการ 'ผสาน' บางอย่างที่ต้องดำเนินการกับการตัดกันจำนวนเต็ม หรืออาจเป็นเพราะนี่เป็นคำถามของผู้เริ่มต้นนี่เป็นกรณีที่ OPer ไม่ได้พิจารณาซึ่งในกรณีนี้ความคิดเห็นของฉันจะเป็นประโยชน์
Michael Scheper

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

One-liner โดยใช้ Java 8 Stream API:

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 นั่นคือจุดรวมของรูปแบบที่สอง บางครั้งคุณต้องการข้อยกเว้นบางครั้งคุณไม่ต้องการ
Alex R

36

Java 8 ทางเลือกเดียวซับสำหรับการผสานสองแผนที่:

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

เช่นเดียวกับการอ้างอิงวิธีการ:

defaultMap.forEach(destMap::putIfAbsent);

หรือ idemponent สำหรับโซลูชันแผนที่ดั้งเดิมพร้อมแผนที่ที่สาม:

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

และนี่คือวิธีรวมแผนที่สองแผนที่เข้ากับแผนที่ที่ไม่เปลี่ยนรูปได้อย่างรวดเร็วด้วยGuavaที่จะทำการคัดลอกระดับกลางได้น้อยที่สุด:

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

หากคุณไม่ต้องการความไม่แน่นอนสำหรับแผนที่สุดท้ายของคุณมีGuava's ImmutableMapพร้อมด้วยBuilderและputAllวิธีการซึ่งตรงกันข้ามกับวิธีส่วนต่อประสานของ Java ที่Mapสามารถผูกมัดได้

ตัวอย่างการใช้งาน:

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

แน่นอนว่าวิธีนี้สามารถใช้ทั่วไปได้มากกว่าใช้ varargs และ loop ไปจนถึงการputAll Mapsขัดแย้งเป็นต้น แต่ฉันต้องการแสดงแนวคิด

นอกจากนี้ImmutableMapและBuilderมีข้อ จำกัด เล็กน้อย (หรืออาจมีคุณสมบัติ):

  • พวกเขาเป็นโมฆะเป็นศัตรู (โยนNullPointerException- หากคีย์หรือค่าใด ๆ ในแผนที่เป็นโมฆะ)
  • ตัวสร้างไม่ยอมรับคีย์ที่ซ้ำกัน (ส่งIllegalArgumentExceptionหากมีการเพิ่มคีย์ซ้ำ)



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>>เพื่อรวม hashmaps ทั้งสองและหลีกเลี่ยงการสูญเสียองค์ประกอบที่จับคู่กับคีย์เดียวกัน

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 สำหรับแผนที่ตามที่อธิบายไว้ในรหัสด้านล่าง

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

แต่มีปัญหานี้อยู่เสมอว่า - ถ้าแฮชแมปทั้งสองของคุณมีรหัสใด ๆ เหมือนกัน - มันจะแทนที่ค่าของคีย์จากแผนที่แฮชแรกด้วยค่าของคีย์จากแผนที่แฮชที่สอง

สำหรับด้านที่ปลอดภัย - เปลี่ยนค่าคีย์ - คุณสามารถใช้คำนำหน้าหรือคำต่อท้ายบนคีย์ - (คำนำหน้า / คำต่อท้ายที่แตกต่างกันสำหรับแผนที่แฮชแรกและคำนำหน้า / คำต่อท้ายที่แตกต่างกันสำหรับแผนที่แฮชที่สอง)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.