Class DeepEquals

java.lang.Object
com.cedarsoftware.util.DeepEquals

public class DeepEquals extends Object
Test two objects for equivalence with a 'deep' comparison. This will traverse the Object graph and perform either a field-by-field comparison on each object (if no .equals() method has been overridden from Object), or it will call the customized .equals() method if it exists. This method will allow object graphs loaded at different times (with different object ids) to be reliably compared. Object.equals() / Object.hashCode() rely on the object's identity, which would not consider two equivalent objects necessarily equals. This allows graphs containing instances of Classes that did not overide .equals() / .hashCode() to be compared. For example, testing for existence in a cache. Relying on an object's identity will not locate an equivalent object in a cache.

This method will handle cycles correctly, for example A->B->C->A. Suppose a and a' are two separate instances of A with the same values for all fields on A, B, and C. Then a.deepEquals(a') will return true. It uses cycle detection storing visited objects in a Set to prevent endless loops.

Numbers will be compared for value. Meaning an int that has the same value as a long will match. Similarly, a double that has the same value as a long will match. If the flag "ALLOW_STRING_TO_MATCH_NUMBERS" is passed in the options are set to true, then Strings will be converted to BigDecimal and compared to the corresponding non-String Number. Two Strings will not be compared as numbers, however.
Author:
John DeRegnaucourt ([email protected])
Copyright (c) Cedar Software LLC

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

License

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
  • Field Details

  • Method Details

    • deepEquals

      public static boolean deepEquals(Object a, Object b)
      Compare two objects with a 'deep' comparison. This will traverse the Object graph and perform either a field-by-field comparison on each object (if not .equals() method has been overridden from Object), or it will call the customized .equals() method if it exists. This method will allow object graphs loaded at different times (with different object ids) to be reliably compared. Object.equals() / Object.hashCode() rely on the object's identity, which would not consider to equivalent objects necessarily equals. This allows graphs containing instances of Classes that did no overide .equals() / .hashCode() to be compared. For example, testing for existence in a cache. Relying on an objects identity will not locate an object in cache, yet relying on it being equivalent will.

      This method will handle cycles correctly, for example A->B->C->A. Suppose a and a' are two separate instances of the A with the same values for all fields on A, B, and C. Then a.deepEquals(a') will return true. It uses cycle detection storing visited objects in a Set to prevent endless loops.
      Parameters:
      a - Object one to compare
      b - Object two to compare
      Returns:
      true if a is equivalent to b, false otherwise. Equivalent means that all field values of both subgraphs are the same, either at the field level or via the respectively encountered overridden .equals() methods during traversal.
    • deepEquals

      public static boolean deepEquals(Object a, Object b, Map<String,?> options)
      Compare two objects with a 'deep' comparison. This will traverse the Object graph and perform either a field-by-field comparison on each object (if not .equals() method has been overridden from Object), or it will call the customized .equals() method if it exists. This method will allow object graphs loaded at different times (with different object ids) to be reliably compared. Object.equals() / Object.hashCode() rely on the object's identity, which would not consider to equivalent objects necessarily equals. This allows graphs containing instances of Classes that did no overide .equals() / .hashCode() to be compared. For example, testing for existence in a cache. Relying on an objects identity will not locate an object in cache, yet relying on it being equivalent will.

      This method will handle cycles correctly, for example A->B->C->A. Suppose a and a' are two separate instances of the A with the same values for all fields on A, B, and C. Then a.deepEquals(a') will return true. It uses cycle detection storing visited objects in a Set to prevent endless loops.
      Parameters:
      a - Object one to compare
      b - Object two to compare
      options - Map options for compare. With no option, if a custom equals() method is present, it will be used. If IGNORE_CUSTOM_EQUALS is present, it will be expected to be a Set of classes to ignore. It is a black-list of classes that will not be compared using .equals() even if the classes have a custom .equals() method present. If it is and empty set, then no custom .equals() methods will be called.
      Returns:
      true if a is equivalent to b, false otherwise. Equivalent means that all field values of both subgraphs are the same, either at the field level or via the respectively encountered overridden .equals() methods during traversal.
    • isContainerType

      public static boolean isContainerType(Object o)
    • hasCustomEquals

      public static boolean hasCustomEquals(Class<?> c)
      Determine if the passed in class has a non-Object.equals() method. This method caches its results in static ConcurrentHashMap to benefit execution performance.
      Parameters:
      c - Class to check.
      Returns:
      true, if the passed in Class has a .equals() method somewhere between itself and just below Object in it's inheritance.
    • deepHashCode

      public static int deepHashCode(Object obj)
      Get a deterministic hashCode (int) value for an Object, regardless of when it was created or where it was loaded into memory. The problem with java.lang.Object.hashCode() is that it essentially relies on memory location of an object (what identity it was assigned), whereas this method will produce the same hashCode for any object graph, regardless of how many times it is created.

      This method will handle cycles correctly (A->B->C->A). In this case, Starting with object A, B, or C would yield the same hashCode. If an object encountered (root, sub-object, etc.) has a hashCode() method on it (that is not Object.hashCode()), that hashCode() method will be called and it will stop traversal on that branch.
      Parameters:
      obj - Object who hashCode is desired.
      Returns:
      the 'deep' hashCode value for the passed in object.
    • hasCustomHashCode

      public static boolean hasCustomHashCode(Class<?> c)
      Determine if the passed in class has a non-Object.hashCode() method. This method caches its results in static ConcurrentHashMap to benefit execution performance.
      Parameters:
      c - Class to check.
      Returns:
      true, if the passed in Class has a .hashCode() method somewhere between itself and just below Object in it's inheritance.