检查一个列表是否包含另一个列表中的元素

175
我有两个包含不同对象的列表。
List<Object1> list1;
List<Object2> list2;

我想检查list1中的元素是否基于特定属性存在于list2中(Object1和Object2有一个共同的名为attributeSame的属性,其中包括其他属性)。

目前,我是这样做的:

boolean found = false;
for(Object1 object1 : list1){
   for(Object2 object2: list2){
       if(object1.getAttributeSame() == object2.getAttributeSame()){
           found = true;
           //also do something
       }
    }
    if(!found){
        //do something
    }
    found = false;
}

但我认为有一种更好、更快的方法来做到这一点 :) 有人可以提出来吗?

谢谢!


首先,当你设置 found = true; 然后简单地 break; 或者退出循环。 - jsist
请尝试使用二分查找并更改数据结构以适应情况,以实现快速搜索。有关详细信息,请参见以下链接:https://dev59.com/5W435IYBdhLWcg3w1juV。 - jsist
除了 Object 以外,它们是否有共同的父类? - Woot4Moo
@Woot4Moo 不,他们不这样做。 - Ned
11个回答

311

如果您只需要测试基本的相等性,那么可以在不修改输入列表的情况下使用基本的JDK来完成这个操作。

!Collections.disjoint(list1, list2);

如果需要测试特定的属性,那就比较困难。我会默认建议使用

list1.stream()
   .map(Object1::getProperty)
   .anyMatch(
     list2.stream()
       .map(Object2::getProperty)
       .collect(toSet())
       ::contains)

该方法收集了list2中的不同值,并测试list1中每个值是否存在。


1
这不会一直返回 false 吗,因为它们是两个不同的对象? - Venki
2
嗯,不是吧?Disjoint测试两个集合之间是否存在没有相等的对象equals()。 - Louis Wasserman
15
请注意,对于列表而言,时间复杂度为 O(n*m);如果您愿意在比较前将 list1 复制到一个 Set 中,则可以获得 O(n) + O(m),即 O(n+m) 的时间复杂度,但需要一些额外的内存。这是在速度和内存之间进行选择的问题。 - Haroldo_OK
1
这只适用于 "List<Person> list1; List<Person> list2",但不适用于两个不同的对象或数据类型,例如 List<Person>list1; List<Employee>list2。 - whoami - fakeFaceTrueSoul
当然,这对于@Zephyr提出的那些情况是行不通的。但对于所提出的问题,只要你实现了正确的等式,它就完美地工作了。这才是最重要的! - Syed Siraj Uddin

94

为了简化Narendra的逻辑,您可以使用以下方法:

boolean var = lis1.stream().anyMatch(element -> list2.contains(element));

6
这个答案并未得到足够的赞赏。 - Kervvv
26
如果你想的话,可以再缩短一点:list1.stream().anyMatch(list2::contains); - tarka
如何在contains中添加ignorecase - OT_DEV

58
你可以使用Apache Commons CollectionUtils来实现:
if(CollectionUtils.containsAny(list1,list2)) {  
    // do whatever you want
} else { 
    // do other thing 
}  

这假设您已正确地重载了自定义对象的等于功能。


11
已经过去了4年,我还明确地调用了包和函数。 - Woot4Moo
3
当存在JDK解决方案时,对Apache Commons进行负投票。 - ohcibi
19
Java也有内置的日志记录器,你可以在这方面走得更远,应该对那些建议使用Log4j和Log4j2的人进行反对投票。 - Woot4Moo
1
@Woot4Moo 这取决于情况。如果使用Log4j解决OP的问题是有道理的,那么就没有理由进行负投票。在这种情况下,像99%的建议apache commons一样,它只会成为无用的臃肿。 - ohcibi
4
如果你已经在使用Apache Commons,那么它并不算是臃肿的。这是一个很好的答案。 - vab2048
显示剩余2条评论

10

Loius的回答是正确的,我只想加上一个例子:

listOne.add("A");
listOne.add("B");
listOne.add("C");

listTwo.add("D");
listTwo.add("E");
listTwo.add("F");      

boolean noElementsInCommon = Collections.disjoint(listOne, listTwo); // true

1
我认为,即使Collections.disjoint(listOne, listTwo)返回true,如果您将元素'A'添加到第二个列表中listTwo.add("A");。 - 0190198
1
如果列表大小不相等,会怎样? - Noor Hossain
返回值: 如果两个指定的集合__没有任何共同元素__,则为true。(c)如果两个集合至少有一个共同元素> False 否则为True无论它们有多少元素。 - Ivan Trechyokas

10

有一个名为retainAllCollection方法,但对你有一些副作用reference

仅保留此列表中包含在指定集合中的元素(可选操作)。换句话说,从此列表中删除未包含在指定集合中的所有元素。

如果此列表因调用而更改,则返回true

就像这样

boolean b = list1.retainAll(list2);

2
为了让它更快,你可以添加一个break;这样,如果found设置为true,循环就会停止:
boolean found = false;
for(Object1 object1 : list1){
   for(Object2 object2: list2){
       if(object1.getAttributeSame() == object2.getAttributeSame()){
           found = true;
           //also do something  
           break;
       }
    }
    if(!found){
        //do something
    }
    found = false;
}

如果你有一个以attributeSame为键的地图,而不是列表,那么你可以更快地检查一个地图中是否有一个对应值或没有一个对应值。


嗨Tom,谢谢你注意到了!是的,我在打字时忘记了“break”。但我在想也许有一些算法,或者我应该将这些列表更改为其他集合。 - Ned
难道没有比O(n*m)更好的算法吗? - Woot4Moo
.getAttributeSame()? - Maveňツ
Object1和Object2的getAttributeSame()方法实现未提供,但对于问题和答案也不相关;它只返回一个属性(attributeSame,Long类型),这两个类都有。 - Tom

2
根据 .contains(Object obj) 的JavaDoc:

如果此列表包含指定的元素,则返回true。更正式地说,当且仅当此列表包含至少一个元素e使得(o == null? E == null:o.equals(e))时,返回true。

因此,如果您为给定对象覆盖了.equals()方法,则应该能够执行:if(list1.contains(object2))... 如果元素将是唯一的(即具有不同的属性),则可以重写.equals().hashcode()并将所有内容存储在HashSets中。这将允许您在常数时间内检查一个元素是否包含另一个元素。

2

更快的方法需要额外的空间。

例如:

  1. 将一个列表中的所有项放入哈希集中(您必须自己实现哈希函数以使用object.getAttributeSame())

  2. 遍历另一个列表,检查是否有任何项在哈希集中。

这样每个对象最多只被访问一次。而HashSet足够快,可以在O(1)时间内检查或插入任何对象。


0

org.springframework.util.CollectionUtils

boolean containsAny(java.util.Collection<?> source, java.util.Collection<?> candidates)

Return true if any element in 'candidates' is contained in 'source'; otherwise returns false

0

你能定义一下你所持有的数据类型吗?它是大数据吗?它是否已排序? 我认为你需要考虑不同的效率方法,这取决于数据。

例如,如果你的数据很大且未排序,你可以尝试通过索引迭代两个列表,并将每个列表属性存储在另一个列表助手中。 然后,你可以通过助手列表中的当前属性进行交叉检查。

祝好运

编辑:我不建议过度使用equals。这是危险的,可能违反你的对象oop意义。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接