TreeMap put is not working as expected
Asked Answered
M

3

5
class Point{
    int x, y, l;

    Point(int x, int y, int l){
        this.x =x;
        this.y =y;
        this.l=l;
    }
    @Override
    public int hashCode() {
        return y;
    }

    @Override
    public boolean equals(final Object obj) {
        if(this == obj) return true;
        if(!(obj instanceof Point)) return false;
        Point p = (Point) obj;
        return this.x == p.x && this.y == p.y;
    }
}

    TreeMap<Point,Integer>  sortedMap = new TreeMap<>((p1, p2)-> p1.l-p2.l);
    sortedMap.put(new Point(4,5,0),0);
    sortedMap.put(new Point(5,5,0),6);
    System.out.println(sortedMap.size()); -> Output:  1
    System.out.println((new Point(4,5,0)).equals(new Point(5,5,0))); -> Output -> False.

I have overloaded both hashcode, equals method in class. I think put method should use equals method to determine whether the same object exits or not. But it is not working as expected.

I am not sure why my hashMap size is 1. Please let me know if I understood hashmap working wrongly. Help me to identify bug in this code ?

Mailable answered 1/8, 2017 at 12:40 Comment(5)
Why is your equality check p1.l - p2.l?Kittrell
Why are you calling your variable sortedSet when it's not a set but a map?Venetis
@MuratK.: That's not an equality check - it's an ordering.Builder
@Aubin: That's still a valid hashCode implementation for the given equals method. Yes, it'll give the same hash code for multiple equal values, but that's fine. (It would potentially cause inefficiencies, but it's valid.)Builder
The tags say hashmap, but your code says TreeMap - which is correct?Grecian
B
11

Your TreeMap is comparing Point values by the l part (whatever that's meant to be). That's what this part of your code does:

new TreeMap<>((p1, p2)-> p1.l-p2.l);

The two points you've created have the same l value (0) so they're considered equal... so the second call to put replaces the existing entry. TreeMap doesn't use equals and hashCode at all.

From the documentation:

Note that the ordering maintained by a tree map, like any sorted map, and whether or not an explicit comparator is provided, must be consistent with equals if this sorted map is to correctly implement the Map interface. (See Comparable or Comparator for a precise definition of consistent with equals.) This is so because the Map interface is defined in terms of the equals operation, but a sorted map performs all key comparisons using its compareTo (or compare) method, so two keys that are deemed equal by this method are, from the standpoint of the sorted map, equal. The behavior of a sorted map is well-defined even if its ordering is inconsistent with equals; it just fails to obey the general contract of the Map interface.

Your comparison isn't consistent with equals, which is why you're seeing results which violate the Map contract.

Builder answered 1/8, 2017 at 12:42 Comment(3)
Or Comparator.comparingInt(Point::l)Judie
@Judie indeed, but that takes up more screen space.Venetis
@KlitosKyriacou is that ever an argument? this is doing the correct thing, by not using boxing/un-boxing all the timeJudie
S
6

TreeMap only uses the supplied Comparator (or the natural ordering when available) to determine equality, so equals and hashCode make no difference.

In your case, both Points have the same l value, so they are considered identical according to your Comparator.

You can modify your Comparator to take all the properties (l, x and y) into account when determining the order (and the equality) of your keys.

Seiden answered 1/8, 2017 at 12:42 Comment(0)
V
2
sortedSet.put(new Point(4,5,0),0);
sortedSet.put(new Point(5,5,0),6);

These put call's use the supplied comparator ((p1, p2)-> p1.l-p2.l) to check if two Points are equal.

(new Point(4,5,0)).equals(new Point(5,5,0))

This uses the equals overridden in Point class.

Hence the difference..

Valentinavalentine answered 1/8, 2017 at 12:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.