Assuming the expected output is aaa, ccc, eee, fff, xxx (all the not-common items), you can use List#removeAll, but you need to use it twice to get both the items in name but not in name2 AND the items in name2 and not in name:

List<String> list = new ArrayList<> (name);
list.removeAll(name2); //list contains items only in name

List<String> list2 = new ArrayList<> (name2);
list2.removeAll(name); //list2 contains items only in name2

list2.addAll(list); //list2 now contains all the not-common items

As per your edit, you can't use remove or removeAll - in that case you can simply run two loops:

List<String> uncommon = new ArrayList<> ();
for (String s : name) {
    if (!name2.contains(s)) uncommon.add(s);
}
for (String s : name2) {
    if (!name.contains(s)) uncommon.add(s);
}
Answer from assylias on Stack Overflow
🌐
Benchresources
benchresources.net › home › java › java 8 – how to find common & uncommon elements from 2 lists ?
Java 8 - How to find common & uncommon elements from 2 Lists ? - BenchResources.Net
July 27, 2023 - package in.bench.resources.common.elements; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; public class FindUncommonElementsFrom2ListUsingJava8Stream { public static void main(String[] args) { // first & second List List<String> nameList1 = Arrays.asList("Ram", "Sam", "Sundar", "Shaz", "Ankit"); List<String> nameList2 = Arrays.asList("Ram", "Sam", "Shaz"); // find uncommon elements & store in new List List<String> uncommonNames = nameList1 .stream() .filter(name -> !nameList2.contains(name)) .collect(Collectors.toList()); // print to console System.out.print(uncommonNames); } }
🌐
Roy Tutorials
roytuts.com › home › java › java to find common, uncommon, unique elements in arraylists
Java To Find Common, Uncommon, Unique Elements In ArrayLists - Roy Tutorials
April 4, 2024 - Common elements in List One and Two: [[name=Roy Tutorials, email=Roy-Tutorials@email.com]] Uncommon elements in List One: [[name=Soumitra, email=Soumitra@email.com], [name=Michael, email=Michael@email.com], [name=John, email=John@email.com], [name=Roytuts, email=Roytuts@email.com]] Uncommon elements in List Two: [[name=Jerome, email=Jerome@email.com], [name=Ford, email=Ford@email.com], [name=Microservices, email=Microservices@email.com]] Unique elements in List One and List Two: [[name=Michael, email=Michael@email.com], [name=John, email=John@email.com], [name=Roy Tutorials, email=Roy-Tutorials@email.com], [name=Ford, email=Ford@email.com], [name=Microservices, email=Microservices@email.com], [name=Jerome, email=Jerome@email.com], [name=Soumitra, email=Soumitra@email.com], [name=Roytuts, email=Roytuts@email.com]]
Top answer
1 of 2
4

You could do something like this:

public static void main(String[] args) {
    List<Foo> fooList = asList(new Foo("1", "1"), new Foo("2", "2"), new Foo("3", "3"));
    List<Bar> barList = asList(new Bar("4", 4), new Bar("3", 3), new Bar("5", 5));

    Set<Blah> fooSet = fooList.stream().map(Main::fooToBlah).collect(toCollection(HashSet::new));
    Set<Blah> barSet = barList.stream().map(Main::barToBlah).collect(toCollection(HashSet::new));

    barList.stream()
            .filter(bar -> !fooSet.contains(barToBlah(bar)))
            .forEach(System.out::println);
    fooList.stream()
            .filter(foo -> !barSet.contains(fooToBlah(foo)))
            .forEach(System.out::println);
}

static Blah fooToBlah(Foo foo) {
    return new Blah(foo.name, foo.age);
}

static Blah barToBlah(Bar bar) {
    return new Blah(bar.barName, "" + bar.codeBar);
}

static class Blah {
    String name;
    String age;

    public Blah(String name, String age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public boolean equals(Object o) {
        ...
    }

    @Override
    public int hashCode() {
        ...
    }
}
  1. Take all Foo objects and convert them to Blah (name it appropriately of course) - this is needed because Foo has other fields that we don't care about.
  2. Put Blah objects into a HashSet so that you don't end up with O(n*m) time complexity. You could do just collect(toSet()), but I prefer to be explicit about it here as it is important for performance.
  3. Go over Bar objects and if an object is not in the above set, that's the uncommon Bar object. It needs to be converted to Blah and unfortunately you cannot use Stream.map as in the end it should still be Stream<Bar>, not Stream<Blah>.
  4. Repeat above three steps for the other list to find all uncommon Foo objects.

Remember that equals and hashCode methods are needed on Blah class. Foo and Bar classes don't need them.


EDIT:

You can convert the following code

Set<Blah> fooSet = fooList.stream().map(Main::fooToBlah).collect(toCollection(HashSet::new));

barList.stream()
        .filter(bar -> !fooSet.contains(barToBlah(bar)))
        .forEach(System.out::println);

into something like this

barList.stream()
        .filter(bar -> fooList.stream()
                .map(Main::fooToBlah)
                .noneMatch(foo -> foo.equals(barToBlah(bar)))
        )
        .forEach(System.out::println);

or even remove Blah class completely

barList.stream()
        .filter(bar -> fooList.stream().noneMatch(foo -> Objects.equals(foo.name, bar.barName) && Objects.equals(foo.age, "" + bar.codeBar)
        .forEach(System.out::println);

but you will end up with much worse time complexity and then there is also a question of readability.

2 of 2
2

A naive solution is to loop through both lists. Assuming you can formulate your equality condition as BiPredicate<Foo, Bar>:

for (Bar bar: bars) {
    boolean foundEqualFoo = false;
    for(Foo foo: foos) {
        if (predicate.test(foo, bar)) {
            foundEqualFoo = true;
            break;
        }
    }
    if (!foundEqualFoo) {
        barsNotInFoos.add(bar);
    }
}

That's O(n*m) time complexity.

A better solution would be to define a key class like FooBarKey which would hold name and age and implement equals(...) and hashCode() correctly. You could then do the following:

Map<FooBarKey, List<Bar>> barsByKey = bars
    .stream()
    .collect(Collectors.groupingBy(
        FooBarKey::ofBar,
        HashMap::new,
        Collectors.toList()));

Set<FooBarKey> fooKeys = foos
    .stream()
    .map(FooBarKey::ofFoo)
    .collect(Collectors.toCollection(HashSet::new));

barsByKey.keySet().removeAll(fooKeys);

Set<Bar> uncommonBars = barsByKey
    .values()
    .stream()
    .flatMap(Collection::stream)
    .collect(Collectors.toSet());

Few notes on the solution:

  • I think creating Map<FooBarKey, ...> is reasonable here to avoid constantly creating new FooBarKey instances for the same Bars.
  • A multi-map Map<FooBarKey, List<Bar>> is needed since there may be bars with the same key in the list.
  • HashMap::new and HashSet::new suppliers are used where applicable to make sure hash tables are used to guarantee O(1) on corresponding operations.

That should be O(2*n + m).

🌐
Baeldung
baeldung.com › home › java › java list › finding the differences between two lists in java
Finding the Differences Between Two Lists in Java | Baeldung
April 29, 2026 - We can create a copy of one list and then remove all the elements common with the other using the List method removeAll():
Top answer
1 of 2
9

These feel like set operations to me: union, overlap, and difference.

Have a look at this:

Classical set operations for java.util.Collection

Works perfectly for me. Here's the code:

import java.util.ArrayList;
import java.util.List;

/**
 * Add something descriptive here.
 * User: mduffy
 * Date: 3/26/2015
 * Time: 1:27 PM
 * @link https://stackoverflow.com/questions/29284061/find-the-uncommon-common-all-elements-from-two-different-array-list-objects-in/29284162?noredirect=1#comment46767251_29284162
 */
public class SetOperationDemo {

    public static void main(String[] args) {
        List<String> setOne = new ArrayList<String>() {{
            add("A");
            add("B");
            add("C");
            add("D");
            add("E");
        }};
        List<String> setTwo = new ArrayList<String>() {{
            add("D");
            add("E");
            add("F");
            add("G");
        }};

        System.out.println("Set A           : " + setOne);
        System.out.println("Set B           : " + setTwo);
        List<String> base = new ArrayList<String>(setOne);
        base.retainAll(setTwo);
        System.out.println("Intersection A+B: " + base);
        base = new ArrayList<String>(setOne);
        base.removeAll(setTwo);
        System.out.println("Subtraction  A-B: " + base);
        base = new ArrayList<String>(setTwo);
        base.removeAll(setOne);
        System.out.println("Subtraction  B-A: " + base);
        base = new ArrayList<String>(setOne);
        base.addAll(setTwo);
        System.out.println("Union A union B : " + base);
    }
}

Here's the output:

Set A           : [A, B, C, D, E]
Set B           : [D, E, F, G]
Intersection A+B: [D, E]
Subtraction  A-B: [A, B, C]
Subtraction  B-A: [F, G]
Union A union B : [A, B, C, D, E, D, E, F, G]

Process finished with exit code 0

If your Lists contain custom classes, you have to be sure that they override equals and hashCode properly or they won't give the expected behavior. Here's my code using a custom class that shows how it's done.

import java.util.ArrayList;
import java.util.List;

/**
 * Add something descriptive here.
 * User: mduffy
 * Date: 3/26/2015
 * Time: 1:27 PM
 * @link https://stackoverflow.com/questions/29284061/find-the-uncommon-common-all-elements-from-two-different-array-list-objects-in/29284162?noredirect=1#comment46767251_29284162
 */
public class SetOperationDemo {

    public static void main(String[] args) {
        List<DemoPerson> setOne = new ArrayList<DemoPerson>() {{
            add(new DemoPerson("Andy", "A"));
            add(new DemoPerson("Bob", "B"));
            add(new DemoPerson("Carl", "C"));
            add(new DemoPerson("David", "D"));
            add(new DemoPerson("Ernie", "E"));
        }};
        List<DemoPerson> setTwo = new ArrayList<DemoPerson>() {{
            add(new DemoPerson("David", "D"));
            add(new DemoPerson("Ernie", "E"));
            add(new DemoPerson("Frank", "F"));
            add(new DemoPerson("Gary", "G"));
        }};

        System.out.println("Set A           : " + setOne);
        System.out.println("Set B           : " + setTwo);
        List<DemoPerson> base = new ArrayList<DemoPerson>(setOne);
        base.retainAll(setTwo);
        System.out.println("Intersection A+B: " + base);
        base = new ArrayList<DemoPerson>(setOne);
        base.removeAll(setTwo);
        System.out.println("Subtraction  A-B: " + base);
        base = new ArrayList<DemoPerson>(setTwo);
        base.removeAll(setOne);
        System.out.println("Subtraction  B-A: " + base);
        base = new ArrayList<DemoPerson>(setOne);
        base.addAll(setTwo);
        System.out.println("Union A union B : " + base);
    }
}

class DemoPerson {
    private final String firstName;
    private final String lastName;

    public DemoPerson(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        DemoPerson that = (DemoPerson) o;

        return !(firstName != null ? !firstName.equals(that.firstName) : that.firstName != null) && !(lastName != null ? !lastName.equals(that.lastName) : that.lastName != null);

    }

    @Override
    public int hashCode() {
        int result = firstName != null ? firstName.hashCode() : 0;
        result = 31 * result + (lastName != null ? lastName.hashCode() : 0);
        return result;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("{");
        sb.append("'").append(firstName).append('\'');
        sb.append(" '").append(lastName).append('\'');
        sb.append('}');
        return sb.toString();
    }
}

And here's the output, still behaving as it should:

Set A           : [{'Andy' 'A'}, {'Bob' 'B'}, {'Carl' 'C'}, {'David' 'D'}, {'Ernie' 'E'}]
Set B           : [{'David' 'D'}, {'Ernie' 'E'}, {'Frank' 'F'}, {'Gary' 'G'}]
Intersection A+B: [{'David' 'D'}, {'Ernie' 'E'}]
Subtraction  A-B: [{'Andy' 'A'}, {'Bob' 'B'}, {'Carl' 'C'}]
Subtraction  B-A: [{'Frank' 'F'}, {'Gary' 'G'}]
Union A union B : [{'Andy' 'A'}, {'Bob' 'B'}, {'Carl' 'C'}, {'David' 'D'}, {'Ernie' 'E'}, {'David' 'D'}, {'Ernie' 'E'}, {'Frank' 'F'}, {'Gary' 'G'}]

Process finished with exit code 0
2 of 2
0

I have not tested this code but you can try this:

for(CustomStation user1 : localStationLists) 
{
    boolean flag = false;
    for(CustomStation user2 : serverStationLists) 
    {
        if(user1.getStationId().equals(user2.getStationId())) 
        {
            if(!user1.getTitle().equals(user2.getTitle())) 
            {
                resultList.add(user1);
            }            
            resultList.add(user2);
            flag = true;
            break;
        }
    }

    if(flag == false)
    {
        resultList1.add(user1);
    }
}
Top answer
1 of 3
2

If you want to ignore the requirement of using a Set, you could use Java 8 Streams to solve the problem.

public static int numInCommon(List<Integer> list1, List<Integer> list2) {
    return (int) list1.stream()
        .distinct()
        .filter(i -> list2.contains(i))
        .count()
}

This is a four-step process:

  • Get a stream of list1 (if you are not used to streams you can think of this as a loop, but in reality a call to .stream() by itself is not looping)
  • Use .distinct() to ignore duplicate elements (that is, if the first list contains a number multiple times, it only processes it once. The .distinct() part uses a Set internally to keep track of which elements it has seen.
  • Use .filter to only keep the numbers that also occur in the second list.
  • .count() to count the number of seen elements. This returns a long which is why the entire result is typecast to an int here.

It's not until you call .count() that the loop is actually performed, this is because .count() is a "terminal operation".

I would recommend to read more about Streams on Oracle's website

2 of 3
5

I personally would shorten this greatly:

public static int numInCommon(List<Integer> list1, List<Integer> list2){
    if(list1.isEmpty() || list2.isEmpty()){
        return 0;
    }

    Set<Integer> setCommon = new HashSet<>(list1);
    setCommon.retainAll(list2);
    return setCommon.size();
}

Returning 0 at the start is good, but no need for the else block on the If code. Next task is to calculate the common elements - putting list1 into the set removes duplicates and then retainAll removes anything that isn't in list2. This leaves everythin in list1 and list2 without duplicates. Returning the size() counts the elements.

For performance - I'm taking the major assumption that HashSet is one of the quickest ways to check if an item exists and handle removal.

🌐
Javaprogramto
javaprogramto.com › 2019 › 05 › find-unique-and-duplicates-values-from-two-lists.html
Find Unique and Duplicates Values From Two Lists JavaProgramTo.com
contains(Object o) method is present in Java.util.List package. This method returns true if the passed object is present in the list and otherwise it returns false. This method checks for the occurrence.
Find elsewhere
Top answer
1 of 1
1

It depends whether you're concerned about memory/time or both of them.

  1. You're concerned about memory

    Go with the brute force N2 solution because it uses no extra memory at all(only the memory required to keep the common elements), and N2 in practice is not that bad for small size lists. Anyway, fetching the phone numbers from database is more likely to cause a bigger performance hit than your N2 algorithm.

  2. You're concerned about time but you can use extra memory

    Wrap your phone numbers into a wrapper class, and add all the phone numbers from the 1st list to a HashSet. Then, iterate over your 2nd list and check if set is containing the wrapped phone number or not. This will guarantee you a O(m+k*n) time ( you have to iterate each list just once , and HastSet's contains method is a constant k - where k represents the average number of Strings having the same hashCode.) . This tends to O(n) because 2 and k(should be 1 because String's hashSet() collisions are quite rare) are constant factors which can be discarded.

    class PhoneNumber { 
    
         private final String val;
    
         public PhoneNumber(final String val){
             this.val = val;
         }
    
         @Override
         public int hashCode(){
             return this.getVal().hashCode();
         }
    
         @Override
         public boolean equals(Object obj){
             if (this == obj) {
               return true;
             }
             if (obj == null) {
               return false;
             }
             if (this.getClass() != obj.getClass()) {
               return false;
             }
    
             PhoneNumber other = (PhoneNumber )obj;
             return PhoneNumberUtils.compare(this.getVal(), other.getVal()) == 0;
         }
    
         public String getVal(){
            return this.val;
         }
    } 
    
    private Set<PhoneNumber> getCommonPhoneNumbers(List<String> dbPhoneNumbers , List<String> userPhoneNumbers){
        Set<PhoneNumber> common = new HashSet<PhoneNumber>();
    
        Set<PhoneNumber> phoneNumbers = new HashSet<PhoneNumber>();
        for(String s : userPhoneNumbers){
            phoneNumbers.add(new PhoneNumber(s));
        }
    
        for(String s : dbPhoneNumbers ){
            PhoneNumber phoneNo = new PhoneNumber(s);
            if(phoneNumbers.contains(phoneNo)){
                common.add(phoneNo);
            }
        }
    
        return common;
    }
    
  3. You're concerned about both the memory and the time complexity.

    Sort the 2 lists by using a custom comparator based on PhoneNumberUtils.compare(String,String) (this should take O(n log n)) and then iterate over both of the lists at once ( O(min(m,n)) ):

      private static final Comparator<String> phoneNoComp = new Comparator<String>(){
          @Override
          public int compare(final String s1, final String s2) {
            return PhoneNumberUtils.compare(s1,s2);
          }
      };
    
      private Set<String> getCommonPhoneNumbers(final List<String> list1 , final List<String> list2){
    
          Set<String> common = new HashSet<String>();
    
          Collections.sort(list1, phoneNoComp);
          Collections.sort(list2, phoneNoComp);
    
          int size1 = list1.size();
          int size2 = list2.size();
          int i = 0, j = 0;
    
          while(i < size1 && j < size2){
              int comparison = PhoneNumberUtils.compare(list1.get(i) , list2.get(j));
              if( comparison == 0) { // found a common element.
                  common.add(list1.get(i));
                  i++;
                  j++;
              }
              else if(comparison == 1){
                  j++;
              }
              else{
                  i++;
              }
          }
    
          return common;
      } 
    
🌐
CareerCup
careercup.com › question
Hiring Consulting — Gayle Laakmann McDowell
Your Custom Text Here · I can help you create or refine your hiring process. With my deep background in both software development and hiring, I have the technical and recruiting knowhow to work with (and earn the respect of) your technology, HR, and other teams
Address: Palo Alto, CA
🌐
Javaprogramto
javaprogramto.com › 2019 › 05 › find-unmatched-values-from-two-lists.html
Java Program To Find Unmatched values From Two Lists JavaProgramTo.com
November 16, 2019 - List interface has a method named "removeAll" which takes any collection implementation class (in our example it an ArrayList). removeAll() method removes from the current list all of its elements that are contained in the specified collection ...
🌐
Way2Java
way2java.com › home › remove common elements two lists java
Remove Common Elements Two Lists Java
November 4, 2015 - import java.util.*; public class UncommonElements { public static void main(String args[]) { ArrayList ... (); al1.add(10); al1.add(20); al1.add(30); al2.add(20); al2.add(30); al2.add(50); al1.removeAll(al2); System.out.println(al1); // prints only 10 } } Output screen on Remove Common Elements Two Lists Java
🌐
GeeksforGeeks
geeksforgeeks.org › java › find-common-elements-in-two-arraylists-in-java
Find common elements in two ArrayLists in Java - GeeksforGeeks
December 5, 2022 - Convert the first list into a stream using stream(). Use filter() to check whether each element exists in the second list.
🌐
Baeldung
baeldung.com › home › java › java list › intersection of two lists in java
Intersection of Two Lists in Java | Baeldung
April 4, 2025 - Like many other things, this has become much easier thanks to the introduction of streams in Java 8. Let’s create two Lists of Strings with some intersection — both having some duplicated elements:
🌐
DCode
dcode.fr › data processing › non common items
List Comparator - Difference Between 2 Sets - Online Non-Common Items
Tool to find uncommon elements between two lists to list different elements present only in the first or second list. Ideal for comparing, detecting differences and extracting unique data.