0

I have an object that has a list as its property, e.g.

public class A {
  private String aName;
  private List<B> bList;
}

public class B {
  private String bName;
}

Lets assume that we have two lists of A's:

List<A> existingList = new ArrayList<A>();
// populate it with a list of A's which in turn, each has its own list of B's

List<A> newList = new ArrayList<A>();
// populate it with possibly some new A's and/or, an existing A which its property B has new items

With that in mind, I would like to know the fastest way to compare these two list of A's, and add the delta of these two lists to the existingList.

Note that we are also comparing a list of B's for A's in both lists, so if there is a matching A's, but there is a delta of their B's, we should be able to add it to existingList's A's bList.

Equally, if we detect an item has been deleted from newList, we should remove that from existingList.

Note, this is not simply about comparing two objects, but rather finding the delta deep in the object graph and updating or adding new and/or existing parts. Here is the sample example:

package collection.delta.model;

import java.util.List;
import java.util.Objects;

import org.apache.commons.lang3.StringUtils;

package collection.delta.model;

import java.util.List;
import java.util.Objects;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

public class A {
    private String aName;

    private List<B> bList;

    public String getaName() {
        return aName;
    }

    public void setaName(String aName) {
        this.aName = aName;
    }

    public List<B> getbList() {
        return bList;
    }

    public void setbList(List<B> bList) {
        this.bList = bList;
    }

    public A(String name, List<B> bList) {
        this.aName = name;
        this.bList = bList;
    }

    @Override
    public boolean equals(Object o) {
        if (o == null) {
            return false;
        }

        if (o == this) {
            return true;
        }

        if (!(o instanceof A)) {
            return false;
        }

        A b = (A) o;
        return StringUtils.isNotBlank(aName) && StringUtils.equalsAnyIgnoreCase(b.getaName(), aName)
                && CollectionUtils.disjunction(this.getbList(), b.getbList()).isEmpty();
    }

    @Override
    public int hashCode() {
        if (StringUtils.isBlank(aName)) {
            return 31;
        }

        return Objects.hashCode(aName);
    }   

package collection.delta.model;

import java.util.Objects;

import org.apache.commons.lang3.StringUtils;

public class B {
    private String bName;

    public String getbName() {
        return bName;
    }

    public void setbName(String bName) {
        this.bName = bName;
    }

    public B(String name) {
        this.bName = name;
    }

    @Override
    public boolean equals(Object o) {
        if (o == null) {
            return false;
        }

        if (o == this) {
            return true;
        }

        if (!(o instanceof B)) {
            return false;
        }

        B b = (B) o;
        return StringUtils.isNotBlank(bName) && StringUtils.equalsAnyIgnoreCase(b.getbName(), bName);
    }

    @Override
    public int hashCode() {
        if (StringUtils.isBlank(bName)) {
            return 31;
        }

        return Objects.hashCode(bName);
    }
}

package collection.delta;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import collection.delta.model.A;
import collection.delta.model.B;

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

        List<A> originalA = new ArrayList<A>();
        List<A> newA = new ArrayList<A>();

        List<B> bListOriginalA1 = new ArrayList<B>();
        bListOriginalA1.add(new B("originalA1_B1"));
        bListOriginalA1.add(new B("originalA1_B2"));
        bListOriginalA1.add(new B("originalA1_B3"));
        bListOriginalA1.add(new B("originalA1_B4"));

        A originalA1 = new A("originalA1", bListOriginalA1);

        List<B> bListOriginalA2 = new ArrayList<B>();
        bListOriginalA2.add(new B("originalA2_B1"));
        bListOriginalA2.add(new B("originalA2_B2"));
        bListOriginalA2.add(new B("originalA2_B3"));
        bListOriginalA2.add(new B("originalA2_B4"));

        A originalA2 = new A("originalA2", bListOriginalA2);

        List<B> bListOriginalA3 = new ArrayList<B>();
        bListOriginalA3.add(new B("originalA3_B1"));
        bListOriginalA3.add(new B("originalA3_B2"));
        bListOriginalA3.add(new B("originalA3_B3"));
        bListOriginalA3.add(new B("originalA3_B4"));

