Do I need to implement hashCode() and equals() methods?

前端 未结 4 960
伪装坚强ぢ
伪装坚强ぢ 2021-01-14 07:47

If I have a map and an object as map key, are the default hash and equals methods enough?

class EventInfo{

    private String name;
    private Map

        
相关标签:
4条回答
  • 2021-01-14 07:55

    Strictly speaking, no. The default implementations of hashCode() and equals() will produce results that ought to work. See http://docs.oracle.com/javase/6/docs/api/java/lang/Object.html#hashCode()

    My understanding is that the default implementation of hashCode() works by taking the object's address in memory and converting to integer, and the default implementation of equals() returns true only if the two objects are actually the same object.

    In practice, you could (and should) probably improve on both of those implementations. For example, both methods should ignore object members that aren't important. In addition, equals() might want to recursively compare references in the object.

    In your particular case, you might define equals() as true if the two objects refer to the same string or the two strings are equal and the two maps are the same or they are equal. I think WChargin gave you pretty good implementations.

    0 讨论(0)
  • 2021-01-14 07:59

    Yes, you need them else you won't be able to compare two EventInfo (and your map won't work).

    0 讨论(0)
  • 2021-01-14 08:08

    Yes, you do. HashMaps work by computing the hash code of the key and using that as a base point. If the hashCode function isn't overriden (by you), then it will use the memory address, and equals will be the same as ==.

    If you're in Eclipse, it'll generate them for you. Click Source menu → Generate hashCode() and equals().

    If you don't have Eclipse, here's some that should work. (I generated these in Eclipse, as described above.)

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((info == null) ? 0 : info.hashCode());
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }
    
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (!(obj instanceof EventInfo)) {
            return false;
        }
        EventInfo other = (EventInfo) obj;
        if (info == null) {
            if (other.info != null) {
                return false;
            }
        } else if (!info.equals(other.info)) {
            return false;
        }
        if (name == null) {
            if (other.name != null) {
                return false;
            }
        } else if (!name.equals(other.name)) {
            return false;
        }
        return true;
    }
    
    0 讨论(0)
  • 2021-01-14 08:11

    Depends on what you want to happen. If two different EventInfo instances with the same name and info should result in two different keys, then you don't need to implement equals and hashCode.

    So

    EventInfo info1 = new EventInfo();
    info1.setName("myname");
    info1.setInfo(null);
    EventInfo info2 = new EventInfo();
    info2.setName("myname");
    info2.setInfo(null);
    

    info1.equals(info2) would return false and info1.hashCode() would return a different value to info2.hashCode().

    Therefore, when you are adding them to your map:

    map.put(info1, "test1");
    map.put(info2, "test2");
    

    you would have two different entries.

    Now, that may be desired behaviour. For example, if your EventInfo is collecting different events, two distinct events with the same data may well want to be desired to be two different entries.

    The equals and hashCode contracts is also applicable in a Set.

    So for example, if your event info contains mouse clicks, it may well be desired that you would want to end up with:

    Set<EventInfo> collectedEvents = new HashSet<EventInfo>();
    collectedEvents.add(info1);
    collectedEvents.add(info2);
    

    2 collected events instead of just 1...

    Hope I'm making sense here...

    EDIT:

    If however, the above set and map should only contain a single entry, then you could use apache commons EqualsBuilder and HashCodeBuilder to simplify the implementation of equals and hashCode:

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof EventInfo) {
            EventInfo other = (EventInfo) obj;
            EqualsBuilder builder = new EqualsBuilder();
            builder.append(name, other.name);
            builder.append(info, other.info);
            return builder.isEquals();
        }
        return false;
    }
    
    @Override
    public int hashCode() {
        HashCodeBuilder builder = new HashCodeBuilder();
        builder.append(name);
        builder.append(info);
        return builder.toHashCode();
    }
    

    EDIT2:

    It could also be appropriate if two EventInfo instances are considered the same, if they have the same name, for example if the name is some unique identifier (I know it's a bit far fetched with your specific object, but I'm generalising here...)

    0 讨论(0)
提交回复
热议问题