With Java 8:
Comparator.comparing((Person p)->p.firstName)
.thenComparing(p->p.lastName)
.thenComparingInt(p->p.age);
If you have accessor methods:
Comparator.comparing(Person::getFirstName)
.thenComparing(Person::getLastName)
.thenComparingInt(Person::getAge);
If a class implements Comparable then such comparator may be used in compareTo method:
@Override
public int compareTo(Person o){
return Comparator.comparing(Person::getFirstName)
.thenComparing(Person::getLastName)
.thenComparingInt(Person::getAge)
.compare(this, o);
}
With Java 8:
Comparator.comparing((Person p)->p.firstName)
.thenComparing(p->p.lastName)
.thenComparingInt(p->p.age);
If you have accessor methods:
Comparator.comparing(Person::getFirstName)
.thenComparing(Person::getLastName)
.thenComparingInt(Person::getAge);
If a class implements Comparable then such comparator may be used in compareTo method:
@Override
public int compareTo(Person o){
return Comparator.comparing(Person::getFirstName)
.thenComparing(Person::getLastName)
.thenComparingInt(Person::getAge)
.compare(this, o);
}
You should implement Comparable <Person>. Assuming all fields will not be null (for simplicity sake), that age is an int, and compare ranking is first, last, age, the compareTo method is quite simple:
public int compareTo(Person other) {
int i = firstName.compareTo(other.firstName);
if (i != 0) return i;
i = lastName.compareTo(other.lastName);
if (i != 0) return i;
return Integer.compare(age, other.age);
}
[JUnits] How to compare 2 complex objects without overriding toequals/hashcode methods?
Java CompareTo Sorting - Sorting Multiple Fields for Same Object
How to recursively compare two objects with different field names but same values
java - How to compare two objects and get the changed fields - Stack Overflow
Videos
The “different member variables” is irrelevant. It’s an implementation detail. What you need is a set of rules which of two people comes first.
You could for example sort by family name, then given name, then date of birth, and if these are all three equal, take the name of the school, university or company (which will be different member variables) and compare them as strings. If that is equal, you might have student and employee ids, and the student ids might be unique, and the employee ids might be unique, but student and employee ids might be the same. So you could sort then students first ordered by id, followed by employees sorted by id, if you might sort by if first if student and employee ids are comparable.
(University or school and employer might be the same, because universities are also employers).
Comparing objects with different fields sounds like bad polymorphic design, whether it's Java or any other OOP language:
- If your comparator needs to know the precise subtype of an object to do the comparison, you mess-up with the the open-closed principle, since for every new subclassing, you'd potentially need to modify the comparator to select the relevant fields.
- If your comparator needs uses reflexion to find on its own the relevant fields to compare, you indirectly mess up with the principle of encapsulation, since you create a hidden requirement that information to be compared must be in some predetermined field.
If you want to sort People properly in a clean polymorphic design:
- you need to rely either on a field, available for any kind of
People, includingStudent, or - you may call some function/transformation that provides a unique value (e.g. a string) that allows to sort any
People.PeopleandStudentmay then just use a different transformation that will be passed to the comparator; Or - you only sort among homogeneous subtypes.
Is there any easier way to compare all the attributes of 2 complex objects without overriding toequals method? The complex object which i am working on has nested objects and the level goes down pretty deep and refactoring of the same is not currently in my scope.
I need to write my test cases to compare 2 such objects and also reporting which all fields are not equal in case the junit test cases fails.
Well i found a solution as below :
private static List<String> getDifference(Object s1, Object s2) throws IllegalAccessException {
List<String> values = new ArrayList<>();
for (Field field : s1.getClass().getDeclaredFields()) {
field.setAccessible(true);
Object value1 = field.get(s1);
Object value2 = field.get(s2);
if (value1 != null && value2 != null) {
if (!Objects.equals(value1, value2)) {
values.add(String.valueOf(field.getName()+": "+value1+" -> "+value2));
}
}
}
return values;
}
You may use javers library for this.
<groupId>org.javers</groupId>
<artifactId>javers-core</artifactId>
POJO:
public class Person {
private Integer id;
private String name;
// standard getters/constructors
}
Usage:
@Test
public void givenPersonObject_whenApplyModificationOnIt_thenShouldDetectChange() {
// given
Javers javers = JaversBuilder.javers().build();
Person person = new Person(1, "Michael Program");
Person personAfterModification = new Person(1, "Michael Java");
// when
Diff diff = javers.compare(person, personAfterModification);
// then
ValueChange change = diff.getChangesByType(ValueChange.class).get(0);
assertThat(diff.getChanges()).hasSize(1);
assertThat(change.getPropertyName()).isEqualTo("name");
assertThat(change.getLeft()).isEqualTo("Michael Program");
assertThat(change.getRight()).isEqualTo("Michael Java");
}
Plus other use cases are supported as well.
I think if there were a one-size-fits-all way to compare objects, then Java (or any language) would make it the default, no?
Equality - like hashing and comparison - should be fast, simple and most importantly referentially transparent.
In the end, the question of what exactly the equals method is for is important. If you don't know, then just leave it as is. Physical comparison makes sense in many use cases, particularly if you have control over when objects are created vs. when they are reused.
Also, rather than trying to design comparison functions meaningful for all cases, write code using things like Comparator and let calling code inject whatever makes the most sense at the call site.
First of all, there are two kinds of classes:
- Classes with "value-type" semantics.
- Classes with "reference" semantics.
You can google these terms to find information about precisely what they mean, and how they differ from each other, for your language of choice.
Every single class with "value-type" semantics must have an equals() method, and this method must take into consideration every single one of its fields, or at least give the illusion that it does so. If it is expensive, bite the bullet and spend clock cycles lavishly, because it is necessary.
Classes with "reference" semantics do not need an equals() method. I would not go as far as to say that they should not have such a method, but if they are to have one, it should be thought of as a utility / helper method which is going to be used in some weird way which is unrelated to the original intent of equals. That's because under normal circumstances, these objects are compared by reference, not by value.
Reference equality is checked using the == operator in Java, while in C#, where the == operator may be overloaded, we use System.Object.ReferenceEquals(). (Though, again, you are not supposed to overload the == operator of a reference-semantics class.)
Incidentally, the kinds of classes that are complex and expensive to check for equality usually tend to be classes with reference semantics, so they do not need an equals() method. And generally, if you have any doubts or second thoughts as to how the equals() method should be implemented, this is a good indication that what you have in your hands is a reference-semantics class, not a value-type-semantics class.
As for hashCode(), the only objects that should implement it are objects that not only have value semantics, but are also immutable. This is because hash containers generally obtain the hash code of an object once, and then cache it for as long as the object resides in the container, so if the object undergoes a mutation, the contents of the object will be in conflict with the cached hash code. It is a very common newbie bug to use a non-immutable object as a key to a hashmap, and a very hard one to track down unless you know where to look first: the immutability of the class used as the key.
So, every class which is not immutable should have a hashCode() method coded as follows:
public int hashCode()
{
assert false; //OMG! hashCode() was invoked on mutable object!
}
So, since only immutable classes should implement hashCode(), ensuring that it works in a way which is in agreement with how equals() works is pretty straightforward, and probably a lot more simple than what you may have feared.