Ignorieren Sie Duplikate, wenn Sie eine Karte mit Streams erstellen

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

Ich bekomme, java.lang.IllegalStateException: Duplicate keywenn ein dupliziertes Element gefunden wird.

Ist es möglich, eine solche Ausnahme beim Hinzufügen von Werten zur Karte zu ignorieren?

Wenn es ein Duplikat gibt, sollte es einfach fortgesetzt werden, indem dieser doppelte Schlüssel ignoriert wird.

Patan
quelle
Wenn Sie es verwenden können, ignoriert HashSet den Schlüssel, falls er bereits vorhanden ist.
Sahitya
@ Captain-Aryabhatta. Ist es möglich, Schlüsselwerte in Hashset zu haben
Patan

Antworten:

449

Dies ist möglich mit dem 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;
             }
          ));

mergeFunctionist eine Funktion, die mit zwei Werten arbeitet, die derselben Taste zugeordnet sind. adress1entspricht der ersten Adresse, die beim Sammeln von Elementen angetroffen wurde, und adress2entspricht der zweiten gefundenen Adresse: Dieses Lambda weist nur an, die erste Adresse beizubehalten, und ignoriert die zweite.

Tunaki
quelle
5
Ich bin verwirrt, warum sind doppelte Werte (keine Schlüssel) nicht erlaubt? Und wie kann man doppelte Werte zulassen?
Hendy Irawan
Gibt es eine Möglichkeit, den Schlüssel abzurufen, für den die Kollision auftritt? Antwort hier: stackoverflow.com/questions/40761954/…
Guillaume
2
Ist es möglich, diesen Eintrag bei einem Konflikt vollständig zu ignorieren? Wenn ich jemals auf doppelte Schlüssel stoße, möchte ich grundsätzlich nicht, dass diese überhaupt hinzugefügt werden. Im obigen Beispiel möchte ich keine Adresse1 oder Adresse2 in meiner Karte haben.
djkelly99
5
@ Endy Irawan: Doppelte Werte sind erlaubt. Die Zusammenführungsfunktion besteht darin, zwischen zwei Werten mit demselben Schlüssel zu wählen (oder diese zusammenzuführen) .
Ricola
3
@ djkelly99 Eigentlich kannst du, du musst nur deine Remapping-Funktion zurückgeben null. Siehe toMap-Dokument , das auf das Zusammenführen eines Dokuments verweist , das angibt. Wenn die Neuzuordnungsfunktion null zurückgibt, wird die Zuordnung entfernt.
Ricola
98

Wie in JavaDocs gesagt :

Wenn die zugeordneten Schlüssel Duplikate enthalten (gemäß Object.equals(Object)), IllegalStateExceptionwird beim Ausführen des Erfassungsvorgangs ein ausgelöst. Wenn die zugeordneten Schlüssel möglicherweise Duplikate enthalten, verwenden Sie toMap(Function keyMapper, Function valueMapper, BinaryOperator mergeFunction)stattdessen.

Also solltest du toMap(Function keyMapper, Function valueMapper, BinaryOperator mergeFunction)stattdessen verwenden. Stellen Sie einfach eine Zusammenführungsfunktion bereit , die bestimmt, welches der Duplikate in die Karte eingefügt wird.

Wenn es Ihnen zum Beispiel egal ist, welches, rufen Sie einfach an

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

Die @ alaster-Antwort hilft mir sehr, aber ich möchte eine aussagekräftige Information hinzufügen, wenn jemand versucht, die Information zu gruppieren.

Wenn Sie beispielsweise zwei Produkte mit jeweils Ordersgleichen, codeaber unterschiedlichen quantityProdukten haben und Ihr Wunsch darin besteht, die Mengen zu summieren , können Sie Folgendes tun:

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

Ergebnis:

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

Zum Gruppieren nach Objekten

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
quelle
1

Stellen Sie sicher, dass Ihre keyMapper-Funktion keine Nullwerte zurückgibt, wenn dieses Problem auftritt , ohne dass doppelte Schlüssel in der Map gestreamt werden .

Es ist sehr ärgerlich, dies aufzuspüren, da der Fehler "Schlüssel 1 duplizieren" anzeigt, wenn 1 tatsächlich der Wert des Eintrags anstelle des Schlüssels ist.

In meinem Fall hat meine keyMapper-Funktion versucht, Werte in einer anderen Zuordnung nachzuschlagen, aber aufgrund eines Tippfehlers in den Zeichenfolgen wurden Nullwerte zurückgegeben.

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
quelle
0

Ich habe ein solches Problem beim Gruppieren von Objekten festgestellt. Ich habe sie immer auf einfache Weise gelöst: Führen Sie einen benutzerdefinierten Filter mit java.util.Set aus, um doppelte Objekte mit einem beliebigen Attribut Ihrer Wahl als unten zu entfernen

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

Hoffe das hilft jedem, der das gleiche Problem hat!

Shessuky
quelle
-1

Angenommen, Sie haben Personen, ist die Liste der Objekte

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

Jetzt brauchen Sie zwei Schritte:

1)

people =removeDuplicate(people);

2)

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

Hier ist eine Methode zum Entfernen von Duplikaten

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;

      }

Hier ein vollständiges Beispiel hinzufügen

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

}

Ergebnisse :

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
quelle