171

I have two lists with different objects in them.

List<Object1> list1;
List<Object2> list2;

I want to check if element from list1 exists in list2, based on specific attribute (Object1 and Object2 have (among others), one mutual attribute (with type Long), named attributeSame).

right now, I do it like this:

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

But I think there is a better and faster way to do this :) Can someone propose it?

Thanks!

Ned
  • 3,961
  • 8
  • 31
  • 49

11 Answers11

308

If you just need to test basic equality, this can be done with the basic JDK without modifying the input lists in the one line

!Collections.disjoint(list1, list2);

If you need to test a specific property, that's harder. I would recommend, by default,

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

...which collects the distinct values in list2 and tests each value in list1 for presence.

Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
  • 1
    Won't this always return false since both are 2 different objects? – Venki Feb 19 '14 at 15:02
  • 2
    Um, no? Disjoint tests if there are no objects equals() to each other between the two collections. – Louis Wasserman Feb 19 '14 at 16:15
  • 15
    Also, please note that, for lists, this will be O(n*m); if you're willing to copy `list1` into a `Set` before comparing, you'll get O(n) + O(m), that is, O(n+m), at the cost of some extra RAM; it's a matter of choosing between speed or memory. – Haroldo_OK Mar 01 '16 at 13:10
  • 1
    This would work only if "List list1; List list2" but not on two different objects or datatypes like Listlist1; Listlist2. – whoami - fakeFaceTrueSoul Sep 27 '18 at 19:14
  • Ofcourse this will not work for those scenarios @Zephyr , for the question asked, it works perfect as long as you have right equals implemented. that's all matters! – Syed Siraj Uddin Feb 08 '19 at 12:51
91

To shorten Narendra's logic, you can use this:

boolean var = lis1.stream().anyMatch(element -> list2.contains(element));
Zoe
  • 27,060
  • 21
  • 118
  • 148
ketanjain
  • 911
  • 6
  • 4
58

You can use Apache Commons CollectionUtils:

if(CollectionUtils.containsAny(list1,list2)) {  
    // do whatever you want
} else { 
    // do other thing 
}  

This assumes that you have properly overloaded the equals functionality for your custom objects.

beat
  • 1,857
  • 1
  • 22
  • 36
Woot4Moo
  • 23,987
  • 16
  • 94
  • 151
  • 11
    it has been 4 years and I also explicitly call out the package and function. – Woot4Moo Feb 15 '16 at 18:53
  • 3
    Downvote for apache commons when there is a jdk only solution – ohcibi Jan 23 '17 at 17:30
  • 19
    @ohcibi Java also has a built in logger, you should go downvote people that suggest to use Log4j and Log4j2 while you are at it. – Woot4Moo Jan 23 '17 at 17:46
  • 1
    @Woot4Moo it depends. There is no reason to downvote if there is a reason to use Log4j to solve the OPs problem. In this case apache commons would simply be useless bloat like in 99% of the answers that suggest apache commons. – ohcibi Jan 23 '17 at 17:50
  • 4
    @ohcibi but if you already are using Apache commons then it is not really bloat. This was a good answer. – vab2048 May 11 '19 at 11:31
  • I think right now (log4j?) developers should think twice before using third party libs. – Waverick Jan 11 '22 at 22:28
  • If a library is used that has a lot of experience behind a comparison - I would trust their dedicated time to such an approach than to just pick one from SO at times. – Ascalonian May 31 '22 at 15:21
10

There is one method of Collection named retainAll but having some side effects for you reference

Retains only the elements in this list that are contained in the specified collection (optional operation). In other words, removes from this list all of its elements that are not contained in the specified collection.

true if this list changed as a result of the call

Its like

boolean b = list1.retainAll(list2);
Harmeet Singh
  • 2,555
  • 18
  • 21
9

Loius answer is correct, I just want to add an example:

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
Matias Elorriaga
  • 8,880
  • 5
  • 40
  • 58
  • 1
    I think if you add element 'A' to second list listTwo.add("A"); even though Collections.disjoint(listOne, listTwo); returns true. – 0190198 Jun 28 '17 at 20:56
  • 1
    what if, the list.size not equal ? – Noor Hossain Oct 18 '20 at 05:46
  • Returns: true if the two specified collections __have no elements__ in common. (c) if two collection have at least 1 common element > False else True doesn't matter how much elements they have. – Ivan Trechyokas Feb 16 '23 at 16:21
2

to make it faster, you can add a break; that way the loop will stop if found is set to 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;
}

If you would have maps in stead of lists with as keys the attributeSame, you could check faster for a value in one map if there is a corresponding value in the second map or not.

dARKpRINCE
  • 1,538
  • 2
  • 13
  • 22
Tom
  • 4,096
  • 2
  • 24
  • 38
  • Hi Tom, thanks for noticing! Yes, I forgot "break" while typing. But I was thinking maybe there is some algorithm, or I should change these lists into some other collections. – Ned Aug 03 '12 at 13:13
  • isn't there anything better than O(n*m)? – Woot4Moo Jan 17 '13 at 14:02
  • .getAttributeSame() ? – Maveňツ Sep 03 '15 at 06:39
  • The implementation for the method getAttributeSame() from Object1 and Object2 are not provided, but also aren't relevant for the question and answer; it just returns an attribute (attributeSame, a Long) which both classses have. – Tom Sep 03 '15 at 07:37
2

According to the JavaDoc for the .contains(Object obj):

Returns true if this list contains the specified element. More formally, returns true if and only if this list contains at least one element e such that (o==null ? e==null : o.equals(e)).

So if you override your .equals() method for your given object, you should be able to do: if(list1.contains(object2))...

If the elements will be unique (ie. have different attributes) you could override the .equals() and .hashcode() and store everything in HashSets. This will allow you to check if one contains another element in constant time.

npinti
  • 51,780
  • 5
  • 72
  • 96
2

faster way will require additional space .

For example:

  1. put all items in one list into a HashSet ( you have to implement the hash function by yourself to use object.getAttributeSame() )

  2. Go through the other list and check if any item is in the HashSet.

In this way each object is visited at most once. and HashSet is fast enough to check or insert any object in O(1).

lavin
  • 2,276
  • 2
  • 13
  • 15
0

Can you define the type of data you hold ? is it big data ? is it sorted ? I think that you need to consider different efficiency approaches depending on the data.

For example, if your data is big and unsorted you could try and iterate the two lists together by index and store each list attribute in another list helper. then you could cross check by the current attributes in the helper lists.

good luck

edited : and I wouldn't recommend overloading equals. its dangerous and probably against your object oop meaning.

R-E-L
  • 137
  • 3
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
akjain
  • 1,787
  • 3
  • 20
  • 35
0

With java 8, we can do like below to check if one list contains any element of other list

boolean var = lis1.stream().filter(element -> list2.contains(element)).findFirst().isPresent();
Narendra Jaggi
  • 1,297
  • 11
  • 33