Menu

Home

Frank Lemke

HE-Collection

Independence from hashCode() and equals(Object)


Motivation

Did you ever see a project where classes break the contract between the methods hashCode() and equals(Object) defined in java.lang.Object? This contract says, that equal objects must provide the same hash-code. The criteria can be changed in any custom class by overriding the default implementations in java.lang.Object.

Legacy Projects may need "standard" Corrections

Objects breaking the contract can not be used in a Java-Standard-Collection (package java.util), because the collection will behave unpredictably. You may find this problem in a legacy project, where you are unable (because of risks or workload) to change any implementation of the hashCode() and equals(Object) methods.

Special Use Case may need "special" corrections

Another problem with the contract and Java-Standard-Collections can occur if you need - in a specal use case for example - other criteria for equality than implemented. In such a case, you would need to extend or wrap the values, so that the Java-Standard-Collection will use your use case specific implementations of hashCode() and equals(Object).

Using HE-Collections

Generelly spoken, HE-Collections do a transparent correction for incorrect hashCode() and equals(Object) implementations. That is, for classes that brake the contract, HE-Collections use a standard correction for these types - if available. Standard corrections can be realised by implenting an interface of the HE-Collections. If a special correction is needed, HE-Collections can be configured via the use of constructor-parameters.

Standard Correction

In a legacy project you may need standard corrections as described above. To achive this, any type that needs a standard correction, must implement the HE-Collection interface EqualsAndHashCorrection. This interface defines the methods hashCodeInHeCollection() and equalsInHeCollection(Object), that serve as correction for the incorrect implemented methods hashCode() and equals(Object).

You use a HE-Collection by creatin an instance as follows:
java.util.Set<Type> mySet = new HeHashSet<Type>();
What you get is a set that you can use just as any other Java-Standard-Set. This set - however - may contain "original" (no wrong implementation of the contract) and corrected objects at the same time.

Transparency

The corrections, HE-Collections provide, are independent from generics and transparent to the user. A HeArrayList for example provides the standard methods add(T value) and T get(int index) from java.util.List. You can even add the content of a java.util.Collection into the HeArrayList. But doing the vice versa is risky, because if the HE-Collection contains objects the break the contract, the Java-Standard-Collection that gets them may behave in an undefined way.

Special Correction

The corrections in the HE-Collections are implemented as Switch. A switch implements the methods hashCode() and equals(Object) and delegates calls to them forward to the "corrections" (other methods). Per default a HE-Collection contains to switches in this order:

  • The switch to the methods in the standard-correction-interface EqualsAndHashCorrection.
  • The switch to the java-standard-methods hashCode() and equals(Object) (no correction).

Every switch provides a type. A switch can be used for any object, that is an instance of that type. A HE-Collection holds a list of switches. The first switch found, that is applicable for a given object, will be used.

If you want special corrections, you can "extend" the standard-switch-list by adding your own switches before the standard switches. Or you can start creating your own switch-list from scratch. For more details see in the java-docs AbstractSwitch and AbstractWrapperFactory.

Example:


public class SpecialCorrection 
  extends AbstractSwitch<BrokenType>
{
  @Override
  protected Class<BrokenType> getType()
  {
    return BrokenType.class;
  }

  @Override
  protected int getCorrectHashCode(BrokenType element)
  {
    ...
  }

  @Override
  protected boolean getCorrectEqualsValue(
    BrokenType element, 
    Object other)
  {
    ...
  }
}

public class SpecialFactory extends AbstractWrapperFactory
{

  public WrapperFactory()
  {
    super();
    add(new BrokenTypeSwitch());
  }
}

java.util.Set<BrokenType>mySet = 
  new HeHashSet<BrokenType>();

Remark the possibilities you have with HE-Collections! Even if a base-type breaks the contract, but not all sub-types (overriding the wrong implementations), you can use a HE-Collection that is generically typed to the base-type!

Frank Lemke