Define yourself a key object that holds and compares the desired properties. In this simple case, you may use a small list whereas each index corresponds to one property. For more complex cases, you may use a Map (using property names as keys) or a dedicated class:

Function<Person,List<Object>> toKey=p -> Arrays.asList(p.getName(), p.getAge());

Having such a mapping function. you may use the simple solution:

list1.stream().map(toKey)
     .flatMap(key -> list2.stream().map(toKey).filter(key::equals))
     .forEach(key -> System.out.println("{name="+key.get(0)+", age="+key.get(1)+"}"));

which may lead to poor performance when you have rather large lists. When you have large lists (or can’t predict their sizes), you should use an intermediate Set to accelerate the lookup (changing the task’s time complexity from O(n²) to O(n)):

list2.stream().map(toKey)
     .filter(list1.stream().map(toKey).collect(Collectors.toSet())::contains)
     .forEach(key -> System.out.println("{name="+key.get(0)+", age="+key.get(1)+"}"));

In the examples above, each match gets printed. If you are only interested in whether such a match exists, you may use either:

boolean exists=list1.stream().map(toKey)
     .anyMatch(key -> list2.stream().map(toKey).anyMatch(key::equals));

or

boolean exists=list2.stream().map(toKey)
     .anyMatch(list1.stream().map(toKey).collect(Collectors.toSet())::contains);
Answer from Holger on Stack Overflow
Top answer
1 of 8
14

Define yourself a key object that holds and compares the desired properties. In this simple case, you may use a small list whereas each index corresponds to one property. For more complex cases, you may use a Map (using property names as keys) or a dedicated class:

Function<Person,List<Object>> toKey=p -> Arrays.asList(p.getName(), p.getAge());

Having such a mapping function. you may use the simple solution:

list1.stream().map(toKey)
     .flatMap(key -> list2.stream().map(toKey).filter(key::equals))
     .forEach(key -> System.out.println("{name="+key.get(0)+", age="+key.get(1)+"}"));

which may lead to poor performance when you have rather large lists. When you have large lists (or can’t predict their sizes), you should use an intermediate Set to accelerate the lookup (changing the task’s time complexity from O(n²) to O(n)):

list2.stream().map(toKey)
     .filter(list1.stream().map(toKey).collect(Collectors.toSet())::contains)
     .forEach(key -> System.out.println("{name="+key.get(0)+", age="+key.get(1)+"}"));

In the examples above, each match gets printed. If you are only interested in whether such a match exists, you may use either:

boolean exists=list1.stream().map(toKey)
     .anyMatch(key -> list2.stream().map(toKey).anyMatch(key::equals));

or

boolean exists=list2.stream().map(toKey)
     .anyMatch(list1.stream().map(toKey).collect(Collectors.toSet())::contains);
2 of 8
7

A simple way to do that is to override equals and hashCode. Since I assume the equality between Person must also consider the id field, you can wrap this instance into a PersonWrapper which will implement the correct equals and hashCode (i.e. only check the name and age fields):

class PersonWrapper {

    private Person person;

    private PersonWrapper(Person person) {
        this.person = person;
    }

    public static PersonWrapper wrap(Person person) {
        return new PersonWrapper(person);
    }

    public Person unwrap() {
        return person;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null || getClass() != obj.getClass()) {
            return false;
        }
        PersonWrapper other = (PersonWrapper) obj;
        return person.name.equals(other.person.name) && person.age.equals(other.person.age);
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + person.name.hashCode();
        result = prime * result + person.age.hashCode();
        return result;
    }

}

With such a class, you can then have the following:

Set<PersonWrapper> set2 = list2.stream().map(PersonWrapper::wrap).collect(toSet());

boolean exists =
    list1.stream()
         .map(PersonWrapper::wrap)
         .filter(set2::contains)
         .findFirst()
         .isPresent();

System.out.println(exists);

This code converts the list2 into a Set of wrapped persons. The goal of having a Set is to have a constant-time contains operation for better performance.

Then, the list1 is filtered. Every element found in set2 is kept and if there is an element left (that is to say, if findFirst() returns a non empty Optional), it means an element was found.

🌐
How to do in Java
howtodoinjava.com › home › collections framework › java arraylist › compare two lists in java: equality, differences, common items
Compare Two Lists in Java: Equality, Differences, Common Items
September 24, 2018 - Compare two lists in Java with equals() or ignoring order, count duplicates correctly, and find additional, missing and common items with removeAll().
Top answer
1 of 3
20

