【问题标题】:Ignore duplicates when producing map using streams使用流生成地图时忽略重复项
【发布时间】:2015-11-25 14:01:15
【问题描述】:
Map<String, String> phoneBook = people.stream()
                                      .collect(toMap(Person::getName,
                                                     Person::getAddress));

当找到重复的元素时,我得到java.lang.IllegalStateException: Duplicate key

是否可以在向地图添加值时忽略此类异常?

当存在重复时,只需忽略该重复键即可继续。

【问题讨论】:

  • 如果可以使用,HashSet 将忽略该键,如果它已经存在。
  • @captain-aryabhatta。是否可以在 hashset 中有键值

标签: java java-8 java-stream


【解决方案1】:

这可以使用Collectors.toMap(keyMapper, valueMapper, mergeFunction)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 对应于遇到的第二个地址:这个 lambda 只告诉保留第一个地址而忽略第二个地址。

【讨论】:

  • 我很困惑,为什么不允许重复的(不是键)?以及如何允许重复值?
  • 如果发生冲突,是否可以完全忽略此条目?基本上,如果我遇到重复的键,我根本不希望添加它们。在上面的示例中,我不想在我的地图中使用 address1 或 address2。
  • @Hendy Irawan :允许重复值。合并功能是在具有相同键的两个值之间进行选择(或合并)。
  • @djkelly99 其实你可以,你只需要让你的重映射函数返回null。请参阅 toMap doc 指向 merge doc 声明 如果重新映射函数返回 null,则删除映射。
  • 我们不应该返回 address2 来模仿标准地图行为。如果这是一个 for each 而不是一个 collect 的标准行为将是放在第二个地址上会消灭第一个。因此,为了避免代码重构发生时行为发生变化,地址 2 是合乎逻辑的选择。
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    @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}
    

    或者来自javadocs的组合地址:

     Map<String, String> phoneBook
         people.stream().collect(toMap(Person::getName,
                                       Person::getAddress,
                                       (s, a) -> s + ", " + a));
    

    【讨论】:

      【解决方案4】:

      对于遇到此问题但在流式传输的地图中没有重复键的其他人,确保您的 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()));
      

      【讨论】:

        【解决方案5】:

        用于按对象分组

        Map<Integer, Data> dataMap = dataList.stream().collect(Collectors.toMap(Data::getId, data-> data, (data1, data2)-> {LOG.info("Duplicate Group For :" + data2.getId());return data1;}));
        

        【讨论】:

          【解决方案6】:

          我在对对象进行分组时遇到过这样的问题,我总是通过一种简单的方式解决它们:使用 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));
          

          希望这对遇到同样问题的人有所帮助!

          【讨论】:

            【解决方案7】:

            感觉toMap 经常工作但并非总是如此,这是 java Streams 的一个阴暗面。就像他们应该称它为 toUniqueMap 或其他什么...

            最简单的方法是使用Collectors.groupingByinstead ofCollectors.toMap

            默认情况下它会返回一个List 类型的输出,但是碰撞问题已经消失了,这也许是你想要的在存在倍数的情况下。

              Map<String, List<Person>> phoneBook = people.stream()
                      .collect(groupingBy((x) -> x.name));
            

            如果Set 类型的地址集合与特定名称相关联,groupingBy 可以做到这一点as well

            Map<String, Set<String>> phoneBook = people.stream()
                      .collect(groupingBy((x) -> x.name, mapping((x) -> x.address, toSet())));
            

            另一种方法是从散列或集合“开始”...并仔细跟踪以确保键在输出流中不会重复。啊。这是一个example,恰好在这...有时...

            【讨论】:

              【解决方案8】:

              为了完整起见,以下是如何将重复“减少”到只有一个。

              如果你对最后一个没问题:

                Map<String, Person> phoneBook = people.stream()
                        .collect(groupingBy(x -> x.name, reducing(null, identity(), (first, last) -> last)));
              

              如果你只想要第一个:

                Map<String, Person> phoneBook = people.stream()
                        .collect(groupingBy(x -> x.name, reducing(null, identity(), (first, last) -> first != null ? first : last)));
              

              如果你想要最后一个但“地址作为字符串”(不使用identity() 作为参数)。

                Map<String, String> phoneBook = people.stream()
                        .collect(groupingBy(x -> x.name, reducing(null, x -> x.address, (first, last) -> last)));
              

              source

              因此,本质上,groupingByreducing 收集器配对后,其行为开始与 toMap 收集器非常相似,具有与其 mergeFunction 相似的功能......以及相同的最终结果......

              【讨论】:

                【解决方案9】:

                假设你有人是对象列表

                  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)
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2017-08-12
                  • 2013-03-11
                  • 2014-02-24
                  • 2012-09-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多