127

I have two ArrayList objects with three integers each. I want to find a way to return the common elements of the two lists. Has anybody an idea how I can achieve this?

demongolem
  • 9,474
  • 36
  • 90
  • 105
zenitis
  • 1,289
  • 2
  • 9
  • 5

15 Answers15

192

Use Collection#retainAll().

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

If you want to avoid that changes are being affected in listA, then you need to create a new one.

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

If you're a fan of streams, best what you could do is to Stream#filter() on Collection#contains() of the other list.

List<Integer> common = listA.stream().filter(listB::contains).toList();
// common now contains only the elements which are contained in listA and listB.

It's only at least twice slower.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • RetainAll returns a new list? I tried to store the output of retain to a new list sth like that tempList.addAll(listA.retainAll(listB)); but it doesnt work – zenitis May 09 '11 at 22:53
  • 1
    As answered in the link behind `Collection#retainAll()` and the comments in the code snippets, no, it doesn't. Changes get reflected in the list you're calling the method on. – BalusC May 09 '11 at 22:56
  • The problem is that list common is initialized with size 3, then you try to change its size by returning only one or two elements. I try what you suggest and it returns me out of bounds exception. – zenitis May 09 '11 at 23:05
  • In this approach, I will not be able to match occurrence number of element.......say for eg listA {2,3,5} and listB {5 5} , if I will do listB.retainAll(listA) , listB will now have {5,5}......I want my final outcome after comparing listA and listB as {5}. Kindly suggest how we can achieve this – NANCY Sep 22 '15 at 14:55
  • @NANCY The Commons Collections solution should work the way you wish, but not `retainAll()` – demongolem Mar 18 '16 at 17:44
  • 1
    If done with poor choice of collection object, it could throw an UnsupportedOperationException. The above example with ArrayList does work of course. – demongolem Mar 30 '17 at 12:46
  • Thx a lot for this – nano_nano Jan 29 '18 at 07:57
45

You can use set intersection operations with your ArrayList objects.

Something like this:

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

Now, l3 should have only common elements between l1 and l2.

CONSOLE OUTPUT
l1 == [1, 2, 3]
l2 == [4, 2, 3]
l3 == [2, 3]
UdayKiran Pulipati
  • 6,579
  • 7
  • 67
  • 92
Pablo Santa Cruz
  • 176,835
  • 32
  • 241
  • 292
  • 7
    Note that this way the changes are reflected in `l2` as well. You probably meant to say `List l3 = new ArrayList(l2);` instead. – BalusC May 09 '11 at 22:46
  • The problem gets a little messier if say l1 has 2 of an element and l2 had 3 of that same element. retainAll returns puts 3 of that element in l3 even though it is only contained twice in l1. – demongolem Nov 12 '12 at 02:19
43

Why reinvent the wheel? Use Commons Collections:

CollectionUtils.intersection(java.util.Collection a, java.util.Collection b)
dkb
  • 4,389
  • 4
  • 36
  • 54
  • 1
    This is a great solution, however as I mentioned above, it has different behavior than `retainAll()` on repeated elements. So likely one is correct and one is incorrect depending upon how you approach the problem. – demongolem Mar 18 '16 at 17:47
26

Using Java 8's Stream.filter() method in combination with 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());
Robby Cornelissen
  • 91,784
  • 22
  • 134
  • 156
  • 4
    Contains looks like it'd be an O(n) operation, which would be called n times, unless the compiler does something clever. Does anyone know whether the above runs in linear or quadratic time? – Regorsmitz Aug 12 '16 at 20:56
  • 1
    It would be a n*n operation ! – Lakshmikant Deshpande Nov 23 '17 at 11:13
  • 2
    Note to reader: In Java 16+, replace [`.collect( Collectors.toList() )`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/stream/Collectors.html#toList()) with shorter [`.toList()`](https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/util/stream/Stream.html#toList()) (a new method on `Stream`). – Basil Bourque May 19 '21 at 22:02
6

consider two list L1 ans L2

Using Java8 we can easily find it out

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

AVN
  • 143
  • 1
  • 3
  • 7
5

enter image description here

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);
Miss Chanandler Bong
  • 4,081
  • 10
  • 26
  • 36
  • While this code may answer the question, providing additional context regarding *how* and/or *why* it solves the problem would improve the answer's long-term value. – Michael Parker Jul 20 '16 at 22:11
5

You can get the common elements between two lists using the method "retainAll". This method will remove all unmatched elements from the list to which it applies.

Ex.: list.retainAll(list1);

In this case from the list, all the elements which are not in list1 will be removed and only those will be remaining which are common between list and 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);

Output:

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

NOTE: After retainAll applied on the list, the list contains common element between list and list1.

Gufran Hasan
  • 8,910
  • 7
  • 38
  • 51
4
public <T> List<T> getIntersectOfCollections(Collection<T> first, Collection<T> second) {
        return first.stream()
                .filter(second::contains)
                .collect(Collectors.toList());
    }
2
    // 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);
Ryagh
  • 31
  • 2
1

In case you want to do it yourself..

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);
}
Amit
  • 13,134
  • 17
  • 77
  • 148
  • 1
    The OP was asking for a way to find which elements were common, not how many common elements there are. – Brendon Dugan Jul 19 '13 at 19:05
  • @BrendonDugan - That is what this code does. The list `commons` contains the common elements. The second for-loop prints them on the console. I do not see where the code is counting the common elements. – Ajoy Bhatia Mar 22 '16 at 21:49
  • @AjoyBhatia - When I made my comment (in 2013) the code only returned a count of common elements. – Brendon Dugan Mar 22 '16 at 22:01
  • @BrendonDugan Oh, OK. Sorry about that. I should keep in mind that the answer can be edited in place but comments are usually left as is, in chronological order :-) – Ajoy Bhatia Mar 22 '16 at 22:06
1

Some of the answers above are similar but not the same so posting it as a new answer.

Solution:
1. Use HashSet to hold elements which need to be removed
2. Add all elements of list1 to HashSet
3. iterate list2 and remove elements from a HashSet which are present in list2 ==> which are present in both list1 and list2
4. Now iterate over HashSet and remove elements from list1(since we have added all elements of list1 to set), finally, list1 has all common elements
Note: We can add all elements of list2 and in a 3rd iteration, we should remove elements from list2.

Time complexity: O(n)
Space Complexity: O(n)

Code:

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

output:

list1:[1, 2, 3, 4, 5]
list2:[1, 3, 5, 7]
toBeRemoveFromList1:[2, 4]
list1:[1, 3, 5]
collectionUtils:[1, 3, 5]
dkb
  • 4,389
  • 4
  • 36
  • 54
1
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());
    }

For better time performance, please use HashSet (O(1) look up) instead of List(O(n) look ups)

Time complexity- O(b) Space Complexity- O(a)

0

Below code Remove common elements in the list

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

Retrieve common elements

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

The question talks about three items and many of the suggestions suggest using retainAll. I think it must be stated that as the size of the lists grown retainAll seems to become more inefficient.

In my tests I found that converting to Sets and looping is around 60 times faster than using retainAll for Lists with 1000s of items

  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;
  }
Steve Bosman
  • 2,599
  • 1
  • 25
  • 41
0

Using Java 8 below solution

list1.stream()
    .filter(list2::contains)
    .collect(Collectors
    .toList()));
Procrastinator
  • 2,526
  • 30
  • 27
  • 36