Let's run through each part of the code. First, createSharedListViaStream:

public static List<SchoolObj> createSharedListViaStream(List<SchoolObj> listOne, List<SchoolObj> listTwo)
{
    // We create a stream of elements from the first list.
    List<SchoolObj> listOneList = listOne.stream()
    // We select any elements such that in the stream of elements from the second list
    .filter(two -> listTwo.stream()
    // there is an element that has the same name and school as this element,
        .anyMatch(one -> one.getName().equals(two.getName()) 
            && two.getSchool().equals(one.getSchool())))
    // and collect all matching elements from the first list into a new list.
    .collect(Collectors.toList());
    // We return the collected list.
    return listOneList;
}

After running through the code, it does exactly what you want it to do. Now, let's run through createSharedListViaLoop:

public static List<SchoolObj> createSharedListViaLoop(List<SchoolObj> listOne, List<SchoolObj> listTwo)
{
    // We build up a result by...
    List<SchoolObj> result = new ArrayList<SchoolObj>();
    // going through each element in the first list,
    for (SchoolObj one : listOne)
    {
    // going through each element in the second list,
        for (SchoolObj two : listTwo)
        {
    // and collecting the first list's element if it matches the second list's element.
            if (one.getName().equals(two.getName()) && one.getSchool().equals(two.getSchool()))
            {
                result.add(one);
            }
        }
    }
    // We return the collected list
    return result;
}

So far, so good... right? In fact, your code in createSharedListViaStream is fundamentally correct; instead, it is your createSharedListViaLoop that may be causing discrepancies in output.

Think about the following set of inputs:
List1 = [SchoolObj("nameA","SchoolX"), SchoolObj("nameC","SchoolZ")]
List2 = [SchoolObj("nameA","SchoolX"), SchoolObj("nameA","SchoolX"), SchoolObj("nameB","SchoolY")]

Here, createSharedListViaStream will return the only element of the first list that appears in both lists: SchoolObj("nameA","SchoolX"). However, createSharedListViaLoop will return the following list: [SchoolObj("nameA","SchoolX"),SchoolObj("nameA","SchoolX")]. More precisely, createSharedListViaLoop will collect the correct object, but it will do so twice. I suspect this to be the reason for the output of createSharedListViaStream to be "incorrect" based on comparison to the output of createSharedListViaLoop.

The reason that createSharedListViaLoop does this duplication is based on the lack of termination of its inner for loop. Although we iterate over all elements of the first list to check if they are present in the second, finding a single match will suffice to add the element to the result. We can avoid redundant element addition by changing the inner loop to the following:

for (SchoolObj one : listOne)
    {
    for (SchoolObj two : listTwo)
    {
        if (one.getName().equals(two.getName()) && one.getSchool().equals(two.getSchool()))
        {
            result.add(one);
            break;
        }
    }
}

Additionally, if you don't want duplicate Objects in your list (by location in memory), you can use distinct like so:

List<SchoolObj> result = ...;
result = result.stream().distinct().collect(Collectors.toList());

As a final caution, the above will keep the results distinct in the following scenario:

List<SchoolObj> list = new ArrayList<>();
SchoolObj duplicate = new SchoolObj("nameC", "schoolD");
listOne.add(duplicate);
listOne.add(duplicate);
list.stream().distinct().forEach(System.out::println); 
// prints:
// nameC schoolD

However, it will not work in the following scenario, unless you override the equals method for SchoolObj:

List<SchoolObj> list = new ArrayList<>();
listOne.add(new SchoolObj("nameC", "schoolD"));
listOne.add(new SchoolObj("nameC", "schoolD"));
list.stream().distinct().forEach(System.out::println); 
// prints (unless Object::equals overridden)
// nameC schoolD
// nameC schoolD
2 of 3
9

You can filter in one list if contains in another list then collect.

List<SchoolObj> listCommon = listTwo.stream()
                                         .filter(e -> listOne.contains(e)) 
                                         .collect(Collectors.toList());

You need to override equals() method in SchoolObj class. contains() method you will uses the equals() method to evaluate if two objects are the same.

@Override
public boolean equals(Object o) {
    if (!(o instanceof SchoolObj))
        return false;
    SchoolObj n = (SchoolObj) o;
    return n.name.equals(name) && n.school.equals(school);
}

But better solution is to use Set for one list and filter in another list to collect if contains in Set. Set#contains takes O(1) which is faster.

