Of course:
public class Test { private final int m, n; public Test(int m, int n) { this.m = m; this.n = n; } public int hashCode() { return n * m; } public boolean equals(Object ob) { if (ob.getClass() != Test.class) return false; Test other = (Test)ob; return m == other.m; } }
from:
Set<Test> set = new HashSet<Test>(); set.put(new Test(3,4)); boolean b = set.contains(new Test(3, 10)); // false
Technically, this should be true, because m == 3 in both cases.
In general, HashMap works as follows: it has a variable number, which is usually called "buckets." The number of buckets can change over time (when records are added and deleted), but always has a value of 2.
Let's say this HashMap has 16 buckets. When you call put () to add an entry, the hashCode () of the key is computed, and then the mask is taken depending on the size of the buckets. If you are (bitwise) and hashCode () with 15 (0x0F), you will get the last 4 bits equal to a number from 0 to 15 inclusive:
int factor = 4; int buckets = 1 << (factor-1) - 1; // 16 int mask = buckets - 1; // 15 int code = key.hashCode(); int dest = code & mask; // a number from 0 to 15 inclusive
Now, if there is already a record in this bucket, you have what is called a collision. There are several ways to deal with this, but the one used by the HashMap (and probably the most common overall) is bucketing. All records with the same masked hash code are placed in a list.
So, to find if a given key is already on the map:
- Calculate a masked hash code;
- Find the appropriate bucket;
- If it is empty, the key was not found;
- If is is not empty, skip all entries in the bucket by checking equals ().
Looking through the bucket is a linear operation (O (n)), but it is on a small subset. The hashcode bucket definition is essentially constant (O (1)). If the buckets are small enough, then access to the HashMap is usually described as "near O (1)".
You can make a couple of comments about this.
Firstly, if you have a bunch of objects that all return 42 as their hash code, then the HashMap will work, but it will work like an expensive list. Access will be O (n) (since everything will be in one bucket regardless of the number of buckets). I was actually asked in an interview.
Secondly, returning to the starting point, if two objects are equal (i.e. a. equals(b) == b.equals(a) == true ) but have different hash codes, then the HashMap will look for (possibly) the wrong bucket, which leads to unpredictable and undefined.