6

I have two DTO objects like following, please, note that I am using lombok to avoid boilerplate code.

DtoA

import lombok.Data;
import java.util.List;

@Data
public class DtoA {

    private String name;
    private String number;
    private List<String> aList;
}

DtoB

import lombok.Data;
import java.util.List;

@Data
public class DtoB {
    private String name;
    private String phone;
    private List<String> bList;
}

I want to compare specific fields of both objects so i have created an adaptor kind of object like following

DtoAdapter

import lombok.Data;
import java.util.List;

@Data
public class DtoAdapter {
    private String nameText;
    private List<String> xList;
}

Following is my Test class with main method where i am trying to do comparison This comparison is failing because of aList and bList contains strings in different orders. I want to compare the contents of the list without worrying about their order.

Test

import junit.framework.Assert;
import java.util.ArrayList;
import java.util.List;

public class Test {
    public static void main(String[] args) {
        DtoA a = new DtoA();
        List<String> aList = new ArrayList<>();
        aList.add("x"); aList.add("y"); aList.add("z");
        a.setName("abc"); a.setNumber("123"); a.setAList(aList);

        DtoB b = new DtoB();
        List<String> bList = new ArrayList<>();
        bList.add("z"); bList.add("x"); bList.add("y");
        b.setName("abc"); b.setPhone("123"); b.setBList(bList);

        DtoAdapter a1 = new DtoAdapter();
        a1.setNameText(a.getName()); a1.setXList(a.getAList());

        DtoAdapter b1 = new DtoAdapter();
        b1.setNameText(b.getName()); b1.setXList(b.getBList());

        // comparision failing because of lists contains string in different orders
        Assert.assertEquals(a1, b1);
    }
}

Note: I have tried writing compareTo (by implementing comparable interface into DtoAdapter class) But I couldn't write comparison of two lists with compareTo method like following

DtoAdapter with comparable interface

import lombok.Data;
import java.util.List;

@Data
public class DtoAdapter implements Comparable<DtoAdapter>{
    private String nameText;
    private List<String> xList;

    @Override
    public int compareTo(DtoAdapter o) {
        return this.getNameText().compareTo(o.getNameText());
        // how to compare this.getXList() and o.getXList() with compareTo?
    }
}
axnet
  • 5,146
  • 3
  • 25
  • 45

5 Answers5

5

What you are looking forward to is not extending a Comparable but an overriden equals implementation for the class(along with hashcode of course).

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    DtoAdapter that = (DtoAdapter) o;
    // following line of code specifically
    return Objects.equals(nameText, that.nameText) &&
            that.getXList().containsAll(xList) && xList.containsAll(that.getXList());
}

Further from the documentation of Comparable, the interface is primarily used for ordering elements and not for equality comparison :

This interface imposes a total ordering on the objects of each class that implements it. This ordering is referred to as the class's natural ordering, and the class's compareTo method is referred to as its natural comparison method.

Naman
  • 27,789
  • 26
  • 218
  • 353
  • 2
    plus one; but two things: 1) if you implement `Comparable`, you are strongly encouraged to override equals/hashCode 2) I wish there would be an interface `EqualsAndHashCode` sometimes... I sometimes wanna take only these types in a method, and I wish I could say `List extends EqualsAndHashCode>`.... – Eugene Oct 11 '19 at 14:55
1

Like the answer from @Naman, you can should override the method equals but try to first compare the lists' length because if have the same length, you only need to check if the elements of the first list are in the other.

@Override
public boolean equals(Object o) {
    if (o == this) {
        return true;
    } 

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

    DtoAdapter that = (DtoAdapter) o;

    return Objects.equals(nameText, that.nameText) && that.getXList().size() == xList.size() && that.getXList().containsAll(xList);

}
axnet
  • 5,146
  • 3
  • 25
  • 45
0

I want to compare the contents of the list without worrying about their order.

I've implemented a utility method which allows to compare List ignoring the order.

The library is available from Maven Central:

<dependency>
  <groupId>org.softsmithy.lib</groupId>
  <artifactId>softsmithy-lib-core</artifactId>
  <version>2.0</version>
</dependency>

The source code is available from GitHub.

Also note the best practice to implement Comparable: https://stackoverflow.com/a/46287029/506855

Puce
  • 37,247
  • 13
  • 80
  • 152
0

You can @Override the method equals in class DtoAdapter.

@Override
public boolean equals(Object o) {
    if (o == this) {
        return true;
    } 

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

    DtoAdapter c = (DtoAdapter) o;

    if(c.getXList().size() != xList.size()) {
        return false;
    }

    // write logic for object comparison

    return c.getNameText().equals(nameText);
}

If you don't want to take care of how list is ordered, you can use the method removeAll().

First, you check if the lists have the same size. Then, you remove all the elements from one list using the other list as parameter.

c.getXList().removeAll(xList);

After you remove all elements, you check if the first list has any element left. If not, it means that the list contains the same elements.

  • You shouldn't remove elements from either collection. You just need to check if both lists contain the same elements, no matter the order. Why removing? A query method such as `equals` shouldn't have side-effects – fps Oct 11 '19 at 13:25
0

You can consider you use compareTo() of String class like the following code.

@Override
public int compareTo(DtoAdapter o) {
    int cmpName = this.getNameText().compareTo(o.getNameText());
    // how to compare this.getXList() and o.getXList() with compareTo?

    int cmpList = 0;
    if(this.list.size() != o.list.size()) {
        cmpList = this.list.size() - o.list.size();
    } else {
        for(int i = 0; i < list.size(); i++) {
            int v = list.get(i).compareTo(o.list.get(i));
            if(v != 0) {
                cmpList = v;
                break;
            }
        }
    }

    return cmpName + cmpList;
}
libliboom
  • 532
  • 5
  • 17