Ignorar duplicatas ao produzir mapa usando fluxos


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

Recebo java.lang.IllegalStateException: Duplicate keyquando um elemento duplicado é encontrado.

É possível ignorar essa exceção ao adicionar valores ao mapa?

Quando houver duplicado, ele simplesmente deve continuar ignorando essa chave duplicada.


Se você pode usá-lo, o HashSet ignorará a chave, se ela já existir.
Sahitya

@ capitão-aryabhatta. É possível ter valores-chave em hashset
Patan

Respostas:


449

Isso é possível usando o mergeFunctionparâmetro de 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é uma função que opera em dois valores associados à mesma chave. adress1corresponde ao primeiro endereço encontrado ao coletar elementos e adress2corresponde ao segundo endereço encontrado: este lambda apenas informa para manter o primeiro endereço e ignora o segundo.


5
Estou confuso, por que valores duplicados (não chaves) não são permitidos? E como permitir valores duplicados?
Hendy Irawan

existe alguma maneira de recuperar a chave para a qual a colisão ocorre? resposta aqui: stackoverflow.com/questions/40761954/…
Guillaume

2
É possível ignorar totalmente essa entrada se houver um choque? Basicamente, se eu encontrar chaves duplicadas, não quero que elas sejam adicionadas. No exemplo acima, não quero o endereço1 ou o endereço2 no meu mapa.
djkelly99

5
@ Hendy Irawan: valores duplicados são permitidos. A função de mesclagem é escolher entre (ou mesclar) dois valores que tenham a mesma chave .
Ricola

3
@ djkelly99 Na verdade, você pode, basta fazer com que sua função de remapeamento retorne null. Consulte o documento toMap que aponta para mesclar o documento que declara Se a função de remapeamento retornar nulo, o mapeamento será removido.
Ricola

98

Como dito no JavaDocs :

Se as chaves mapeadas contiverem duplicatas (de acordo com Object.equals(Object)), um IllegalStateExceptionserá lançado quando a operação de coleta for executada. Se as chaves mapeadas podem ter duplicatas, use toMap(Function keyMapper, Function valueMapper, BinaryOperator mergeFunction).

Então você deve usar em seu toMap(Function keyMapper, Function valueMapper, BinaryOperator mergeFunction)lugar. Basta fornecer uma função de mesclagem , que determinará qual das duplicatas será colocada no mapa.

Por exemplo, se você não se importa com qual, basta ligar

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

8

A resposta @alaster me ajuda muito, mas eu gostaria de adicionar informações importantes se alguém estiver tentando agrupar as informações.

Se você tem, por exemplo, dois produtos Ordersiguais para cada um, codemas diferentes quantity, e seu desejo é somar as quantidades, é possível:

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

Resultado:

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

1

Para agrupar por objetos

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

Para qualquer outra pessoa que esteja recebendo esse problema, mas sem que chaves duplicadas no mapa sejam transmitidas, verifique se a função keyMapper não está retornando valores nulos .

É muito chato rastrear isso porque o erro diz "Chave duplicada 1" quando 1 é realmente o valor da entrada em vez da chave.

No meu caso, minha função keyMapper tentou procurar valores em um mapa diferente, mas devido a um erro de digitação nas strings estava retornando valores nulos.

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

Eu encontrei esse problema ao agrupar objetos, sempre os resolvia de uma maneira simples: execute um filtro personalizado usando um java.util.Set para remover objetos duplicados com qualquer atributo de sua escolha, conforme abaixo

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

Espero que isso ajude alguém com o mesmo problema!


-1

Supondo que você tenha pessoas é Lista de objetos

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

Agora você precisa de duas etapas:

1)

people =removeDuplicate(people);

2)

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

Aqui está o método para remover duplicado

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;

      }

Adicionando exemplo completo aqui

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

}

Resultados :

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)
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.