Determine if 2 lists with similar objects contain a partial duplicate I have two lists of Person objects. I need to compare the lists to determine if items in list1 exist in list2 and vice versa.

This is not a straight equals() scenario as the objects are not the same, but may have identical field values. It is those values that I need to compare.

Currently, I am using Java 8's Streams API to return either a match or null:

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

public class Main {

    public static void main(String[] args) {

        ArrayList&lt;Person&gt; originalPeople = new ArrayList&lt;&gt;();
        ArrayList&lt;Person&gt; newPeople = new ArrayList&lt;&gt;();

        originalPeople.add(new Person("William", "Tyndale"));
        originalPeople.add(new Person("Jonathan", "Edwards"));
        originalPeople.add(new Person("Martin", "Luther"));

        newPeople.add(new Person("Jonathan", "Edwards"));
        newPeople.add(new Person("James", "Tyndale"));
        newPeople.add(new Person("Roger", "Moore"));

        // Create a list of people that no longer exist in the new list
        for (Person original : originalPeople) {
            if (getPersonInList(
                    newPeople, original.getFirstName(), original.getLastName()) == null) {
                System.out.printf("%s %s is not in the new list!%n",
                        original.getFirstName(), original.getLastName());
            }
        }
    }

    private static Person getPersonInList(
            final List&lt;Person&gt; list, final String firstName, final String lastName) {

        return list.stream()
                .filter(t -&gt; t.getFirstName().equalsIgnoreCase(firstName))
                .filter(t -&gt; t.getLastName().equalsIgnoreCase(lastName))
                .findFirst().orElse(null);
    }
}

class Person {

    private final String firstName;
    private final String lastName;

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

    String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }
}


In my real-world application, however, both lists have over 30,000 items and this is very time-consuming.

Is there a better way to perform this comparison? I would want this to work both ways as well, so if there are items in newPeople that were not in originalPeople, I would want that list as well.
There are certain reasons I would not prefer your approach. These are:

1) There are multiple method calls which reduces the readability.

2) You are using filter twice which decreases the performances. You could do it inside the same filter like I've shown below

3) With this approach you don't have a consolidated List which contains common elements. For that you need another else condition which increases the cyclomatic complexity.

4) A null check which could produce NPE if not handled carefully as you are using findFirst() which returns Optional (Although null can be replaced with default new Person("","") object which is again not recommended).

5) Finally don't use ArrayList&lt;Person&gt; originalPeople = new ArrayList&lt;&gt;();; rather declare as List, to follow programming to interface.

Rather I would use below approach:

private static List&lt;Person&gt; getPersonInList(
            final List&lt;Person&gt; newPeople, List&lt;Person&gt; originalPeople) {
        List&lt;Person&gt; list = new ArrayList&lt;&gt;();

        newPeople.forEach(p -&gt;
                originalPeople.stream()
                              .filter(p1 -&gt; p.getFirstName().equals(p1.getFirstName()) &amp;&amp; 
                                      p.getLastName().equals(p1.getLastName()))
                              .forEach(list::add));
        return list;
    }