Set<SchoolObj> setOne = new HashSet<>(listOne);
List<SchoolObj> listCommon = listTwo.stream()
                                     .filter(e -> setOne.contains(e)) 
                                     .collect(Collectors.toList());

You need to override hashCode() method also along with equals() in SchoolObj class for Set#contains.(assuming name and school can't be null)

@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + name.hashCode();
    result = prime * result + school.hashCode();
    return result;
}

Here you will get details how to override equals and hashCode in a better way

🌐
Javaprogramto
javaprogramto.com › 2020 › 04 › how-to-compare-two-arraylist-for-equality-in-java.html
How to compare two ArrayList for equality in Java 8? ArrayList equals() or containsAll() methods works?
June 13, 2021 - The below program shows how to compare two ArrayList of different objects in Java 8 using stream api.. public class TwoArrayListEqualityExampleInJava8 { public static void main(String[] args) { // list 1 List<String> list1 = new ArrayList<String>(); list1.add("compare"); list1.add("two"); list1.add("lists"); list1.add("in java 8"); // list 2 List<String> list2 = new ArrayList<String>(); list2.add("compare"); list2.add("two"); list2.add("lists"); list2.add("in java 8"); List<String> unavailable = list1.stream().filter(e -> (list2.stream().filter(d -> d.equals(e)).count()) < 1) .collect(Collecto
🌐
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 - As an example, imagine we have a list of students who applied for an exam, and another list of students who passed it. The difference between those two lists would give us the students who didn’t pass the exam. In Java, there’s no explicit way of finding the differences between two lists in the List API, though there are some helper methods that come close.
🌐
CodingTechRoom
codingtechroom.com › question › java-8-stream-find-matching-elements-list
How to Find Matching Elements in Two Lists Using Java 8 Streams - CodingTechRoom
// Import statements import java.util.*; import java.util.stream.Collectors; public class MatchingElements { public static void main(String[] args) { List<String> list1 = Arrays.asList("apple", "banana", "cherry"); List<String> list2 = Arrays.asList("banana", "kiwi", "cherry"); // Finding matching elements List<String> matchingElements = list1.stream() .filter(list2::contains) .collect(Collectors.toList()); System.out.println("Matching elements: " + matchingElements); } }
Find elsewhere
Top answer
1 of 6
4

What you seem to be after is something known as the cartesian product.

See https://en.wikipedia.org/wiki/Cartesian_product

You can do this with Java 8 streams

All permutations

// Just substitute the types and values for Lecture and Classroom instances
// I'm not going to do this for you
final List<String> first = Arrays.asList("foo","bar","baz");
final List<String> second = Arrays.asList("spam","ham","eggs");

final Set<Map.Entry<String,String>> objects = 
    first
      .stream()
      .flatMap(f -> 
         second
           .stream()
           .map(s -> new AbstractMap.SimpleEntry<>(f, s)))
      .collect(Collectors.toSet());

Your 'objects' set is going to contain Abstract entry maps which hold your combinations.

Set[
  Map{foo : spam}
  Map{foo : ham}
  Map{foo : eggs}
  Map{bar : spam}
  Map{bar : ham}
  Map{bar : eggs}
  Map{baz : spam}
  Map{baz : ham}
  Map{baz : eggs}
]

Groups of combinations

If you actually want 3 items in your set, you can do an intermediate collect on the second stream to collect into a data structure of your choice. Below shows that for a list, as I've already shown use of Collectors.toSet()

final Set<List<AbstractMap.SimpleEntry<String,String>>> objects = 
    first
      .stream()
      .map(f -> 
         second
           .stream()
           .map(s -> new AbstractMap.SimpleEntry<>(f, s))
           .collect(Collectors.toList()))
      .collect(Collectors.toSet());

Your 'objects' set is going to contain a list of Abstract entry maps which hold your combinations.

Set[
  List(
    Map{foo : spam}, Map{foo : ham}, Map{foo : eggs}
  ),
  List(
    Map{bar : spam}, Map{bar : ham}, Map{bar : eggs}
  ),
  List(
    Map{baz : spam}, Map{baz : ham}, Map{baz : eggs}
  )
]

This illustrates a simple cartesian product algorithm using Java 8 in a single functional statement. Should you wish to add any clauses or exclusions, you can use filter or any of the other higher order functions to manipulate the stream.

2 of 6
1

So i got sucked in to this one and wrote a working solution

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;

class ClassMatcher {

        //The set of all possible matchings.
    static ArrayList<ArrayList<Pair>> set = new ArrayList<ArrayList<Pair>>(); 
        // The current matching being built
    static ArrayList<Pair> cur = new ArrayList<Pair>();

    public static void main(String[] args) {

        Lecture[] l = { new Lecture(50, 1), new Lecture(70, 2), new Lecture(50, 3)};
        ArrayList<Classroom> c = new ArrayList<>(Arrays.asList(
            new Classroom(50, 1), new Classroom(70, 2),
            new Classroom(100, 3)));

        for (int i = 0; i < l.length; i++) {
                    //Fill with dummy values
            cur.add(new Pair(new Classroom(-1, -1), new Lecture(-1, -1)));
        }

        // Sort the arrays to save work in rec()
        Arrays.sort(l);
                //Sort classrooms in descending order
        Collections.sort(c, new Comparator<Classroom>() {
            @Override
            public int compare(Classroom o1, Classroom o2) {
                return o1.compareTo(o2) * -1;
            }
        });

        recursive(l, c, 0);

        // Print all the sets
        for (int i = 0; i < set.size(); i++) {
            System.out.print("{");
            for (int j = 0; j < set.get(i).size(); j++) {
                System.out.print("Lecture " + set.get(i).get(j).l + ": "
                    + "Classroom " + set.get(i).get(j).c);
                if (j < set.get(i).size() - 1) {
                    System.out.print(", ");
                } else {
                    System.out.print("}");
                }
            }
            System.out.println();
        }

    }

    public static void recursive(Lecture[] lectureList,
            ArrayList<Classroom> classroomList, int curLecture) {

        for (int i = 0; i < classroomList.size(); i++) {
            // if the classroom is smaller than the lecture we cna stop as the
            // lists are sorted so all other lectures will be to big for the
            // current classroom
            if (lectureList[curLecture].size > classroomList.get(i).size) {
                return;
            }

            //Match the current classroom to the current lecture and add to the working matching
            cur.set(curLecture, new Pair(classroomList.get(i), lectureList[curLecture]));

                //If there are more lectures to do then remove the used classroom and recursively call.
            if (curLecture < lectureList.length - 1) {
                Classroom tmp = classroomList.remove(i);
                recursive(lectureList, classroomList, curLecture + 1);
                classroomList.add(i, tmp);
            } 
                // If no Lectures left then add this matching to the set of all matchings. 
            else {
                ArrayList<Pair> copy = (ArrayList<Pair>) cur.clone();
                set.add(copy);
            }
        }

    }

}

class Classroom implements Comparable<Classroom> {

    int size;
    int number;

    public Classroom(int s, int n) {
        size = s;
        number = n;
    }

    @Override
    public int compareTo(Classroom o) {

        return Integer.compare(this.size, o.size);
    }

    public String toString() {
        return number + " (" + size + ")";
    }
}

class Lecture implements Comparable<Lecture> {

    int size;
    int number;

    public Lecture(int s, int n) {
        size = s;
        number = n;
    }

    @Override
    public int compareTo(Lecture o) {

        return Integer.compare(this.size, o.size);
    }

    public String toString() {
        return number + " (" + size + ")";
    }
}

class Pair {

    Classroom c;
    Lecture l;

    public Pair(Classroom c, Lecture l) {
        this.c = c;
        this.l = l;
    }
}

This gives the output

{Lecture 1 (50): Classroom 3 (100), Lecture 3 (50): Classroom 1 (50), Lecture 2 (70): Classroom 2 (70)}
{Lecture 1 (50): Classroom 2 (70), Lecture 3 (50): Classroom 1 (50), Lecture 2 (70): Classroom 3 (100)}
{Lecture 1 (50): Classroom 1 (50), Lecture 3 (50): Classroom 3 (100), Lecture 2 (70): Classroom 2 (70)}
{Lecture 1 (50): Classroom 1 (50), Lecture 3 (50): Classroom 2 (70), Lecture 2 (70): Classroom 3 (100)}
Top answer
1 of 12
182

EDIT

Here are two versions. One using ArrayList and other using HashSet

Compare them and create your own version from this, until you get what you need.

This should be enough to cover the:

P.S: It is not a school assignment :) So if you just guide me it will be enough

part of your question.

continuing with the original answer:

You may use a java.util.Collection and/or java.util.ArrayList for that.

The retainAll method does the following:

Retains only the elements in this collection that are contained in the specified collection

see this sample:

import java.util.Collection;
import java.util.ArrayList;
import java.util.Arrays;

public class Repeated {
    public static void main( String  [] args ) {
        Collection listOne = new ArrayList(Arrays.asList("milan","dingo", "elpha", "hafil", "meat", "iga", "neeta.peeta"));
        Collection listTwo = new ArrayList(Arrays.asList("hafil", "iga", "binga", "mike", "dingo"));

        listOne.retainAll( listTwo );
        System.out.println( listOne );
    }
}

EDIT

For the second part ( similar values ) you may use the removeAll method:

Removes all of this collection's elements that are also contained in the specified collection.

This second version gives you also the similar values and handles repeated ( by discarding them).

This time the Collection could be a Set instead of a List ( the difference is, the Set doesn't allow repeated values )

import java.util.Collection;
import java.util.HashSet;
import java.util.Arrays;

class Repeated {
      public static void main( String  [] args ) {

          Collection<String> listOne = Arrays.asList("milan","iga",
                                                    "dingo","iga",
                                                    "elpha","iga",
                                                    "hafil","iga",
                                                    "meat","iga", 
                                                    "neeta.peeta","iga");

          Collection<String> listTwo = Arrays.asList("hafil",
                                                     "iga",
                                                     "binga", 
                                                     "mike", 
                                                     "dingo","dingo","dingo");

          Collection<String> similar = new HashSet<String>( listOne );
          Collection<String> different = new HashSet<String>();
          different.addAll( listOne );
          different.addAll( listTwo );

          similar.retainAll( listTwo );
          different.removeAll( similar );

          System.out.printf("One:%s%nTwo:%s%nSimilar:%s%nDifferent:%s%n", listOne, listTwo, similar, different);
      }
}

Output:

$ java Repeated
One:[milan, iga, dingo, iga, elpha, iga, hafil, iga, meat, iga, neeta.peeta, iga]

Two:[hafil, iga, binga, mike, dingo, dingo, dingo]

Similar:[dingo, iga, hafil]

Different:[mike, binga, milan, meat, elpha, neeta.peeta]

If it doesn't do exactly what you need, it gives you a good start so you can handle from here.

Question for the reader: How would you include all the repeated values?

2 of 12
45

You can try intersection() and subtract() methods from CollectionUtils.

intersection() method gives you a collection containing common elements and the subtract() method gives you all the uncommon ones.

They should also take care of similar elements

🌐
DZone
dzone.com › coding › languages › how to compare list objects in java 7 vs. java 8
How to Compare List Objects in Java 7 vs. Java 8 - DZone
June 1, 2018 - We need to define a Predicate instance with the comparison logic and provide this Predicate as an input to the matching methods. Then, Java 8 processes the matching function internally and provides you with the result whether a match for the ...
Top answer
1 of 2
4

I'd follow an approach like the following:

List<String> list1 = new ArrayList<>();
list1.add("A,Airplane");
list1.add("B,Boat");

List<String> list2 = new ArrayList<>();
list2.add("A90, Boing Airplane");
list2.add("A70, Boing777");
list2.add("B80, Boing Boat");

Pattern commaPattern = Pattern
    .compile("\\s*,\\s*"); // a regex pattern to split by comma and the whitespace around it

Map<String, String> modelToType = list1.stream().map(commaPattern::split)
    .collect(Collectors
        .toMap(modelAndType -> modelAndType[0],
            modelAndType -> modelAndType[1])); // mapping models to types for o(1) lookups

List<Vehicle> vehicles = list2.stream().map(commaPattern::split)
    .map(subModelAndSubType -> {
      Vehicle vehicle = new Vehicle();
      vehicle.submodel = subModelAndSubType[0];
      vehicle.subtype = subModelAndSubType[1];
      vehicle.model = vehicle.submodel.substring(0, 1);
      vehicle.type = modelToType.get(vehicle.model);
      return vehicle;
    }).collect(Collectors.toList());
2 of 2
2

The data as provided.

List<String> list1 = new ArrayList<>();
list1.add("A,Airplane");
list1.add("B,Boat");

List<String> list2 = new ArrayList<>();
list2.add("A90, Boeing Airplane");
list2.add("A70, Boeing777");
list2.add("B80, Boeing Boat");

Regardless of how the model type information is obtained, this conversion might be more efficient and certainly easier if instead of using Lists you used Maps to hold the information. If the information was read in from a file it would be best to pre-process them (split if need be) as they are read in and put them in a map. If it was entered by hand then putting the information in a map would negate the need for any pre-processing at all. Here is a simple example.

Map<String,String> map1 = new HashMap<>();
map1.put("A","Airplane");
map1.put("B","Boat");

However, using the information as provided here is how I proceeded.

First, I created a Lambda to assist in the List conversion.

Function<List<String>, Map<String, String>> makeMap =
        lst -> lst.stream().map(st -> st.split("\\s*,\\s*")).collect(
                Collectors.toMap(a -> a[0], a -> a[1]));

// create the TypeModel map
Map<String, String> mapTM = makeMap.apply(list1);
// create the subTypeSubModel Map;
Map<String, String> mapSTSM = makeMap.apply(list2);

Now just use the keysets of each to go thru and piece it together. I created a constructor in my example for Vehicle which imho makes a cleaner object creation.

List<Vehicle> vehicles = mapTM.keySet().stream()
        .flatMap(type -> mapSTSM.keySet().stream()
                .filter(subType -> subType.startsWith(type))
                .map(sbType -> new Vehicle(mapTM.get(type),
                        mapSTSM.get(sbType), type, sbType)))
        .collect(Collectors.toList());

vehicles.forEach(System.out::println);

Prints based on toString (which can be changed).

[Airplane, Boeing Airplane,A,A90]
[Airplane, Boeing777,A,A70]
[Boat, Boeing Boat,B,B80]

Here is the class sans setters and getters.

class Vehicle {
    
    private String model;
    private String submodel;
    private String type;
    private String subtype;
    
    public Vehicle() {
    }
    
    public Vehicle(String model, String submodel, String type,
            String subtype) {
        this.model = model;
        this.submodel = submodel;
        this.type = type;
        this.subtype = subtype;
    }
    
    public String toString() {
       return "[" + String.join(", ", model, submodel, type, subtype)
                + "]";
    }
}

🌐
DevQA
devqa.io › java-compare-two-arraylists
Java Compare Two Lists
March 17, 2020 - To compare two lists for equality just in terms of items regardless of their location, we need to use the sort() method from the Collections() class. ... import java.util.Arrays; import java.util.Collections; import java.util.List; public class ...
Top answer
1 of 4
3

The approach, algorithm

To find if an object exists in a list, you need to perform a linear search, potentially visiting every single element, in \$O(n)\$ time. More efficient data structures exist:

  • Use an ordered data structure: if the values are sorted, then you can find if an element exists using binary search, in \$O(\log n)\$ time.

  • Use a hashset: you can find if an element is in the set in constant time, \$O(1)\$

To be to search efficiently, use a hashset instead of a list. However, to be able to use a hashset efficiently, it is required that the objects you put in it have appropriate implementation of hashCode and equals methods.

See the official tutorial on the Object class, especially the sections on the equals and hashCode methods. Note that IDEs like IntelliJ and Eclipse can generate these methods for you easily (they are boring to write by hand, and usually there's little reason to do so).

With correct implementation of the equals and hashCode methods, for example as in the other answer by @Teddy, your main program could be reduced to this:

public static void main(String[] args) {

    Set<Person> originalPeople = new HashSet<>();
    Set<Person> newPeople = new HashSet<>();

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

    for (Person original : originalPeople) {
        if (!newPeople.contains(original)) {
            System.out.printf("%s %s is not in the new list!%n",
                    original.getFirstName(), original.getLastName());
        }
    }
}
2 of 4
3

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<Person> originalPeople = new ArrayList<>();; rather declare as List, to follow programming to interface.

Rather I would use below approach:

private static List<Person> getPersonInList(
            final List<Person> newPeople, List<Person> originalPeople) {
        List<Person> list = new ArrayList<>();

        newPeople.forEach(p ->
                originalPeople.stream()
                              .filter(p1 -> p.getFirstName().equals(p1.getFirstName()) && 
                                      p.getLastName().equals(p1.getLastName()))
                              .forEach(list::add));
        return list;
    }
🌐
Coderanch
coderanch.com › t › 696394 › java › Combining-lists-Java
Combining lists using Java 8 (Features new in Java 8 forum at Coderanch)
July 9, 2018 - Then java 8 came, I used IntStreams, taking me an hour or so to get it up and running, got capitalized that list.get(i) might not be very efficient if list is say a LinkedList. So I thought couple of hours ago: why not combine a Stream with good old iterators?