Ignoruj ​​duplikaty podczas tworzenia mapy za pomocą strumieni

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

Dostaję się, java.lang.IllegalStateException: Duplicate keygdy zostanie znaleziony zduplikowany element.

Czy można zignorować taki wyjątek podczas dodawania wartości do mapy?

W przypadku duplikatu należy po prostu kontynuować ignorowanie tego duplikatu klucza.

Patan
źródło
Jeśli możesz go użyć, HashSet zignoruje klucz, jeśli już istnieje.
sahitya
@ captain-aryabhatta. Czy można mieć kluczowe wartości w haszcie
Patan

Odpowiedzi:

448

Jest to możliwe przy użyciu mergeFunctionparametru 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;
             }
          ));

mergeFunctionto funkcja działająca na dwóch wartościach powiązanych z tym samym kluczem. adress1odpowiada pierwszemu adresowi napotkanemu podczas zbierania elementów i adress2odpowiada drugiemu napotkanemu adresowi: ta lambda po prostu mówi, aby zachować pierwszy adres i ignoruje drugi.

Tunaki
źródło
5
Jestem zdezorientowany, dlaczego zduplikowane wartości (nie klucze) są niedozwolone? I jak zezwolić na zduplikowane wartości?
Hendy Irawan
czy jest jakiś sposób na odzyskanie klucza, dla którego dochodzi do kolizji? odpowiedz tutaj: stackoverflow.com/questions/40761954/…
Guillaume,
2
Czy można całkowicie zignorować ten wpis, jeśli dojdzie do kolizji? Zasadniczo, jeśli kiedykolwiek napotkam zduplikowane klucze, nie chcę, aby były w ogóle dodawane. W powyższym przykładzie nie chcę adresu1 ani adresu2 na mojej mapie.
djkelly99
5
@Hendy Irawan: dozwolone są zduplikowane wartości. Funkcja łączenia polega na wybraniu (lub scaleniu) dwóch wartości, które mają ten sam klucz .
Ricola
3
@ djkelly99 Tak naprawdę możesz po prostu przywrócić funkcję mapowania null. Zobacz toMap doc wskazujący na scalenie dokumentu, który stwierdza: Jeśli funkcja odwzorowania zwraca null, mapowanie jest usuwane.
Ricola
98

Jak powiedziano w JavaDocs :

Jeśli zmapowane klucze zawierają duplikaty (zgodnie z Object.equals(Object)), IllegalStateExceptionpodczas wykonywania operacji gromadzenia generowany jest znak „a”. Jeśli zmapowane klucze mogą mieć duplikaty, użyj toMap(Function keyMapper, Function valueMapper, BinaryOperator mergeFunction)zamiast tego.

Więc powinieneś użyć toMap(Function keyMapper, Function valueMapper, BinaryOperator mergeFunction)zamiast tego. Wystarczy podać funkcję scalania , która określi, który z duplikatów zostanie umieszczony na mapie.

Na przykład, jeśli nie obchodzi Cię który, po prostu zadzwoń

Map<String, String> phoneBook = 
        people.stream()
              .collect(Collectors.toMap(Person::getName, 
                                        Person::getAddress, 
                                        (a1, a2) -> a1));
alaster
źródło
8

Odpowiedź @alaster bardzo mi pomaga, ale chciałbym dodać znaczącą informację, jeśli ktoś próbuje pogrupować informacje.

Jeśli masz na przykład dwa Orderstakie same, codeale różne quantityprodukty dla każdego z nich, a Twoim pragnieniem jest zsumowanie ilości, możesz:

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

Wynik:

{COD_3=4, COD_2=3, COD_1=9}
Dherik
źródło
1

Do grupowania według obiektów

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
źródło
1

Dla każdego, kto dostanie ten problem, ale bez przesyłania duplikatów kluczy w mapie, upewnij się, że funkcja keyMapper nie zwraca wartości null .

Śledzenie tego jest bardzo denerwujące, ponieważ błąd powie „Duplikuj klucz 1”, gdy 1 jest faktycznie wartością wpisu zamiast klucza.

W moim przypadku moja funkcja keyMapper próbowała wyszukać wartości na innej mapie, ale z powodu literówki w łańcuchach zwracała wartości 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()));
Andrzej
źródło
0

Zetknąłem się z takim problemem podczas grupowania obiektu, zawsze rozwiązywałem je w prosty sposób: wykonaj niestandardowy filtr za pomocą pliku java.util.Set, aby usunąć zduplikowany obiekt z dowolnym wybranym atrybutem poniżej

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

Mam nadzieję, że pomoże to każdemu, kto ma ten sam problem!

Shessuky
źródło
-1

Zakładając, że masz ludzi, to lista obiektów

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

Teraz potrzebujesz dwóch kroków:

1)

people =removeDuplicate(people);

2)

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

Oto metoda usunięcia duplikatu

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;

      }

Dodanie pełnego przykładu tutaj

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

}

Wyniki:

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
źródło