【问题标题】:Common elements in two lists两个列表中的共同元素
【发布时间】:2011-08-22 01:09:51
【问题描述】:

我有两个 ArrayList 对象,每个对象都包含三个整数。我想找到一种方法来返回两个列表的共同元素。有谁知道我怎么能做到这一点?

【问题讨论】:

    标签: java arraylist element


    【解决方案1】:

    问题涉及三个项目,许多建议建议使用retainAll。我认为必须说明的是,随着列表大小的增加,retainAll 似乎变得越来越低效。

    在我的测试中,我发现转换为 Sets 和循环比使用 retainAll 处理包含 1000 个项目的列表快大约 60 倍

      List<Integer> common(List<Integer> biggerList, List<Integer> smallerList) {
        Set<Integer> set1 = new HashSet<>(biggerList);
        List<Integer> result = new ArrayList<>(smallerList.size());
        for (Integer i : smallerList) {
          if (set1.contains(i)) {
            result.add(i);
          }
        }
        return result;
      }
    

    【讨论】:

      【解决方案2】:

      List<String> lista =new ArrayList<String>();
      List<String> listb =new ArrayList<String>();
      
      lista.add("Isabella");
      lista.add("Angelina");
      lista.add("Pille");
      lista.add("Hazem");
      
      listb.add("Isabella");
      listb.add("Angelina");
      listb.add("Bianca");
      
      // Create an aplusb list which will contain both list (list1 and list2) in which common element will occur twice 
      List<String> listapluslistb =new ArrayList<String>(lista);    
      listapluslistb.addAll(listb);
                      
      // Create an aunionb set which will contain both list (list1 and list2) in which common element will occur once
      Set<String> listaunionlistb =new HashSet<String>(lista);
      listaunionlistb.addAll(listb);
                      
      for(String s:listaunionlistb)
      {
          listapluslistb.remove(s);
      }
      System.out.println(listapluslistb);
      

      【讨论】:

      • 虽然此代码可以回答问题,但提供有关 如何 和/或 为什么 解决问题的附加上下文将改善答案的长期价值。
      【解决方案3】:

      结合使用 Java 8 的 Stream.filter() 方法和 List.contains()

      import static java.util.Arrays.asList;
      import static java.util.stream.Collectors.toList;
      
      /* ... */
      
      List<Integer> list1 = asList(1, 2, 3, 4, 5);
      List<Integer> list2 = asList(1, 3, 5, 7, 9);
          
      List<Integer> common = list1.stream().filter(list2::contains).collect(toList());
      

      【讨论】:

      • 包含看起来像是一个 O(n) 操作,它会被调用 n 次,除非编译器做了一些聪明的事情。有谁知道以上是线性时间还是二次时间?
      • 这将是一个 n*n 操作!
      • 读者注意:在 Java 16+ 中,将 .collect( Collectors.toList() ) 替换为更短的 .toList()Stream 上的新方法)。
      【解决方案4】:
      public static <T> List<T> getCommonElements(
                  java.util.Collection<T> a,
                  java.util.Collection<T> b
                  ) {
              if(a==null && b==null) return new ArrayList<>();
              if(a!=null && a.size()==0) return new ArrayList<>(b);           
              if(b!=null && b.size()==0) return new ArrayList<>(a);
              
              Set<T> set= a instanceof HashSet?(HashSet<T>)a:new HashSet<>(a);
              return b.stream().filter(set::contains).collect(Collectors.toList());
          }
      

      为了更好的时间性能,请使用 HashSet (O(1) 查找) 而不是 List(O(n) 查找)

      时间复杂度- O(b) 空间复杂度 - O(a)

      【讨论】:

        【解决方案5】:

        以下代码 移除列表中的常用元素

        List<String> result =  list1.stream().filter(item-> !list2.contains(item)).collect(Collectors.toList());
        

        检索公共元素

        List<String> result = list1.stream()
                        .distinct()
                        .filter(list::contains)
                        .collect(Collectors.toList());
        

        【讨论】:

        • 这会有什么复杂性?效率高吗?
        【解决方案6】:

        您可以对 ArrayList 对象使用集合交集操作。

        类似这样的:

        List<Integer> l1 = new ArrayList<Integer>();
        
        l1.add(1);
        l1.add(2);
        l1.add(3);
        
        List<Integer> l2= new ArrayList<Integer>();
        l2.add(4);
        l2.add(2);
        l2.add(3);
        
        System.out.println("l1 == "+l1);
        System.out.println("l2 == "+l2);
        
        List<Integer> l3 = new ArrayList<Integer>(l2);
        l3.retainAll(l1);
        
            System.out.println("l3 == "+l3);
        

        现在,l3 应该只有 l1l2 之间的共同元素。

        CONSOLE OUTPUT
        l1 == [1, 2, 3]
        l2 == [4, 2, 3]
        l3 == [2, 3]
        

        【讨论】:

        • 请注意,这样更改也会反映在l2 中。你可能是想说List&lt;Integer&gt; l3 = new ArrayList&lt;Integer&gt;(l2);
        • 如果说 l1 有 2 个元素而 l2 有 3 个相同的元素,问题会变得有点混乱。即使它在 l1 中只包含两次,retainAll 返回也将 3 放入 l3。
        【解决方案7】:

        考虑两个列表 L1 和 L2

        使用Java8我们可以很容易地找到它

        L1.stream().filter(L2::contains).collect(Collectors.toList())

        【讨论】:

          【解决方案8】:

          上面的一些答案相似但不相同,因此将其发布为新答案。

          解决方案:
          1. 使用HashSet来保存需要被移除的元素
          2.将list1的所有元素加入HashSet
          3. 迭代 list2 并从 HashSet 中删除 list2 ==> 中存在的元素,这些元素同时存在于 list1 和 list2
          4.现在遍历HashSet并从list1中删除元素(因为我们已经将list1的所有元素都添加到了set中),最后,list1具有所有公共元素
          注意:我们可以添加 list2 的所有元素,并且在第三次迭代中,我们应该从 list2 中删除元素。

          时间复杂度:O(n)
          空间复杂度:O(n)

          代码:

          import com.sun.tools.javac.util.Assert;
          import org.apache.commons.collections4.CollectionUtils;
          
              List<Integer> list1 = new ArrayList<>();
              list1.add(1);
              list1.add(2);
              list1.add(3);
              list1.add(4);
              list1.add(5);
          
              List<Integer> list2 = new ArrayList<>();
              list2.add(1);
              list2.add(3);
              list2.add(5);
              list2.add(7);
              Set<Integer> toBeRemoveFromList1 = new HashSet<>(list1);
              System.out.println("list1:" + list1);
              System.out.println("list2:" + list2);
              for (Integer n : list2) {
                  if (toBeRemoveFromList1.contains(n)) {
                      toBeRemoveFromList1.remove(n);
                  }
              }
              System.out.println("toBeRemoveFromList1:" + toBeRemoveFromList1);
              for (Integer n : toBeRemoveFromList1) {
                  list1.remove(n);
              }
              System.out.println("list1:" + list1);
              System.out.println("collectionUtils:" + CollectionUtils.intersection(list1, list2));
              Assert.check(CollectionUtils.intersection(list1, list2).containsAll(list1));
          

          输出:

          list1:[1, 2, 3, 4, 5]
          list2:[1, 3, 5, 7]
          toBeRemoveFromList1:[2, 4]
          list1:[1, 3, 5]
          collectionUtils:[1, 3, 5]
          

          【讨论】:

            【解决方案9】:

            为什么要重新发明轮子?使用Commons Collections:

            CollectionUtils.intersection(java.util.Collection a, java.util.Collection b)
            

            【讨论】:

            • 这是一个很好的解决方案,但是正如我上面提到的,它在重复元素上的​​行为与 retainAll() 不同。很可能一个是正确的,一个是不正确的,这取决于你解决问题的方式。
            【解决方案10】:
            public <T> List<T> getIntersectOfCollections(Collection<T> first, Collection<T> second) {
                    return first.stream()
                            .filter(second::contains)
                            .collect(Collectors.toList());
                }
            

            【讨论】:

              【解决方案11】:

              你可以使用方法获取两个列表之间的共同元素 “保留所有”。此方法将从列表中删除所有不匹配的元素以 它适用。

              Ex.: list.retainAll(list1);
              

              在这种情况下,从列表中,所有不在 list1 中的元素将被 删除,只剩下那些在列表和 list1.

              List<Integer> list = new ArrayList<>();
              list.add(10);
              list.add(13);
              list.add(12);
              list.add(11);
              
              List<Integer> list1 = new ArrayList<>();
              list1.add(10);
              list1.add(113);
              list1.add(112);
              list1.add(111);
              //before retainAll
              System.out.println(list);
              System.out.println(list1);
              //applying retainAll on list
              list.retainAll(list1);
              //After retainAll
              System.out.println("list::"+list);
              System.out.println("list1::"+list1);
              

              输出:

              [10, 13, 12, 11]
              [10, 113, 112, 111]
              list::[10]
              list1::[10, 113, 112, 111]
              

              注意:在列表上应用retainAll后,列表包含之间的公共元素 列表和列表1。

              【讨论】:

              • 是交集我们删除所有公共并保留列表1中列表中唯一的方法吗?
              【解决方案12】:
                  // Create two collections:
                  LinkedList<String> listA =  new LinkedList<String>();
                  ArrayList<String> listB =  new ArrayList<String>();
              
                  // Add some elements to listA:
                  listA.add("A");
                  listA.add("B");
                  listA.add("C");
                  listA.add("D");
              
                  // Add some elements to listB:
                  listB.add("A");
                  listB.add("B");
                  listB.add("C");
              
                  // use 
              
                  List<String> common = new ArrayList<String>(listA);
                  // use common.retainAll
              
                  common.retainAll(listB);
              
                  System.out.println("The common collection is : " + common);
              

              【讨论】:

                【解决方案13】:

                如果你想自己做的话..

                List<Integer> commons = new ArrayList<Integer>();
                
                for (Integer igr : group1) {
                    if (group2.contains(igr)) {
                        commons.add(igr);
                    }
                }
                
                System.out.println("Common elements are :: -");
                for (Integer igr : commons) {
                    System.out.println(" "+igr);
                }
                

                【讨论】:

                • OP 要求找到一种方法来查找哪些元素是共同的,而不是有多少共同元素。
                • @BrendonDugan - 这就是这段代码的作用。列表commons 包含公共元素。第二个 for 循环将它们打印在控制台上。我没有看到代码在哪里计算公共元素。
                • @AjoyBhatia - 当我发表评论时(2013 年),代码只返回了一些常见元素。
                • @BrendonDugan 哦,好的。对于那个很抱歉。我应该记住,答案可以就地编辑,但 cmets 通常按时间顺序保持原样:-)
                【解决方案14】:

                使用Collection#retainAll()

                listA.retainAll(listB);
                // listA now contains only the elements which are also contained in listB.
                

                如果您想避免listA 中的更改受到影响,那么您需要创建一个新的。

                List<Integer> common = new ArrayList<Integer>(listA);
                common.retainAll(listB);
                // common now contains only the elements which are contained in listA and listB.
                

                【讨论】:

                • RetainAll 返回一个新列表?我试图将保留的输出存储到一个新的列表中,比如 tempList.addAll(listA.retainAll(listB));但它不起作用
                • 正如Collection#retainAll() 后面的链接和代码 sn-ps 中的 cmets 所回答的那样,不,它没有。更改会反映在您调用该方法的列表中。
                • 问题是 list common 被初始化为 3,然后你尝试通过只返回一个或两个元素来改变它的大小。我尝试了您的建议,但它使我超出范围异常。
                • 在这种方法中,我将无法匹配元素的出现次数......例如 listA {2,3,5} 和 listB {5 5} ,如果我将执行 listB.retainAll(listA) ,listB 现在将具有 {5,5} ......我想要将 listA 和 listB 比较为 {5} 后的最终结果。请建议我们如何实现这一目标
                • 如果集合对象选择不当,可能会抛出 UnsupportedOperationException。上面的 ArrayList 示例当然可以工作。
                猜你喜欢
                • 1970-01-01
                • 2021-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2012-12-07
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多