0

I have two arraylists of objects, I want to know which strings are unique to arraylist 1, and which strings are unique to arraylist 2. What I have come up with is the forloop below, which I have to implement twice, reversing the positions of the arraylists. I'm hopeful someone can suggest a more elegant way to do this.

Per request, a bunch more stuff I guess I wrongfully assumed was implied in the code-snippet itself. And the output this produces is:

grape doesn't exist in second arrayList

pineapple doesn't exist in first arrayList

Works great, everything is great, but, per above, I'm hopeful someone with more knowledge of streams/java in general can provide a better solution than just running my stream twice, with the inputs reversed.

import java.util.ArrayList;

public class CompareTwoArrays {

    ArrayList<MyCustomObject> firstArrayListOfObjects = new ArrayList<>();
    ArrayList<MyCustomObject> secondArrayListOfObjects = new ArrayList<>();

    public void superSpecificExampleMethod() {
        firstArrayListOfObjects.add(new MyCustomObject(1, 1, "apple"));
        firstArrayListOfObjects.add(new MyCustomObject(1, 1, "orange"));
        firstArrayListOfObjects.add(new MyCustomObject(1, 1, "banana"));
        firstArrayListOfObjects.add(new MyCustomObject(1, 1, "grape"));
        secondArrayListOfObjects.add(new MyCustomObject(1, 1, "apple"));
        secondArrayListOfObjects.add(new MyCustomObject(1, 1, "pineapple"));
        secondArrayListOfObjects.add(new MyCustomObject(1, 1, "orange"));
        secondArrayListOfObjects.add(new MyCustomObject(1, 1, "banana"));

        for (MyCustomObject object : firstArrayListOfObjects) {
            if (!secondArrayListOfObjects.stream().map(MyCustomObject::getString).filter(object.getString()::equals).findFirst().isPresent()) {
                System.out.println(object.getString() + " doesn't exist in second arrayList");
            }
        }

        for (MyCustomObject object : secondArrayListOfObjects) {
            if (!firstArrayListOfObjects.stream().map(MyCustomObject::getString).filter(object.getString()::equals).findFirst().isPresent()) {
                System.out.println(object.getString() + " doesn't exist in first arrayList");
            }
        }
    }
}

class MyCustomObject {

    private int randomIntOne;
    private int randomIntTwo;
    private String string;

    public MyCustomObject(int randomIntOne, int randomIntTwo, String string) {
        this.randomIntOne = randomIntOne;
        this.randomIntTwo = randomIntTwo;
        this.string = string;
    }

    public String getString() {
        return string;
    }
}
6
  • 1
    Could you provide a test case? Commented Jul 16, 2021 at 19:51
  • Sorry, I'm a novice, and am not sure what you're asking for. By test case, do you mean an example of the arraylist of objects? That's the only thing I can think of that isn't present in the snippet. Commented Jul 16, 2021 at 19:57
  • Just print an example of two arrays and required output. Commented Jul 16, 2021 at 20:12
  • 1
    Java Object class does not have method getString. Do you mean you have your own POJO with this method? Commented Jul 16, 2021 at 20:16
  • Yes, correct. Maybe that is the root cause of oleg.cherednik's request, my seemingly poor choice for a name of my generic object. Commented Jul 16, 2021 at 20:36

1 Answer 1

1

Assuming there are two array lists of objects MyObject containing strings:

List<MyObject> listOne = new ArrayList<>(Arrays.asList(
    new MyObject("aaa"), new MyObject("bbb"), new MyObject("ccc"), new MyObject("ddd")
));

List<MyObject> listTwo = new ArrayList<>(Arrays.asList(
    new MyObject("fff"), new MyObject("bbb"), new MyObject("ggg"), new MyObject("ddd")
));

To find "unique" objects in listOne that is those which are not available in listTwo there are several ways:

  1. Use List::removeAll providing that the methods equals and hashCode are properly implemented in this class

removeAll should be applied to a copy of listOne

List<MyObject> diffOneMinusTwo = new ArrayList<>(listOne); // copy
diffOneMinusTwo.removeAll(listTwo); // ["aaa", "ccc"]
  1. Use List::removeIf accepting a predicate and using a set of the strings contained in the objects of listTwo:
Set<String> listTwoStrings = listTwo
    .stream()
    .map(MyObject::getString)
    .collect(Collectors.toSet);
List<MyObject> diffOneMinusTwo = new ArrayList<>(listOne); // copy
diffOneMinusTwo.removeIf(x -> listTwoStrings.contains(x.getString()));
  1. Use Stream API filter and collect - no copy is needed here but a temporary set of strings is used
List<MyObject> diffOneMinusTwo = listOne
        .stream()
        .filter(x -> !listTwoStrings.contains(x.getString()))
        .collect(Collectors.toList());

In Java 11 there is static Predicate::not method so the stream version may look like this (if hashCode and equals are implemented properly):

List<MyObject> diffOneMinusTwo = listOne
        .stream()
        .filter(Predicate.not(listTwo::contains)) // using method reference
        .collect(Collectors.toList());

The difference between listTwo and listOne can be created vice versa.

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks, but unfortunately I don't have two lists of strings, I have two arraylists of objects which contain strings.
Don;t use the class name Object. That's the origin of all objects in Java itself. You might get way with it, but best not to try ;) Call it MyObject and check the API docs for overriding key methods of all objects: hashCode and equals. Those are essential to how collections behave w.r.t. your custom class. Use methods like contains() simply will NOT work unless you override those methods
@g00se Yes, I see that now. Out of curiosity, would my original code snippet, which was just one of the for loops, make any sense/even function if 'Object' was anything other than a custom object?
@AlexRudenko So, looking at your answer, it seems that the best option is what I already have, just running the stream twice, first with listA vs listB, and then with listB vs listA. Thanks. Not sure what alternative I was hoping for, just seems kind of privative.
Not exactly, in your code you run the stream operations on listTwo for each element of listOne that is an overkill. removeAll is the shortest way of finding a difference (what is implied in your task) between two collections providing that hashCode and equals are implemented properly.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.