Maurice
Maurice

Reputation: 7381

Hashmap get and containskey method unable to retrieve object despite having implemented equals and hashcode methods

I have a class called Language, Language has a bunch of fields but i only want to use one field to be able to compare it with other Language objects. That field is called languageCode and it is a string. I have automatically implemented equals and hashcode using the method generation tool of Intellij Idea. Heres the the class with the relevant field and the generated methods.

public class Language implements Serializable{

    private String languageCode

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Language language = (Language) o;
        return languageCode.equals(language.getLanguageCode());
    }

    @Override
    public int hashCode() {
        return Objects.hash(languageCode);
    }

}

There is also another class called Journal Journal also has a bunch of different fields but only one is relevant namely:

public class Journal {

  private Map<Language, String> titleTranslations = new HashMap<>()

}

Through out the program i use the constant variable ENGLISH_LANGUAGE which contains the english representing language object from the database. All the language objects in titleTranslations are also coming from the same database table. Furthermore Journal definately has an english language object in its Hashmap titleTranslations.

However when i run the following test:

journal.getTitleTranslations().forEach((Language language, String string) ->{
    log.info(language.getLanguageCode() + " " + string);
    log.info("hashcode is " + language.hashCode());
});
log.info("default language " + ENGLISH_LANGUAGE.getLanguageCode());
log.info("hashcode is " + ENGLISH_LANGUAGE.hashCode());
log.info("contains key " + journal.getTitleTranslations().containsKey(ENGLISH_LANGUAGE));
log.info("key is not null " + (journal.getTitleTranslations().get(ENGLISH_LANGUAGE) != null));

It gives the following output:

de deutsch 
hashcode is 3232 
en english 
hashcode is 3272 
fr francais 
hashcode is 3307 
nl nederlands 
hashcode is 3549 
defautl language en 
hashcode is 3272 
contiains key false 
key is not null false

and you can see that despite the hashcodes of ENGLISH_LANGUAGE and the english language object in titletranslations being exactly the same (3272) it still cannot find the key when using the get method and it returns false when i use containsKey and using ENGLISH_LANGUAGE as argument.

Can someone please tell me what i'm doing wrong? Do single field hashcode implementations somehow don't work?

Thank you

Upvotes: 0

Views: 99

Answers (1)

Maurice
Maurice

Reputation: 7381

Ok the problem appeared to be framework related. Because i am using a framework that uses proxy subclasses like hibernate the equals method should be adjusted slightly. This is something intellij can do for you automatically when you check the option Accept subclasses as parameter to equals() method Below the checkbox is this explanation:

While generally incomplaint to Object.equals() specifiation accepting subclasses might be necessary for generated method to work correctly with frameworks, which generate Proxy subclasses like Hibernate

Checking this will create an equals method that uses this if statement if (!(o instanceof Language)) return false; instead of this one if (o == null || getClass() != o.getClass()) return false;.

This somehow fixes the problem. The hashcode method remains the same. Heres the full code:

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (!(o instanceof Language)) return false;
    Language language = (Language) o;
    return getLanguageCode().equals(language.getLanguageCode());
}

@Override
public int hashCode() {
    return Objects.hash(getLanguageCode());
}

Upvotes: 1

Related Questions