You want intersection of the list while saving of first list type.

Get the common field values in the Set.

valuesToCheck=secondList.stream().map(SecondListObject::commonFiled).collect(Collectors.toList);

'''

Apply a stream on the first while filtering based on the matching common field value in the set built in the previous step.

firstList.stream().filter(x->valuesToCheck.contains(x.getCommonFiled)).collect(toList)

You got the gist.

Answer from Gajraj Tanwar on Stack Overflow
🌐
Baeldung
baeldung.com › home › java › java streams › java streams: find items from one list based on values from another list
Java Streams: Find Items From One List Based on Values From Another List | Baeldung
November 30, 2024 - In this quick tutorial, we’ll learn how to find items from one list based on values from another list using Java 8 Streams. Let’s start with two entity classes – Employee and Department: class Employee { Integer employeeId; String employeeName; // getters and setters } class Department { Integer employeeId; String department; // getters and setters } The idea here is to filter a list of Employee objects based on a list of Department objects.
🌐
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
June 1, 2018 - Now let us develop all the above use cases in Java 8. The Java 8 Stream API provides three methods allMatch, anyMatch, and noneMatch, which can be applied to a stream object that matches the given Predicate and then returns a boolean value.
🌐
Backendless
support.backendless.com › server code › codeless
Best way to compare two lists of objects - Codeless - Backendless Support
January 8, 2022 - I am trying to figure out the most efficient way (lowest execution time) to filter two lists of objects, keeping only a sublist of list A whose objects are not in list B. One list is coming form an external API and the …
🌐
Coderanch
coderanch.com › t › 673893 › java › Comparing-ArrayLists-objects-shared-properties
Comparing two ArrayLists containing different objects with (some) shared properties (Beginning Java forum at Coderanch)
December 15, 2016 - In short, what I'd like to code ... current solution I have in mind is to simply iterate through the two ArrayLists at the same time (after sorting them), comparing the shared properties, and if they match up, continue the loop....
🌐
Baeldung
baeldung.com › home › java › java list › finding the differences between two lists in java
Finding the Differences Between Two Lists in Java | Baeldung
January 28, 2026 - We investigate how to finding elements present in one list, but not in another, including lists with multiple copies of the same element.
🌐
Stack Overflow
stackoverflow.com › questions › 52403761 › java-8-compare-2-lists-based-on-one-property
filter - Java 8 compare 2 lists based on one property - Stack Overflow
September 19, 2018 - You can do it in Java 8 “by putting 2 for each loops” as well. ... list1.stream() .flatMap(x -> list2 .stream() .filter(y -> x.getId() == y.getId()) .filter(y -> !x.getLastUpdated().isEqual(y.getLastUpdated())) .limit(1)) .forEach(product ...
Top answer
1 of 2
1

Supposing that the class is named Foo and that the field to change is String valueToReplace with getter/setter, you could use listOne.replaceAll() in this way :

list1.replaceAll(one ->  list2.stream()
                              .filter(other -> other.getCode().equals(one.getCode())
                              .findAny()
                              .map(Foo::getValueToReplace)
                              .ifPresent( newValue -> one.setValueToReplace(newValue));
                        return one;
                )

The idea is for each elements of list1 you replace the valueToReplace field of it by the value of the first match of list2. Otherwise you do nothing.
This code is not as efficient as it could but for small lists it is perfect.
For bigger lists, using a Map to store code/valueToReplace is very welcome.

// supposing that the code are unique in each list
Map<Integer, String>  mapOfValueToReplaceByCode =
    list2.stream()
         .collect(toMap(Foo::getCode, Foo::getValueToReplace));

list1.replaceAll(one -> {
                 String newValue = mapOfValueToReplaceByCode.get(one.getCode());
                 if (newValue != null){ 
                     one.setValueToReplace(newValue);
                 }
                  return one;
                )
2 of 2
1

Just keep the replace values in a map (with code as key) and then iterate over list1 to modify where necessary.

Map<String, String> replaceValues = list2.stream()
    .collect(Collectors.toMap(x -> x.code, x -> x.tobereplace));
list1.stream
    .filter(x -> replaceValues.containsKey(x.code))
    .forEach(x -> x.tobereplace = replaceValues.get(x.code));

EDIT

As josejuan points out in the comments, the Collectors.toMap will throw an exception if list2 contains duplicate values. The OP doesn't really specify what to do in that case, but the solution is using a merge function in the Collectors.toMap.

This will use the first element it encounters with any given code:

Map<String, String> replaceValues = list2.stream()
    .collect(Collectors.toMap(x -> x.code, x -> x.tobereplace, (x1, x2) -> x1));

The merge policy could be anything, like using the first element with a non-empty value e.g.

If the list was known to not have duplicates, please use a Set instead of a List. It will make things clearer for anyone reading the code, and help you avoid unnecessary checks.

🌐
Quora
quora.com › How-can-two-lists-of-objects-be-compared-in-Java
How can two lists of objects be compared in Java? - Quora
Google Guava’s Maps.difference (after mapping), or java-diff-utils for sequence diffs (gives edit script), or Apache Commons Collections for collection utilities. For complex objects, base diff on unique identifier (id) to detect modifications rather than object equality.
Find elsewhere
🌐
YouTube
youtube.com › java coding insight interview preparation
Comparing List of Objects Using Multiple Fields -Plain Java 8 Comparator - No Third Party Library - YouTube
If you find this video helpful, please 'Like' or 'Subscribe'. This is really helpful for the channel and also motivates me to do more of such good and usefu...
Published   September 30, 2021
Views   5K
🌐
CopyProgramming
copyprogramming.com › howto › java-8-compare-two-lists-of-objects
Java: Comparing Lists of Objects in Java 8
April 6, 2023 - Java 8 - comparing two different type of List using Java8, create an auxiliary class with two fields val1 and val2, and associate every instance of A and B with instances of this class; create a nested map … ... list1.stream() .flatMap(x -> list2 .stream() .filter(y -> x.getId() == y.getId()) ...
🌐
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? JavaProgramTo.com
June 13, 2021 - A quick program to compare two list values inside the lists. This equality check is done using the ArrayList equals() method and containsAll() method. Java 8 Stream API Example as well.
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

Top answer
1 of 1
1

I find the current algorithm of "adding and removing" to be a streamed implementation of non-functional thinking. Rather than adding and removing, I encourage you to think in terms of joining (akin to SQL outer-joining) and filtration, which are more functional concepts. In this interpretation, your "local" collection is on the left-hand side of a left outer join, and your "remote" collection is on the right. This reduces the number of iterations from your six down to two, and produces the same output:

Customer.java

package com.stackexchange.ConsentExample;

import java.util.Collection;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public record Customer(
    Long id,
    String name,
    Integer consent
) {
    @Override
    public String toString() {
        return String.format(
            "Customer{id=%d, name='%s', consent=%d}",
            id, name, consent);
    }

    public static Stream<Customer> join(
        Collection<Customer> locals,
        Collection<Customer> remotes
    ) {
        /* Take local consent if there is no matching remote consent.
           Take remote consent if there is a match. */
        Map<Integer, Customer> remoteByConsent =
            remotes.stream()
            .collect(Collectors.toMap(
                Customer::consent, Function.identity()
            ));

        return locals.stream()
            .map(local ->
                remoteByConsent.getOrDefault(local.consent, local)
            );
    }
}

JoinTest.java

import com.stackexchange.ConsentExample.Customer;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

public class JoinTest {
    @Test
    public void testJoin() {
        final List<Customer>
        remotes = List.of(
            new Customer(10L, "name1", 12),
            new Customer(11L, "name2", 11),
            new Customer(12L, "name3", 14),
            new Customer(13L, "name4", 16)
        ),
        locals = List.of(
            new Customer(1L, "name1", 12),
            new Customer(2L, "name2", 13),
            new Customer(3L, "name3", 14),
            new Customer(4L, "name4", 15)
        );
        Map<Long, Customer> result =
            Customer.join(locals, remotes)
            .collect(Collectors.toMap(
                Customer::id, Function.identity()
            ));

        assertEquals(4, result.size());

        assertEquals("name1", result.get(10L).name());
        assertEquals(12, result.get(10L).consent());
        assertEquals("name2", result.get(2L).name());
        assertEquals(13, result.get(2L).consent());
        assertEquals("name3", result.get(12L).name());
        assertEquals(14, result.get(12L).consent());
        assertEquals("name4", result.get(4L).name());
        assertEquals(15, result.get(4L).consent());
    }
}
🌐
Google Groups
groups.google.com › g › clojure › c › YYFLB7Z4II8
Get difference between two lists with java objects of same class
March 11, 2013 - I am trying to find out, which objects from the first list, based on that "id" property, are not included in list b. I assume this rules out the use of clojure.data/diff and I will need my own function like Marko suggested to make those property comparisons. Yes, from data/diff's perspective you'd have all distinct objects. ... Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message ... I only asked because if they aren't java.util.Lists to begin with, you'd definitely not want to convert into one just to use removeAll.
🌐
Stack Overflow
stackoverflow.com › questions › 59376939 › how-to-compare-two-lists-of-java-objects-by-property-values
testing - How to Compare Two Lists of Java Objects by Property Values - Stack Overflow
December 17, 2019 - A direct comparison, to ensure that the object properties of each object in the first list are matched by object properties of some object in the second list. I'd considered overriding equals, but was wondering if there are tools available to do this "directly" ... By overriding the equal and hash methods for your object,you can use the compare method of your lists as shows in the following sample: this helps you to compare objects by as many as their proprieties as you want. import java.util.*; public class TestListConvert { public static void main(String[] args) { List<Obs> firstList = new A
🌐
CopyProgramming
copyprogramming.com › howto › java-java-compare-two-objects-by-properties
Java: Comparing Two Java Objects Based on Their Properties
September 25, 2023 - One way to utilize the compare method of your lists is by implementing the equal and hash methods for your object, as illustrated in the following example. This feature enables you to compare objects based on multiple properties that you can customize according to your preferences. import ...
Top answer
1 of 4
19

If I understand correctly, this is the example scenario:

  • listOne [datab] items: [A, B, C, D]
  • listTwo [front] items: [B, C, D, E, F]

and what you need to get as an effect is:

  • added: [E, F]
  • deleted: [A]

First thing first, I would use some type adapter or extend the different types from one common class and override the equals method so you can match them by id and name

Secondly, this is very easy operations on sets (you could use set's but list are fine too). I recommend using a library: https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/CollectionUtils.html

And now basically:

  • added is listTwo - listOne
  • deleted is listOne - listTwo

and using java code:

  • added: CollectionUtils.removeAll(listTwo, listOne)
  • deleted: CollectionUtils.removeAll(listOne, listTwo)

Otherwise, all collections implementing Collection (Java Docs) also has removeAll method, which you can use.

2 of 4
18

I propose solution using java 8 streams:

    ArrayList<ObjOne> list = new ArrayList<>(Arrays.asList(new ObjOne("1","1"),new ObjOne("3","3"),new ObjOne("2","2")));
    ArrayList<ObjTwo> list2 = new ArrayList<>(Arrays.asList(new ObjTwo("1","1"),new ObjTwo("3","3"),new ObjTwo("4","4")));

    List<ObjOne> removed = list.stream().filter(o1 -> list2.stream().noneMatch(o2 -> o2.getId().equals(o1.getId())))
            .collect(Collectors.toList());
    System.out.print("added ");
    removed.forEach(System.out::println);

    List<ObjTwo> added = list2.stream().filter(o1 -> list.stream().noneMatch(o2 -> o2.getId().equals(o1.getId())))
             .collect(Collectors.toList());

    System.out.print("removed ");
    added.forEach(System.out::println);

This is basically your solution but implemented using streams, which will make your code shorter and easer to read