        A originalA3 = new A("originalA3", bListOriginalA3);

        originalA.add(originalA1);
        originalA.add(originalA2);
        originalA.add(originalA3);


        List<B> bListNewA1 = new ArrayList<B>();
        bListNewA1.add(new B("originalA1_B1"));
        bListNewA1.add(new B("originalA1_B2"));
        bListNewA1.add(new B("originalA1_B3"));
        bListNewA1.add(new B("originalA1_B4"));

        A newA1 = new A("originalA1", bListNewA1);

        List<B> bListNewA2 = new ArrayList<B>();
        bListNewA2.add(new B("originalA2_B1"));
        bListNewA2.add(new B("originalA2_B2"));
        bListNewA2.add(new B("originalA2_B3"));
        bListNewA2.add(new B("originalA2_B4"));

        A newA2 = new A("originalA2", bListNewA2);

        List<B> bListNewA3 = new ArrayList<B>();
        bListNewA3.add(new B("originalA3_B1"));
        bListNewA3.add(new B("originalA3_B2"));
        bListNewA3.add(new B("originalA3_B5"));
        bListNewA3.add(new B("originalA3_B4"));

        A newA3 = new A("originalA3", bListNewA3);

        List<B> bListNewA4 = new ArrayList<B>();
        bListNewA4.add(new B("A4_B1"));
        bListNewA4.add(new B("A4_B2"));
        bListNewA4.add(new B("A4_B3"));
        bListNewA4.add(new B("A4_B4"));

        A newA4 = new A("originalA4", bListNewA4);

        newA.add(newA1);
        newA.add(newA2);
        newA.add(newA3);
        newA.add(newA4);

        List<A> result = CollectionUtils.disjunction(originalA, newA).stream().collect(Collectors.toList());
        for (A a : newA) {
            for (A aResult : result) {
                if (StringUtils.equalsAnyIgnoreCase(a.getaName(), aResult.getaName())) {
                    originalA.add(aResult);
                }
            }
        }

