3

I need to obtain a set difference between two sets/lists but the objects in these sets are constructed differently. I would like the set difference be calculated on only one field of these objects, how can I do this?

POJO o1 = new POJO();
o1.setName("Bean");
o1.setLocation("Boston");

POJO o2 = new POJO();
o2.setName("Bagel");
o2.setLocation("NYC");

POJO o3 = new POJO();
o3.setName("Bean");

List<POJO> p1 = new ArrayList<>();
p1.add(o1);
p1.add(o2);

List<POJO> p2 = new ArrayList<>();
p2.add(o3);

Collection<POJO> subList = CollectionUtils.subtract(p1, p2);
System.out.println("subList: "+subList.toString());

The output I see is : subList: [POJO(name=Bean)] i.e. o3. But for my use case, o1 and o3 are the same - how can I achieve this? I overrode the default equals() method as well but i don't think it is invoked.

Olaf Kock
  • 46,930
  • 8
  • 59
  • 90
Saturnian
  • 1,686
  • 6
  • 39
  • 65
  • 1
    I had to search to see where `CollectonUtils` was from. It appears to be from `Apache`. An overloaded version takes a predicate as the third argument for determining which values to subtract. Please update your tags to include the appropriate third party API reference. – WJS Jan 23 '20 at 20:29
  • 1
    https://stackoverflow.com/questions/49324437/is-there-a-simple-way-in-java-to-get-the-difference-between-two-collections-usin/49332217#49332217 – Donald Raab Jan 24 '20 at 02:18

2 Answers2

4

You can achieve this with the help of overloaded subtract with predicate.

  1. Override hashcode and equals in POJO class, (and toString for clean output)

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        POJO pojo = (POJO) o;
        return Objects.equals(name, pojo.name);
    }
    
    @Override
    public int hashCode() {
        return name.hashCode();
    }
    
  2. Use Predicate with set as

    Collection<POJO> subList = CollectionUtils.subtract(p1, p2, new Predicate<POJO>() {
        @Override
        public boolean evaluate(POJO object) {
            return p1.contains(object); // might need to make p1 final
        }
    });
    

Result:

subList: [ Bagel ]

Note: Apache common does not support anything like Java 8 BiPredicate

Pavneet_Singh
  • 36,884
  • 5
  • 53
  • 68
4

You can use a UnifiedSetWithHashingStrategy from Eclipse Collections:

POJO o1 = new POJO();
o1.setName("Bean");
o1.setLocation("Boston");

POJO o2 = new POJO();
o2.setName("Bagel");
o2.setLocation("NYC");

POJO o3 = new POJO();
o3.setName("Bean");

HashingStrategy<POJO> strategy = HashingStrategies.fromFunction(POJO::getName);

MutableSet<POJO> set1 =
        HashingStrategySets.mutable.with(strategy, o1, o2);
MutableSet<POJO> set2 =
        HashingStrategySets.mutable.with(strategy, o3);
MutableSet<POJO> difference = set1.difference(set2);

Assert.assertEquals("[POJO(name=Bagel)]", difference.toString());

This answer explains how UnifiedSetWithHashingStrategy works in more detail.

Note: I am a committer for Eclipse Collections.

Donald Raab
  • 6,458
  • 2
  • 36
  • 44