Abaikan duplikat saat membuat peta menggunakan aliran

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

Saya dapatkan java.lang.IllegalStateException: Duplicate keyketika elemen digandakan ditemukan.

Apakah mungkin untuk mengabaikan pengecualian seperti itu dalam menambahkan nilai ke peta?

Ketika ada duplikat itu harus dilanjutkan dengan mengabaikan kunci duplikat itu.

Patan
sumber
Jika Anda dapat menggunakannya, HashSet akan mengabaikan kunci, jika sudah ada.
sahitya
@ kapten-aryabhatta. Apakah mungkin untuk memiliki nilai-nilai kunci dalam hashset
Patan

Jawaban:

449

Ini dimungkinkan menggunakan mergeFunctionparameter 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;
             }
          ));

mergeFunctionadalah fungsi yang beroperasi pada dua nilai yang terkait dengan kunci yang sama. adress1sesuai dengan alamat pertama yang ditemui ketika mengumpulkan elemen dan adress2sesuai dengan alamat kedua yang ditemui: lambda ini hanya memberitahu untuk menjaga alamat pertama dan mengabaikan yang kedua.

Tunaki
sumber
5
Saya bingung, mengapa nilai duplikat (bukan kunci) tidak diizinkan? Dan bagaimana cara memungkinkan nilai duplikat?
Hendy Irawan
apakah ada cara untuk mengambil kunci yang menyebabkan tabrakan? jawab di sini: stackoverflow.com/questions/40761954/…
Guillaume
2
Apakah mungkin untuk sepenuhnya mengabaikan entri ini jika ada bentrokan? Pada dasarnya, jika saya pernah menemukan kunci duplikat saya tidak ingin mereka ditambahkan sama sekali. Pada contoh di atas, saya tidak ingin address1 atau address2 di peta saya.
djkelly99
5
@Hendy Irawan: nilai duplikat diizinkan. Fungsi penggabungan adalah untuk memilih antara (atau menggabungkan) dua nilai yang memiliki kunci yang sama .
Ricola
3
@ djkelly99 Sebenarnya Anda bisa, Anda hanya perlu mengembalikan fungsi remapping Anda null. Lihat dokumen toMap yang menunjuk untuk menggabungkan dokumen yang menyatakan Jika fungsi pemetaan kembali nol, pemetaan dihapus.
Ricola
98

Seperti yang dikatakan dalam JavaDocs :

Jika kunci yang dipetakan berisi duplikat (sesuai dengan Object.equals(Object)), sebuah IllegalStateExceptiondilemparkan ketika operasi pengumpulan dilakukan. Jika kunci yang dipetakan mungkin memiliki duplikat, gunakan toMap(Function keyMapper, Function valueMapper, BinaryOperator mergeFunction)saja.

Jadi sebaiknya Anda gunakan toMap(Function keyMapper, Function valueMapper, BinaryOperator mergeFunction)saja. Cukup berikan fungsi penggabungan , yang akan menentukan duplikat mana yang diletakkan di peta.

Misalnya, jika Anda tidak peduli yang mana, hubungi saja

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

Jawaban @alaster banyak membantu saya, tetapi saya ingin menambahkan informasi yang berarti jika seseorang mencoba mengelompokkan informasi tersebut.

Jika Anda memiliki, misalnya, dua Ordersdengan produk yang sama codetetapi berbeda quantityuntuk masing-masing, dan keinginan Anda adalah jumlah jumlahnya, Anda dapat melakukannya:

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

Hasil:

{COD_3=4, COD_2=3, COD_1=9}
Dherik
sumber
1

Untuk dikelompokkan berdasarkan Objek

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

Untuk orang lain yang mendapatkan masalah ini tetapi tanpa kunci duplikat di peta yang dialirkan, pastikan fungsi keyMapper Anda tidak mengembalikan nilai nol .

Sangat menjengkelkan untuk melacak ini karena kesalahannya akan mengatakan "Duplicate key 1" ketika 1 sebenarnya nilai entri, bukan kunci.

Dalam kasus saya, fungsi keyMapper saya mencoba mencari nilai di peta yang berbeda, tetapi karena kesalahan ketik dalam string mengembalikan nilai nol.

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()));
Andrew
sumber
0

Saya telah mengalami masalah seperti itu ketika mengelompokkan objek, saya selalu menyelesaikannya dengan cara sederhana: melakukan filter kustom menggunakan java.util.Set untuk menghapus objek duplikat dengan atribut apa pun pilihan Anda seperti di bawah ini

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

Semoga ini bisa membantu siapa pun yang memiliki masalah yang sama!

Shessuky
sumber
-1

Anggap Anda memiliki orang adalah Daftar objek

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

Sekarang Anda membutuhkan dua langkah:

1)

people =removeDuplicate(people);

2)

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

Berikut adalah metode untuk menghapus duplikat

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;

      }

Menambahkan contoh lengkap di sini

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

}

Hasil:

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)
vaquar khan
sumber