Ignora i duplicati durante la produzione di mappe utilizzando flussi


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

Ottengo java.lang.IllegalStateException: Duplicate keyquando viene trovato un elemento duplicato.

È possibile ignorare tale eccezione quando si aggiungono valori alla mappa?

Quando c'è un duplicato, semplicemente dovrebbe continuare ignorando quella chiave duplicata.


Se puoi usarlo, HashSet ignorerà la chiave, se esiste già.
sahitya,

@ capitano-Aryabhatta. È possibile avere valori chiave in hashset
Patan,

Risposte:


449

Questo è possibile utilizzando il mergeFunctionparametro di 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è una funzione che opera su due valori associati alla stessa chiave. adress1corrisponde al primo indirizzo che è stato riscontrato durante la raccolta degli elementi e adress2corrisponde al secondo indirizzo riscontrato: questo lambda dice solo di mantenere il primo indirizzo e ignora il secondo.


5
Sono confuso, perché i valori duplicati (non le chiavi) non sono ammessi? E come consentire valori duplicati?
Hendy Irawan,

c'è un modo per recuperare la chiave per cui si verifica la collisione? rispondi qui: stackoverflow.com/questions/40761954/…
Guillaume,

2
È possibile ignorare totalmente questa voce in caso di scontro? Fondamentalmente, se mai dovessi incontrare chiavi duplicate, non voglio che vengano aggiunte affatto. Nell'esempio sopra, non voglio address1 o address2 nella mia mappa.
djkelly99,

5
@Hendy Irawan: sono ammessi valori duplicati. La funzione di fusione è di scegliere tra (o unire) due valori che hanno la stessa chiave .
Ricola,

3
@ djkelly99 In realtà puoi, devi solo far tornare la tua funzione di rimappatura null. Vedere Documento toMap che punta a unire documento che indica Se la funzione di rimappatura restituisce null, la mappatura viene rimossa.
Ricola,

98

Come detto in JavaDocs :

Se le chiavi mappate contengono duplicati (in base a Object.equals(Object)), IllegalStateExceptionviene lanciata quando viene eseguita l'operazione di raccolta. Se le chiavi mappate possono avere duplicati, utilizzare toMap(Function keyMapper, Function valueMapper, BinaryOperator mergeFunction)invece.

Quindi dovresti usare toMap(Function keyMapper, Function valueMapper, BinaryOperator mergeFunction)invece. Basta fornire una funzione di unione , che determinerà quale dei duplicati viene inserito nella mappa.

Ad esempio, se non ti interessa quale, chiama

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

8

La risposta di @alaster mi aiuta molto, ma vorrei aggiungere un'informazione significativa se qualcuno sta cercando di raggruppare le informazioni.

Se ne hai, ad esempio, due Orderscon prodotti uguali codema diversi quantityper ognuno e il tuo desiderio è la somma delle quantità, puoi fare:

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

Risultato:

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

1

Per raggruppare per oggetti

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

Per chiunque abbia questo problema ma senza lo streaming di chiavi duplicate nella mappa, assicurati che la tua funzione keyMapper non restituisca valori null .

È molto fastidioso rintracciarlo perché l'errore dirà "Duplicate key 1" quando 1 è in realtà il valore della voce invece della chiave.

Nel mio caso, la mia funzione keyMapper ha cercato di cercare valori in una mappa diversa, ma a causa di un refuso nelle stringhe stava restituendo valori 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

Ho riscontrato un tale problema durante il raggruppamento di oggetti, li ho sempre risolti in modo semplice: esegui un filtro personalizzato utilizzando un java.util.Set per rimuovere l'oggetto duplicato con qualsiasi attributo di tua scelta come muggito

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

Spero che questo aiuti chiunque abbia lo stesso problema!


-1

Supponendo di avere persone è Elenco di oggetti

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

Ora hai bisogno di due passaggi:

1)

people =removeDuplicate(people);

2)

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

Ecco il metodo per rimuovere i duplicati

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;

      }

Aggiungendo l'esempio completo qui

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

}

Risultati:

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)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.