Игнорировать дубликаты при создании карты с использованием потоков

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

Я получаю, java.lang.IllegalStateException: Duplicate keyкогда дублированный элемент найден.

Можно ли игнорировать такое исключение при добавлении значений на карту?

Когда есть дубликат, он просто должен продолжаться, игнорируя этот дубликат ключа.

Patan
источник
Если вы можете использовать его, HashSet проигнорирует ключ, если он уже существует.
сахитья
@ капитан-Арьябхатт. Возможно ли иметь ключевые значения в хешсете
Патан

Ответы:

449

Это возможно с помощью mergeFunctionпараметра 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это функция, которая работает с двумя значениями, связанными с одним и тем же ключом. adress1соответствует первому адресу, который был обнаружен при сборе элементов, и adress2соответствует второму обнаруженному адресу: эта лямбда просто говорит сохранить первый адрес и игнорирует второй.

Tunaki
источник
6
Я запутался, почему дублированные значения (не ключи) не допускаются? А как разрешить дублирование значений?
Хэнди Ираван
Есть ли способ получить ключ, для которого происходит столкновение? ответ здесь: stackoverflow.com/questions/40761954/…
Гийом
2
Можно ли полностью игнорировать эту запись, если есть столкновение? В принципе, если я когда-либо сталкиваюсь с дублирующимися ключами, я не хочу, чтобы они были добавлены вообще. В приведенном выше примере я не хочу address1 или address2 на моей карте.
djkelly99
5
@Hendy Irawan: допускаются повторяющиеся значения. Функция слияния заключается в выборе (или слиянии) двух значений, имеющих одинаковый ключ .
Рикола
3
@ djkelly99 На самом деле вы можете, вам просто нужно вернуть функцию переназначения null. См. Документ toMap , указывающий на объединение документа, в котором говорится, что если функция переназначения возвращает ноль, отображение удаляется.
Рикола
98

Как сказано в JavaDocs :

Если сопоставленные ключи содержат дубликаты (в соответствии с Object.equals(Object)), IllegalStateExceptionпри выполнении операции сбора выдается символ. Если сопоставленные ключи могут иметь дубликаты, используйте toMap(Function keyMapper, Function valueMapper, BinaryOperator mergeFunction)вместо этого.

Поэтому вы должны использовать toMap(Function keyMapper, Function valueMapper, BinaryOperator mergeFunction)вместо этого. Просто предоставьте функцию слияния , которая определит, какой из дубликатов будет добавлен на карту.

Например, если вам все равно, просто позвоните

Map<String, String> phoneBook = 
        people.stream()
              .collect(Collectors.toMap(Person::getName, 
                                        Person::getAddress, 
                                        (a1, a2) -> a1));
Аластер
источник
8

Ответ @alaster мне очень помогает, но я хотел бы добавить содержательную информацию, если кто-то пытается сгруппировать информацию.

Если у вас есть, например, два Ordersс одинаковыми, codeно разными quantityпродуктами для каждого, и вы хотите суммировать количество, вы можете сделать:

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

Результат:

{COD_3=4, COD_2=3, COD_1=9}
Dherik
источник
2

Для всех, кто столкнулся с этой проблемой, но без дублирования ключей на карте, убедитесь, что ваша функция keyMapper не возвращает нулевые значения .

Это очень раздражает, потому что ошибка скажет «Дублировать ключ 1», когда 1 на самом деле является значением записи, а не ключа.

В моем случае моя функция keyMapper пыталась найти значения на другой карте, но из-за опечатки в строках возвращала нулевые значения.

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()));
Андрей
источник
1

Для группировки по объектам

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
источник
0

Я столкнулся с такой проблемой при группировке объектов, я всегда разрешал их простым способом: выполнял пользовательский фильтр, используя java.util.Set, чтобы удалить дублирующийся объект с любым атрибутом по вашему выбору, как показано ниже.

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

Надеюсь, что это помогает всем, кто испытывает ту же проблему!

Shessuky
источник
-1

Предполагая, что у вас есть люди, список объектов

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

Теперь вам нужно сделать два шага:

1)

people =removeDuplicate(people);

2)

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

Вот метод удаления дубликатов

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;

      }

Добавляем полный пример сюда

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

}

Полученные результаты :

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)
Ваквар хан
источник