ละเว้นการทำซ้ำเมื่อสร้างแผนที่โดยใช้สตรีม


257
Map<String, String> phoneBook = people.stream()
                                      .collect(toMap(Person::getName,
                                                     Person::getAddress));

ฉันได้รับjava.lang.IllegalStateException: Duplicate keyเมื่อพบองค์ประกอบที่ซ้ำกัน

เป็นไปได้หรือไม่ที่จะละเว้นข้อยกเว้นเช่นการเพิ่มค่าลงในแผนที่?

เมื่อมีการทำซ้ำก็ควรดำเนินการต่อโดยละเว้นคีย์ที่ซ้ำกันนั้น


หากคุณสามารถใช้งานได้ HashSet จะไม่สนใจคีย์หากมีอยู่แล้ว
sahitya

@ กัปตัน aryabhatta เป็นไปได้หรือไม่ที่จะมีค่าคีย์ในแฮชเซ็ต
Patan

คำตอบ:


449

สิ่งนี้เป็นไปได้โดยใช้mergeFunctionพารามิเตอร์ของ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เป็นฟังก์ชั่นที่ทำงานกับสองค่าที่เกี่ยวข้องกับคีย์เดียวกัน adress1สอดคล้องกับที่อยู่แรกที่พบเมื่อรวบรวมองค์ประกอบและadress2สอดคล้องกับที่อยู่ที่สองที่พบ: แลมบ์ดานี้เพียงบอกให้เก็บที่อยู่แรกและไม่สนใจที่สอง


5
ฉันสับสนทำไมค่าที่ซ้ำกัน(ไม่ใช่คีย์) ไม่ได้รับอนุญาต และวิธีการอนุญาตค่าที่ซ้ำกัน?
Hendy Irawan

มีวิธีใดในการดึงคีย์ที่เกิดการชนกัน ตอบได้ที่นี่: stackoverflow.com/questions/40761954/…
Guillaume

2
เป็นไปได้ไหมที่จะเพิกเฉยต่อข้อความนี้ถ้ามีการปะทะ โดยทั่วไปถ้าฉันเคยพบกุญแจที่ซ้ำกันฉันไม่ต้องการให้เพิ่มเลย ในตัวอย่างข้างต้นฉันไม่ต้องการที่อยู่ 1 หรือที่อยู่ 2 ในแผนที่ของฉัน
djkelly99

5
@Hendy Irawan: อนุญาตให้ใช้ค่าที่ซ้ำกันได้ ฟังก์ชั่นการควบรวมกิจการคือการเลือกระหว่าง (หรือการผสาน) สองค่าที่มีคีย์เดียวกัน
Ricola

3
@ djkelly99 nullจริงคุณสามารถคุณเพียงแค่ต้องทำให้ผลตอบแทนจากฟังก์ชั่นแมปของคุณ โปรดดูtoMap docที่ชี้ไปที่ผสานเอกสารที่ระบุหากฟังก์ชันการแม็พส่งคืนค่า null การแม็พจะถูกลบออก
Ricola

98

ดังที่ได้กล่าวไว้ในJavaDocs :

หากคีย์ที่แม็พมีการซ้ำ (ตาม Object.equals(Object)) IllegalStateExceptionจะถูกโยนทิ้งเมื่อดำเนินการรวบรวม หากคีย์ที่แมปอาจซ้ำซ้อนให้ใช้toMap(Function keyMapper, Function valueMapper, BinaryOperator mergeFunction)แทน

ดังนั้นคุณควรใช้toMap(Function keyMapper, Function valueMapper, BinaryOperator mergeFunction)แทน เพียงแค่ให้ฟังก์ชั่นการผสานที่จะกำหนดว่ารายการใดรายการหนึ่งถูกวางลงในแผนที่

ตัวอย่างเช่นหากคุณไม่สนใจว่าจะเรียกใคร

Map<String, String> phoneBook = 
        people.stream()
              .collect(Collectors.toMap(Person::getName, 
                                        Person::getAddress, 
                                        (a1, a2) -> a1));

