简体   繁体   中英

Overriding hashCode() method

Joshua Bloch says on Effective Java:

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.

My overridden equals() method implements fuzzy score algorithm for comparing Match objects:

public class Match {

    private String homeTeam;

    private String awayTeam;

    public Match(String homeTeam, String awayTeam) {
        this.homeTeam = formatTeamName(homeTeam);
        this.awayTeam = formatTeamName(awayTeam);
    }
}

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Match that = (Match) o;

        final int threshold = 6;

        return (computeFuzzyScore(this.homeTeam, that.awayTeam) <= threshold || computeFuzzyScore(this.awayTeam, that.homeTeam) <= threshold) &&
                    computeFuzzyScore(this.homeTeam, that.homeTeam) > threshold && computeFuzzyScore(this.awayTeam, that.awayTeam) > threshold;

    }

    // formatTeamName(), computeFuzzyScore() have been left out for brevity.
}

This way these objects are equal:

Match match0 = new Match("Man.City", "Atl.Madryt");
Match match1 = new Match("Manchester City", "Atlético Madryt");

How should I override hashCode() method to generate same value for such objects?

As the answers by M. le Rutte and AxelH already say, equals should only return true for objects that are the same (should be okay to switch between at all times and render the same results in your code regardless of which is used).

One way to solve this is to use a wrapper-class, described in the answer to Remove duplicates from a list of objects based on property in Java 8 . You make it so the wrapper class only stores the computed fuzzy values and compare and uses the values in both the equals and the hashcode, then you may use unwrapp to get the real values.

Another way is to do like yshavit said and do another equal similar to String:equalsIgnoreCase

I would recommend you use some other method name such as fuzzyEquals instead of equals . You should view hashCode and equals in terms of their usage. Many Java classes such as HashMap call these two methods without your consent and require them to adhere strictly to their idea. And their idea is not what you want, but what they need. It's something like this:

  • equals = homeTeam.equals && awayTeam.equals
  • hashCode = homeTeam.hashCode ^ awayTeam.hashCode

By renaming, you (a) keep HashMap and his friends happy, (b) avoid confusion, (c) improve readability and (d) have two different methods for two different things, which you can use independently or combine further.

To be complete, you should review the data model. It is the one failing you for the moment

  • A Match is between two Team .
  • A Team have a name
  • A Team have alias (0..n).

You would have somethink like :

public Team{

    private final String name;
    private List<String> alias;

    public Team(Sting name){ ... }

    public boolean equals(Object obj){
        // check name
    }

    public int hashCode(){
        // hash the name
    }
}

Then, just review the Match to used this class the same way.

The class Team could provide a method to check for any alias matching a String , if not matching/found in alias , it would check with the name using your algo. If this alias match, you add it to the List for futur research.

That way, you don't need to run your fuzzy algorithm each time. It could be usefull if you want a user to get the Team matching any input of his choice.

The important part is that if match1 == match2 then match1.hashCode() == match2.hashCode() . Let's say that you have a database where you store the match with an id (a number) then you can have hashCode return the id and be done.

without using a database to keep track of the id of matches you could be done with assigning every team a numeric id of fixed length and concatenate the two id as a result of hashCode .

As an example the "Manchester City" team could be team 1 and "Atlético Madryt" team 2 . If the hash is 32 bit long you can have the first 16 bit be the team 1 and the last 16 be team 2 as in this representation.

// 16 bit for team 1 + 16 bit for team 2
0000000000000001        0000000000000010

An this is a valid 32 bit integer that will match the rule of match1 == match2 then match1.hashCode() == match2.hashCode()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM