Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a Java utility to do a deep comparison of two objects? [closed]

How to "deep"-compare two objects that do not implement the equals method based on their field values in a test?


Original Question (closed because lack of precision and thus not fulfilling SO standards), kept for documentation purposes:

I'm trying to write unit tests for a variety of clone() operations inside a large project and I'm wondering if there is an existing class somewhere that is capable of taking two objects of the same type, doing a deep comparison, and saying if they're identical or not?

like image 675
Uri Avatar asked Sep 19 '09 17:09

Uri


People also ask

How do you check if 2 objects have the same content in Java?

If the two objects have the same values, equals() will return true . In the second comparison, equals() checks to see whether the passed object is null, or if it's typed as a different class. If it's a different class then the objects are not equal.

How do you compare two sets of objects in Java?

The equals() method of java. util. Set class is used to verify the equality of an Object with a Set and compare them. The method returns true if the size of both the sets are equal and both contain the same elements.

Can you compare objects in Java with ==?

In Java, the == operator compares that two references are identical or not. Whereas the equals() method compares two objects. Objects are equal when they have the same state (usually comparing variables). Objects are identical when they share the class identity.

What is deep comparison in Java?

The deepEquals method of the Objects class in Java is a static method used to check whether two given objects are deep equals. Two null values are always deeply equal. If both the objects passed are arrays, then Arrays. deepEquals is used to check equality.


6 Answers

Unitils has this functionality:

Equality assertion through reflection, with different options like ignoring Java default/null values and ignoring order of collections

like image 73
Wolfgang Avatar answered Oct 05 '22 22:10

Wolfgang


I love this question! Mainly because it is hardly ever answered or answered badly. It's like nobody has figured it out yet. Virgin territory :)

First off, don't even think about using equals. The contract of equals, as defined in the javadoc, is an equivalence relation (reflexive, symmetric, and transitive), not an equality relation. For that, it would also have to be antisymmetric. The only implementation of equals that is (or ever could be) a true equality relation is the one in java.lang.Object. Even if you did use equals to compare everything in the graph, the risk of breaking the contract is quite high. As Josh Bloch pointed out in Effective Java, the contract of equals is very easy to break:

"There is simply no way to extend an instantiable class and add an aspect while preserving the equals contract"

Besides what good does a boolean method really do you anyway? It'd be nice to actually encapsulate all the differences between the original and the clone, don't you think? Also, I'll assume here that you don't want to be bothered with writing/maintaining comparison code for each object in the graph, but rather you're looking for something that will scale with the source as it changes over time.

Soooo, what you really want is some kind of state comparison tool. How that tool is implemented is really dependent on the nature of your domain model and your performance restrictions. In my experience, there is no generic magic bullet. And it will be slow over a large number of iterations. But for testing the completeness of a clone operation, it'll do the job pretty well. Your two best options are serialization and reflection.

Some issues you will encounter:

  • Collection order: Should two collections be considered similar if they hold the same objects, but in a different order?
  • Which fields to ignore: Transient? Static?
  • Type equivalence: Should field values be of exactly the same type? Or is it ok for one to extend the other?
  • There's more, but I forget...

XStream is pretty fast and combined with XMLUnit will do the job in just a few lines of code. XMLUnit is nice because it can report all the differences, or just stop at the first one it finds. And its output includes the xpath to the differing nodes, which is nice. By default it doesn't allow unordered collections, but it can be configured to do so. Injecting a special difference handler (Called a DifferenceListener) allows you to specify the way you want to deal with differences, including ignoring order. However, as soon as you want to do anything beyond the simplest customization, it becomes difficult to write and the details tend to be tied down to a specific domain object.

My personal preference is to use reflection to cycle through all the declared fields and drill down into each one, tracking differences as I go. Word of warning: Don't use recursion unless you like stack overflow exceptions. Keep things in scope with a stack (use a LinkedList or something). I usually ignore transient and static fields, and I skip object pairs that I've already compared, so I don't end up in infinite loops if someone decided to write self-referential code (However, I always compare primitive wrappers no matter what, since the same object refs are often reused). You can configure things up front to ignore collection ordering and to ignore special types or fields, but I like to define my state comparison policies on the fields themselves via annotations. This, IMHO, is exactly what annotations were meant for, to make meta data about the class available at runtime. Something like:

 @StatePolicy(unordered=true, ignore=false, exactTypesOnly=true) private List<StringyThing> _mylist; 

I think this is actually a really hard problem, but totally solvable! And once you have something that works for you, it is really, really, handy :)

So, good luck. And if you come up with something that's just pure genius, don't forget to share!

like image 29
Kevin C Avatar answered Oct 05 '22 23:10

Kevin C


In AssertJ, you can do:

Assertions.assertThat(expectedObject).isEqualToComparingFieldByFieldRecursively(actualObject);

Probably it won't work in all cases, however it will work in more cases that you'd think.

Here's what the documentation says:

Assert that the object under test (actual) is equal to the given object based on recursive a property/field by property/field comparison (including inherited ones). This can be useful if actual's equals implementation does not suit you. The recursive property/field comparison is not applied on fields having a custom equals implementation, i.e. the overridden equals method will be used instead of a field by field comparison.

The recursive comparison handles cycles. By default floats are compared with a precision of 1.0E-6 and doubles with 1.0E-15.

You can specify a custom comparator per (nested) fields or type with respectively usingComparatorForFields(Comparator, String...) and usingComparatorForType(Comparator, Class).

The objects to compare can be of different types but must have the same properties/fields. For example if actual object has a name String field, it is expected the other object to also have one. If an object has a field and a property with the same name, the property value will be used over the field.

like image 28
Vlad Dinulescu Avatar answered Oct 05 '22 22:10

Vlad Dinulescu


See DeepEquals and DeepHashCode() within java-util: https://github.com/jdereg/java-util

This class does exactly what the original author requests.

like image 26
John DeRegnaucourt Avatar answered Oct 05 '22 23:10

John DeRegnaucourt


Override The equals() Method

You can simply override the equals() method of the class using the EqualsBuilder.reflectionEquals() as explained here:

 public boolean equals(Object obj) {
   return EqualsBuilder.reflectionEquals(this, obj);
 }
like image 28
Loukan ElKadi Avatar answered Oct 05 '22 22:10

Loukan ElKadi


Just had to implement comparison of two entity instances revised by Hibernate Envers. I started writing my own differ but then found the following framework.

https://github.com/SQiShER/java-object-diff

You can compare two objects of the same type and it will show changes, additions and removals. If there are no changes, then the objects are equal (in theory). Annotations are provided for getters that should be ignored during the check. The frame work has far wider applications than equality checking, i.e. I am using to generate a change-log.

Its performance is OK, when comparing JPA entities, be sure to detach them from the entity manager first.

like image 23
Ryan Avatar answered Oct 05 '22 22:10

Ryan