        System.out.println("");
    }

}
NuCradle
  • 665
  • 1
  • 9
  • 31
  • Possible duplicate of [Compare two objects with "<" or ">" operators in Java](https://stackoverflow.com/questions/29179194/compare-two-objects-with-or-operators-in-java) – surendrapanday Mar 01 '19 at 01:21
  • @surendrapanday This question is not about only comparison of two objects. Please do not mark it as duplicate or down voted before understanding the problem. – NuCradle Mar 01 '19 at 03:19
  • Just letting you know that I have not downvoted you, but i found bunch of other questions related to this; someone else downvoted you, I don't know who did it. – surendrapanday Mar 01 '19 at 06:50

3 Answers3

0

Step 1: Implement equals() and hashCode for both classes.

Step 2: Convert lists to sets.

Step 3: Use one or more of the Set methods addAll, removeAll, and/or retainAll to exercise set operations such as union, intersection, and difference.

Andreas
  • 154,647
  • 11
  • 152
  • 247
0

If you care about order, then just use the equals method:

list1.equals(list2);

From the javadoc:

Compares the specified object with this list for equality. Returns true if and only if the specified object is also a list, both lists have the same size, and all corresponding pairs of elements in the two lists are equal. (Two elements e1 and e2 are equal if (e1==null ? e2==null : e1.equals(e2)).) In other words, two lists are defined to be equal if they contain the same elements in the same order. This definition ensures that the equals method works properly across different implementations of the List interface.

If you want to check independent of order, you could copy all of the elements to Sets and use equals on the resulting Sets:

public static <T> boolean listEqualsIgnoreOrder(List<T> list1, List<T> list2) {
    return new HashSet<>(list1).equals(new HashSet<>(list2));
}

A limitation of this approach is that it not only ignores order, but also frequency of duplicate elements. For example, if list1 was ["A", "B", "A"] and list2 was ["A", "B", "B"] the Set approach would consider them to be equal.

If you need to be insensitive to order but sensitive to the frequency of duplicates you can either:

sort both lists (or copies) before comparing them or copy all elements to a Multiset.

surendrapanday
  • 530
  • 3
  • 13
0

This works:

package collection.delta;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import collection.delta.model.A;
import collection.delta.model.B;

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

        List<A> originalA = new ArrayList<A>();
        List<A> newA = new ArrayList<A>();

        List<B> bListOriginalA1 = new ArrayList<B>();
        bListOriginalA1.add(new B("originalA1_B1"));
        bListOriginalA1.add(new B("originalA1_B2"));
        bListOriginalA1.add(new B("originalA1_B3"));
        bListOriginalA1.add(new B("originalA1_B4"));

        A originalA1 = new A("originalA1", bListOriginalA1);

        List<B> bListOriginalA2 = new ArrayList<B>();
        bListOriginalA2.add(new B("originalA2_B1"));
        bListOriginalA2.add(new B("originalA2_B2"));
        bListOriginalA2.add(new B("originalA2_B3"));
        bListOriginalA2.add(new B("originalA2_B4"));

        A originalA2 = new A("originalA2", bListOriginalA2);

        List<B> bListOriginalA3 = new ArrayList<B>();
        bListOriginalA3.add(new B("originalA3_B1"));
        bListOriginalA3.add(new B("originalA3_B2"));
        bListOriginalA3.add(new B("originalA3_B3"));
        bListOriginalA3.add(new B("originalA3_B4"));

        A originalA3 = new A("originalA3", bListOriginalA3);

        originalA.add(originalA1);
        originalA.add(originalA2);
        originalA.add(originalA3);


        List<B> bListNewA1 = new ArrayList<B>();
        bListNewA1.add(new B("originalA1_B1"));
        bListNewA1.add(new B("originalA1_B2"));
        bListNewA1.add(new B("originalA1_B3"));
        bListNewA1.add(new B("originalA1_B4"));

        A newA1 = new A("originalA1", bListNewA1);

        List<B> bListNewA2 = new ArrayList<B>();
        bListNewA2.add(new B("originalA2_B1"));
        bListNewA2.add(new B("originalA2_B3"));
        bListNewA2.add(new B("originalA2_B4"));
        bListNewA2.add(new B("originalA2_B2"));

        A newA2 = new A("originalA2", bListNewA2);

        List<B> bListNewA3 = new ArrayList<B>();
        bListNewA3.add(new B("originalA3_B1"));
        bListNewA3.add(new B("originalA3_B2"));
        bListNewA3.add(new B("originalA3_B5"));
        bListNewA3.add(new B("originalA3_B4"));

        A newA3 = new A("originalA3", bListNewA3);

        List<B> bListNewA4 = new ArrayList<B>();
        bListNewA4.add(new B("A4_B1"));
        bListNewA4.add(new B("A4_B2"));
        bListNewA4.add(new B("A4_B3"));
        bListNewA4.add(new B("A4_B4"));

        A newA4 = new A("originalA4", bListNewA4);

        newA.add(newA1);
        newA.add(newA2);
        newA.add(newA3);
        newA.add(newA4);

        List<A> result = newA.stream()
                .filter(not(new HashSet<A>(originalA)::contains))
                .collect(Collectors.toList());

        A tempA = null;
        B tempB = null;
        List<B> bList = null;
        for (A a : result) {
            if (!containsName(originalA, a.getaName())) {
                originalA.add(a);
            } else {
                tempA = getAIfPresent(originalA, a.getaName());

                if (tempA != null) {

                    bList = a.getbList().stream()
                            .filter(not(new HashSet<B>(tempA.getbList())::contains))
                            .collect(Collectors.toList());

                    if (bList != null) {
                        tempA.getbList().addAll(bList);
                    }
                }
            }
        }

        System.out.println("");
    }

    public static <T> Predicate<T> not(Predicate<T> predicate) {
        return predicate.negate();
    }

    public static boolean containsName(final List<A> list, final String name){
        return list.stream().map(A::getaName).filter(name::equals).findFirst().isPresent();
    }

    public static A getAIfPresent(final List<A> list, final String name) {
        return list.stream().filter(x -> x.getaName().equalsIgnoreCase(name)).findFirst().orElse(null);
    }

}
NuCradle
  • 665
  • 1
  • 9
  • 31