Class Equivalence<T>

  • Type Parameters:
    T - type of objects compared by this equivalence
    Direct Known Subclasses:
    StatelessEquivalence

    public abstract class Equivalence<T>
    extends Object
    A strategy for determining whether two instances are considered equivalent.

    This class is inspired and very similar to Guava's Equivalence, with one notable difference: this Equivalence forces the actual implementation to override equals(Object) and hashCode(). Notice these are Equivalence's own equals and hashCode, not the strategy equivalent(Object, Object) and hash(Object) methods. It is needed because, for example, ObjCollection's equality depends on Equivalence equality.

    In most cases, when Equivalence is stateless, you can extend StatelessEquivalence not to bother with implementing these methods. See examples in the documentation to identity and case insensitive equivalences.

    • Constructor Detail

      • Equivalence

        protected Equivalence()
        Constructor for use by subclasses.
    • Method Detail

      • defaultEquality

        public static <T> Equivalence<T> defaultEquality()
        Returns the default, built-in equivalence in Java, driven by Object.equals(Object) and Object.hashCode() methods.
        Type Parameters:
        T - type of objects, needed to compare. Object.equals(java.lang.Object) could be applied to object of any type, so there aren't any constraints over the generic type parameter.
        Returns:
        the built-in Java equality
      • identity

        public static <T> Equivalence<T> identity()
        Returns the equivalence that uses == to compare objects and System.identityHashCode(Object) to compute the hash code. nullableEquivalent(T, T) returns true if a == b, including in the case when a and b are both null.

        This equivalence could be implemented as follows:

        
         final class Identity extends StatelessEquivalence<Object> {
             static final Identity INSTANCE = new Identity();
             private Identity() {}
             @Override
             public boolean equivalent(Object a, Object b) {
                 return a == b;
             }
             @Override
             public int hash(Object t) {
                 return System.identityHashCode(t);
             }
         }
         
        Type Parameters:
        T - type of objects, needed to compare. Identity check could be applied to objects of any type, so there aren't any constraints over the generic type parameter.
        Returns:
        the identity equivalence
      • charSequence

        public static Equivalence<CharSequence> charSequence()
        Returns the equivalence that compares CharSequences by their contents.

        This equivalence could be implemented as follows (actual implementation, of cause, is more efficient and doesn't allocate garbage objects):

        
         final class CharSequenceEquivalence extends StatelessEquivalence<CharSequence> {
             static final CharSequenceEquivalence INSTANCE = new CharSequenceEquivalence();
             private CharSequenceEquivalence() {}
             @Override
             public boolean equivalent(CharSequence a, CharSequence b) {
                 return a.toString().equals(b.toString());
             }
             @Override
             public int hash(CharSequence cs) {
                 return cs.toString().hashCode();
             }
         }
        Returns:
        the CharSequence equivalence
      • caseInsensitive

        public static Equivalence<String> caseInsensitive()
        Returns the String equivalence that uses String.equalsIgnoreCase(java.lang.String) to compare strings.

        This equivalence could be implemented as follows:

        
         final class CaseInsensitive extends StatelessEquivalence<String> {
             static final CaseInsensitive INSTANCE = new CaseInsensitive();
             private CaseInsensitive() {}
             @Override
             public boolean equivalent(String a, String b) {
                 return a.equalsIgnoreCase(b);
             }
             @Override
             public int hash(String s) {
                 return s.toLowerCase().hashCode();
             }
         }
         
        Returns:
        the case-insensitive String equivalence
      • entryEquivalence

        public static <K,​V> Equivalence<Map.Entry<K,​V>> entryEquivalence​(Equivalence<K> keyEquivalence,
                                                                                     Equivalence<V> valueEquivalence)
        Returns a Map.Entry equivalence for the given key and value equivalences.
        Type Parameters:
        K - the entry key type
        V - the entry value type
        Parameters:
        keyEquivalence - the entry key equivalence
        valueEquivalence - the entry value equivalence
        Returns:
        a Map.Entry equivalence for the given key and value equivalences
      • nullableEquivalent

        public boolean nullableEquivalent​(@Nullable
                                          T a,
                                          @Nullable
                                          T b)
        Returns true if a and b are considered equivalent, false otherwise. a and b both might be null.

        If the implementation overrides this method, it must ensure that it returns true if both the given objects are nulls and false, if only one of them is null. If both a and b are non-null, this method should perform just the same as equivalent(Object, Object) method does.

        Parameters:
        a - the first object to compare
        b - the second object to compare
        Returns:
        true if a and b are considered equivalent, false otherwise
      • equivalent

        public abstract boolean equivalent​(T a,
                                           T b)
        Returns true if a and b are considered equivalent, false otherwise. a and b are assumed to be non-null.

        This method implements an equivalence relation on object references:

        • It is reflexive: for any reference x, equivalent(x, x) returns true.
        • It is symmetric: for any references x and y, equivalent(x, y) == equivalent(y, x).
        • It is transitive: for any references x, y, and z, if equivalent(x, y) returns true and equivalent(y, z) returns true, then equivalent(x, z) returns true.
        • It is consistent: for any references x and y, multiple invocations of equivalent(x, y) consistently return true or consistently return false (provided that neither x nor y is modified).

        This method is called by nullableEquivalent(Object, Object).

        Parameters:
        a - the first object to compare
        b - the second object to compare
        Returns:
        true if a and b are considered equivalent, false otherwise
      • nullableHash

        public int nullableHash​(@Nullable
                                T t)
        Returns a hash code for the given object. The t object might be null.

        If the implementation overrides this method, it must ensure that it returns 0 if the given object is null. Otherwise this method should perform just the same as hash(Object) method does.

        Parameters:
        t - the object to compute hash code for
        Returns:
        a hash code for the given object
      • hash

        public abstract int hash​(T t)
        Returns a hash code for the given object. The t object is assumed to be non-null.

        This method has the following properties:

        • It is consistent: for any reference x, multiple invocations of hash(x) consistently return the same value provided x remains unchanged according to the definition of the equivalence. The hash need not remain consistent from one execution of an application to another execution of the same application.
        • It is distributable across equivalence: for any references x and y, if equivalent(x, y), then hash(x) == hash(y). It is not necessary that the hash be distributable across inequivalence. If equivalence(x, y) is false, hash(x) == hash(y) may still be true.

        This method is called by nullableHash(Object).

        Parameters:
        t - the object to compute hash code for
        Returns:
        a hash code for the given object
      • equals

        public abstract boolean equals​(Object o)

        This method is made abstract to force the Equivalence implementation to override it. This is needed because, for example, ObjObjMap's equality depends on the equality of key and value Equivalence objects, configured for a map.

        Overrides:
        equals in class Object
      • hashCode

        public abstract int hashCode()

        This method is made abstract to force the Equivalence implementation to override it. This is needed because equals(Object) is also needed to be overridden.

        Overrides:
        hashCode in class Object