0

In my most recent question, I was informed that I needed to override my equals and hashcode method (among other things). So I took a bit of time to read a few articles and attempted to come up with a proper implementation.

Here are some of the articles I read:

All the articles were pretty good. Since this is my first time attempting to do this, I just want to be sure I'm not making some simple (or dumb) mistake.

I will be using name to indicate whether my Person object is equivalent to another Person object. The reason for this, is that all the other variables can vary, but the name will always be unique.

Updated to reflect recommended changes

public class Person {

    private String name;
    private int p_number;
    private String address;
    //other variables

    public Person(String a_name) {
        name = a_name;
    }

    public String getName() {
        return name;
    }

    //other getters and setters

    @Override
    public boolean equals(Object o) {
        if(o == null) 
            return false;

        if(o == this) 
            return true;

        if(!(o instanceof Person)) 
            return false;

        Person p = (Person) o;
        return name.equals(p.name));

    }

    @Override
    public int hashCode() {
        return name.hashCode();
    }
}

My questions are as follows:

  1. Did I implement these methods correctly?
  2. Since name is the only variable that determines uniqueness, do I need to bother checking any of the other variables in hashcode?
  3. I was reading on StackOverflow that 31 was chosen as a good prime number awhile ago, but choosing a larger prime is better now? Can someone confirm or deny this claim? (the claim was made in the third link above)

If I haven't implemented these methods properly, how can I change/improve them?

Community
  • 1
  • 1
WilliamShatner
  • 926
  • 2
  • 12
  • 25
  • IDEs like Eclipse will generate good implementations of `equals()` and `hashCode()` for you. – GriffeyDog Jun 06 '13 at 18:52
  • 2
    @GriffeyDog In my last question, a few helper SO'ers informed me of this as well. However, they advised that I shouldn't generate anything that I didn't understand. – WilliamShatner Jun 06 '13 at 18:53
  • @WilliamShatner that is excellent advice from them! – fge Jun 06 '13 at 18:54
  • @fge Indeed it is! That is why I started reading into it a bit. I'm hoping to get a better understanding of these two methods as my project will more than likely use it extensively. – WilliamShatner Jun 06 '13 at 19:01

2 Answers2

3

In equals():

if(name.equals(p.getName()))
    return true;

Missing false, and you can just:

// Both are Person instances, no need to use the accessor here
return name.equals(p.name);

As to hashCode(), just return name.hashCode();

Also, can name be null? Your methods don't seem to account for that. (edit: answer: no)

As to your questions:

Since name is the only variable that determines uniqueness, do I need to bother checking any of the other variables in hashcode?

NO, certainly not! If your names are equal but ages different, this would lead to a different hash code for objects which are equal, and this violates the Object contract!

I was reading on StackOverflow that 31 was chosen as a good prime number awhile ago, but choosing a larger prime is better now? Can someone confirm or deny this claim? (the claim was made in the third link above)

This, no idea...

To be more complete about the .equals()/.hashCode() contract, I'll mention a utility class from Guava: Equivalence. An implementation of this abstract class for a given class can allow you to create Sets, and therefore Maps, with these objects as members (keys) as if they had a different implementation of both of these functions:

Equivalence<MyClass> eq = ....;

Set<Equivalence.Wrapper<MyClass>> set = ...;

set.add(eq.wrap(myClassInstance));

This can be actually very useful in some scenarios...

fge
  • 119,121
  • 33
  • 254
  • 329
  • Good point, no, name cannot be null. Thank you for the quick reply! I'll make these fixes – WilliamShatner Jun 06 '13 at 18:48
  • Note the edit for `.equals()`: no need to use the accessor for accessing `name` with `o`. – fge Jun 06 '13 at 18:53
  • Another thing: can `Person` be extended? – fge Jun 06 '13 at 18:55
  • Thank you, I noted the changes from you (as well as greedybuddha) and will be going over them as I edit the class. For the time being `Person` will not be extended. I don't have plans for extending it in the future either though. – WilliamShatner Jun 06 '13 at 19:01
  • Pity that you have setters too, otherwise I'd have recommended to make the instance variable `final` as well. Or maybe you can use a builder to build `Person` objects, it would allow for that class to be immutable, which is a nice plus. – fge Jun 06 '13 at 19:03
  • I'm still learning Java, I haven't gotten to the part where immutable objects are talked about yet. As far as I'm aware of, `name` will never change once the object is made. The reason I have set/get is because of the way I parse the XML to create the Person objects. (kept my constructors to default) – WilliamShatner Jun 06 '13 at 19:07
  • Ah yeah, deserialization frameworks allowing to use builders are still too rare :/ – fge Jun 06 '13 at 19:09
  • OK as far as I'm concerned! Good job ;) – fge Jun 06 '13 at 21:55
  • Thanks :) I appreciate the time you gave up to confirm it! – WilliamShatner Jun 06 '13 at 22:09
  • Don't worry about that, it just makes me get the eyes off my IDE ;) – fge Jun 06 '13 at 22:10
  • @WilliamShatner: An aspect of class design which is far too seldom considered when designing things like `equals` is what it would mean if e.g. there existed two instances of `Person` with the same name but different addresses. Even if that's not "supposed" to happen, it will be almost impossible to avoid if any aspects of the object can be changed after creation. Mutable objects have a strong *identity* separate from the contents of their fields, and it generally only makes sense to override `equals` when objects are used in ways that don't imply a strong identity. – supercat Nov 11 '13 at 22:21
1

Your equals needs to return a value in all cases, just change the end part to return the name.equals.

@Override
public boolean equals(Object o) {
    ...
    return name.equals(o.getName());
}

Also your hashcode is actually detrimental, all it does is use the name.hashCode() but then multiply it by 31, better to use the default Java string hashcode from the name directly.

@Override
public int hashCode() {
    return name.hashCode();
}
greedybuddha
  • 7,488
  • 3
  • 36
  • 50
  • Thank you for the quick response as well! Glad to see that both of you made the same statements. I'm glad I wasn't too far off track. – WilliamShatner Jun 06 '13 at 18:52