8

คำตอบ @alaster ช่วยฉันได้มาก แต่ฉันต้องการเพิ่มข้อมูลที่มีค่าต่ำต้อยถ้ามีคนพยายามจัดกลุ่มข้อมูล

หากคุณมีสองอย่างOrdersที่เหมือนกันcodeแต่แตกต่างกันquantityสำหรับแต่ละผลิตภัณฑ์และความปรารถนาของคุณคือผลรวมปริมาณคุณสามารถทำได้:

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

ผลลัพธ์:

{COD_3=4, COD_2=3, COD_1=9}

1

สำหรับการจัดกลุ่มตามวัตถุ

Map<Integer, Data> dataMap = dataList.stream().collect(Collectors.toMap(Data::getId, data-> data, (data1, data2)-> {LOG.info("Duplicate Group For :" + data2.getId());return data1;}));

1

สำหรับคนอื่นได้รับปัญหานี้ แต่ไม่มีกุญแจที่ซ้ำกันในแผนที่เป็นสตรีมมิ่งให้แน่ใจว่าฟังก์ชั่น keyMapper ของคุณไม่ได้กลับค่า

มันน่ารำคาญมากที่จะติดตามสิ่งนี้ลงเพราะข้อผิดพลาดจะพูดว่า "คีย์ซ้ำ 1" เมื่อ 1 คือมูลค่าของรายการแทนที่จะเป็นคีย์

ในกรณีของฉันฟังก์ชั่น keyMapper ของฉันพยายามค้นหาค่าในแผนที่ที่แตกต่างกัน แต่เนื่องจากพิมพ์ผิดในสตริงที่ส่งคืนค่า null

final Map<String, String> doop = new HashMap<>();
doop.put("a", "1");
doop.put("b", "2");

final Map<String, String> lookup = new HashMap<>();
doop.put("c", "e");
doop.put("d", "f");

doop.entrySet().stream().collect(Collectors.toMap(e -> lookup.get(e.getKey()), e -> e.getValue()));

0

ฉันพบปัญหาดังกล่าวเมื่อจัดกลุ่มวัตถุฉันมักจะแก้ไขปัญหาเหล่านั้นด้วยวิธีง่าย ๆ : ใช้ตัวกรองที่กำหนดเองโดยใช้ java.util.Set เพื่อลบวัตถุที่ซ้ำกันด้วยคุณลักษณะใด ๆ ที่คุณเลือกเป็นร้อง

Set<String> uniqueNames = new HashSet<>();
Map<String, String> phoneBook = people
                  .stream()
                  .filter(person -> person != null && !uniqueNames.add(person.getName()))
                  .collect(toMap(Person::getName, Person::getAddress));

หวังว่านี่จะช่วยให้ทุกคนที่มีปัญหาเดียวกัน!


-1

สมมติว่าคุณมีคนเป็นรายชื่อของวัตถุ

  Map<String, String> phoneBook=people.stream()
                                        .collect(toMap(Person::getName, Person::getAddress));

ตอนนี้คุณต้องการสองขั้นตอน:

1)

people =removeDuplicate(people);

2)

Map<String, String> phoneBook=people.stream()
                                        .collect(toMap(Person::getName, Person::getAddress));

นี่คือวิธีการลบที่ซ้ำกัน

public static List removeDuplicate(Collection<Person>  list) {
        if(list ==null || list.isEmpty()){
            return null;
        }

        Object removedDuplicateList =
                list.stream()
                     .distinct()
                     .collect(Collectors.toList());
     return (List) removedDuplicateList;

      }

การเพิ่มตัวอย่างแบบสมบูรณ์ที่นี่

 package com.example.khan.vaquar;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class RemovedDuplicate {

    public static void main(String[] args) {
        Person vaquar = new Person(1, "Vaquar", "Khan");
        Person zidan = new Person(2, "Zidan", "Khan");
        Person zerina = new Person(3, "Zerina", "Khan");

        // Add some random persons
        Collection<Person> duplicateList = Arrays.asList(vaquar, zidan, zerina, vaquar, zidan, vaquar);

        //
        System.out.println("Before removed duplicate list" + duplicateList);
        //
        Collection<Person> nonDuplicateList = removeDuplicate(duplicateList);
        //
        System.out.println("");
        System.out.println("After removed duplicate list" + nonDuplicateList);
        ;

        // 1) solution Working code
        Map<Object, Object> k = nonDuplicateList.stream().distinct()
                .collect(Collectors.toMap(s1 -> s1.getId(), s1 -> s1));
        System.out.println("");
        System.out.println("Result 1 using method_______________________________________________");
        System.out.println("k" + k);
        System.out.println("_____________________________________________________________________");

        // 2) solution using inline distinct()
        Map<Object, Object> k1 = duplicateList.stream().distinct()
                .collect(Collectors.toMap(s1 -> s1.getId(), s1 -> s1));
        System.out.println("");
        System.out.println("Result 2 using inline_______________________________________________");
        System.out.println("k1" + k1);
        System.out.println("_____________________________________________________________________");

        //breacking code
        System.out.println("");
        System.out.println("Throwing exception _______________________________________________");
        Map<Object, Object> k2 = duplicateList.stream()
                .collect(Collectors.toMap(s1 -> s1.getId(), s1 -> s1));
        System.out.println("");
        System.out.println("k2" + k2);
        System.out.println("_____________________________________________________________________");
    }

    public static List removeDuplicate(Collection<Person> list) {
        if (list == null || list.isEmpty()) {
            return null;
        }

        Object removedDuplicateList = list.stream().distinct().collect(Collectors.toList());
        return (List) removedDuplicateList;

    }

}

// Model class
class Person {
    public Person(Integer id, String fname, String lname) {
        super();
        this.id = id;
        this.fname = fname;
        this.lname = lname;
    }

    private Integer id;
    private String fname;
    private String lname;

    // Getters and Setters

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getFname() {
        return fname;
    }

    public void setFname(String fname) {
        this.fname = fname;
    }

    public String getLname() {
        return lname;
    }

    public void setLname(String lname) {
        this.lname = lname;
    }

    @Override
    public String toString() {
        return "Person [id=" + id + ", fname=" + fname + ", lname=" + lname + "]";
    }

}

ผล :

Before removed duplicate list[Person [id=1, fname=Vaquar, lname=Khan], Person [id=2, fname=Zidan, lname=Khan], Person [id=3, fname=Zerina, lname=Khan], Person [id=1, fname=Vaquar, lname=Khan], Person [id=2, fname=Zidan, lname=Khan], Person [id=1, fname=Vaquar, lname=Khan]]

After removed duplicate list[Person [id=1, fname=Vaquar, lname=Khan], Person [id=2, fname=Zidan, lname=Khan], Person [id=3, fname=Zerina, lname=Khan]]

Result 1 using method_______________________________________________
k{1=Person [id=1, fname=Vaquar, lname=Khan], 2=Person [id=2, fname=Zidan, lname=Khan], 3=Person [id=3, fname=Zerina, lname=Khan]}
_____________________________________________________________________

Result 2 using inline_______________________________________________
k1{1=Person [id=1, fname=Vaquar, lname=Khan], 2=Person [id=2, fname=Zidan, lname=Khan], 3=Person [id=3, fname=Zerina, lname=Khan]}
_____________________________________________________________________

Throwing exception _______________________________________________
Exception in thread "main" java.lang.IllegalStateException: Duplicate key Person [id=1, fname=Vaquar, lname=Khan]
    at java.util.stream.Collectors.lambda$throwingMerger$0(Collectors.java:133)
    at java.util.HashMap.merge(HashMap.java:1253)
    at java.util.stream.Collectors.lambda$toMap$58(Collectors.java:1320)
    at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
    at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
    at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
    at com.example.khan.vaquar.RemovedDuplicate.main(RemovedDuplicate.java:48)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.