两个列表中的共同元素

Posted

技术标签:

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

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

【问题讨论】:

【参考方案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 示例当然可以工作。

以上是关于两个列表中的共同元素的主要内容,如果未能解决你的问题,请参考以下文章

查找列表中不常见的元素

基于共同的第一个元素合并二维列表中的元素

合并共享共同元素的列表

合并列表与共同元素

两个列表中的匹配元素算法 C++

元素明智的连接与Python中的两个列表列表