2

A coworker encountered a bug where HashMap#containsKey was used to verify if a key-value pair exists. The specified key was a different type so the method would always return false. For example (Let's assume the map is populated with objects that he wants to retrieve):

Map<String, String> map = new HashMap<String, String>();
Long value = new Long(12);
boolean hasString = map.containsKey(value);

My question is:

Why does the Map implementation force you to put keys of type K and values of type V, but the containsKey() method allow you specify any object?

There are other methods in the Map interface such as get() that also accept a parameter of type Object.

austen
  • 3,314
  • 3
  • 25
  • 26
  • 1
    Also see http://smallwig.blogspot.com/2007/12/why-does-setcontains-take-object-not-e.html – Steve Kuo Dec 18 '09 at 04:49
  • Sorry about asking the duplicate question. I tried to search around for it but I must have used the wrong keywords. Thanks for the links! – austen Dec 18 '09 at 09:03

1 Answers1

2

I think it's because Java's Map implementation uses the .equals() command to decide whether two objects are equal for the purposes of methods like .containsKey() or .remove(). And since .equals() compares with a general Object (and if you're overriding it, you usually want a check at the top like if (!(t instanceof T)) { return false; } but there's no actual stipulation that A and B have to be the same type for A.equals(B) to be true.

Here's some code that highlights the difference. Might not do what you expect.

import java.util.*;

public class Test {

    static class Foo implements Comparable {
        public String a;

        public boolean equals(Object obj) {
            if (obj instanceof Bar) { return a.equals((((Bar) obj).b)); }
            return false;
        }

        public int compareTo(Object o) {
            return this.equals(o) ? 0 : -1;
        }
    }

    static class Bar implements Comparable {
        public String b;

        public boolean equals(Object obj) {
            if (obj instanceof Foo) { return b.equals((((Foo) obj).a)); }
            return false;
        }

        public int compareTo(Object o) {
            return this.equals(o) ? 0 : -1;
        }
    }   

    public static void main(String [] args) {
        Map<Foo, Bar> test = new TreeMap<Foo, Bar>();
        Foo foo = new Foo();
        Bar bar = new Bar();

        foo.a = "Hello";
        bar.b = "Hello";

        System.out.println(foo.equals(bar));
        System.out.println(bar.equals(foo));

        test.put(foo, bar);

        System.out.println(test.containsKey(bar));
        System.out.println(test.containsValue(foo));
    }

}
Adrian Petrescu
  • 16,629
  • 6
  • 56
  • 82