Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom hashcode/equals operation for HashMap

Is there a an implementation of the HashMap class (or Map interface) that will allow me to use alternate hashcode and equals operations... Similar to how collections of the same type can be sorted in multiple ways using the Comparator in Collections.sort(list, comparator).

I would like to avoid if possible, creating a key wrapper providing the desired hashcode and equals operations.


In my case, one of the scenarios why I need something like this:

In my web application, for each request, I load the Location/ISP and other data. In different parts of code (in my service and repository layers) I have "minimized" caches specific to its requirement.

Here is a simplified code example:

class GeoIpData{
    private String countryName;
    private String state;
    private String city;
    private String isp;
    @Override
    public int hashCode() {
        //countryName hashCode
        //state hashCode
        //city hashCode
        //isp hashCode
    }
    @Override
    public boolean equals(Object obj) {
        // compare countryName
        // compare state
        // compare city
        // compare isp
    }
}

 Map<GeoIpData,#Type1> fullCache = ... //This cache needs to be unique per countryName,state,city and isp
 Map<GeoIpData,#Type2> countryCache = ... //This cache needs to be unique per countryName
 Map<GeoIpData,#Type2> ispCache = ... //This cache needs to be unique per countryName,isp

To achieve this the above 3 maps need 3 different hashcode and equals methods.

fullCache:
hashCode -> GeoIpData.hashCode();
equals   -> GeoIpData.equals(Object obj);

countryCache:
hashCode -> {countryName hashCode }
equals   -> {compare countryName }

ispCache:
hashCode -> {countryName hashCode & isp hashCode }
equals   -> {compare countryName & compare isp hashCode }
like image 809
NeilA Avatar asked Mar 03 '13 10:03

NeilA


People also ask

How hashCode () and equals () method are used in HashMap?

In HashMap, hashCode() is used to calculate the bucket and therefore calculate the index. equals() method: This method is used to check whether 2 objects are equal or not. This method is provided by the Object class. You can override this in your class to provide your implementation.

What is the need to override the hashCode and equals method in custom HashMap?

You must override hashCode() in every class that overrides equals(). Failure to do so will result in a violation of the general contract for Object. hashCode(), which will prevent your class from functioning properly in conjunction with all hash-based collections, including HashMap, HashSet, and Hashtable.

Can we use custom key in HashMap?

We can conclude that to use a custom class for a key, it is necessary that hashCode() and equals() are implemented correctly. To put it simply, we have to ensure that the hashCode() method returns: the same value for the object as long as the state doesn't change (Internal Consistency)

How does get () method of HashMap works if two keys have the same hashCode?

If two keys are the same ( equals() returns true when you compare them), their hashCode() method must return the same number. If keys violate this, then keys that are equal might be stored in different buckets, and the hashmap would not be able to find key-value pairs (because it's going to look in the same bucket).


2 Answers

GNU Trove allows you to provide specific TObjectHashingStrategy with your own hash and equals functions for TCustomHashMap.

like image 97
Mikhail Avatar answered Sep 24 '22 01:09

Mikhail


Initial Java API bounded equals / hashCode behavior to type (class) because they didn't have lambdas back those days.

In order to reuse existing API / implementations - make ephemeral keys:

public CountryKey {
    private String country;
    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof CountryKey)) { return false; }
        CountryKey that = (CountryKey) obj;
        return this.country.equals(that.country);
    }
    @Override int hashCode() {
         return country.hashCode();
    }
}

or wrappers:

public CountryKey {
    private GeoIpData holder;
    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof CountryKey)) { return false; }
        CountryKey that = (CountryKey) obj;
        return this.holder.getCountry().equals(that.holder.getCountry());
    }
    @Override int hashCode() {
         return holder.getCountry().hashCode();
    }
}

and write helper constructors for keys:

public class GeoIpData {
     public CountryKey buildCountryKey() {
          return new CountryKey(this.country);
     }
}
like image 26
gavenkoa Avatar answered Sep 27 '22 01:09

gavenkoa