A hash table in Java with 64-bit hash codes (ctd)
Continuing our compact hash map
implementation, we turn to the get() and set() methods.
get() method
We'll start by looking at how to implement the get() method, because
this is more straightforward than the put() method and will help us
envisage more clearly how the fields are used. The first stage is to take the
relevant number of bottom bits of the hash code of the key. We do this by
ANDing the 64-bit hash code with the "hash mask" that we set up in our
initialiser, resulting in the "bucket" index (index into the table array).
We then query this table for the pointer, k, to the start of the
bucket: i.e. the index of hashValues/elements that represents
the first entry in that bucket. If no item has yet been added to that bucket, this
value will be -1 and we simply return null. Otherwise, we compare the
hash value of the first element of the "bucket" (i.e. hashValues[k]);
if it matches the hash of the key we are looking for, then we return the corresponding
value, e.g. elements[k]. Otherwise, we query nextPtrs, which
gives us the index of hashValues/elements representing the next item
in the bucket:
public E get(CharSequence key) {
long hash = DataHasher.hashCode(key);
int hc = (int) hash & hashMask;
int k = table[hc];
if (k != -1)
do {
if (hashValues[k] == hash)
return elements[k];
k = nextPtrs[k];
} while (k != -1);
return null; // No value added at this bucket
}
put() method
At first glance, the put() method seems slightly more involved, but
it is essentially setting up the structure expected by the get() method
presented. We also return the previous value mapped to by the key in question,
as this is sometimes useful and it is more efficient to retrive the old value as
we are adding the new one.
public E put(CharSequence key, E val) {
long hash = DataHasher.hashCode(key);
int hc = (int) hash & hashMask;
int k = table[hc];
if (k == -1) {
// Start a new bucket: none there before
k = nextHashValuePos++;
table[hc] = k;
} else {
// traverse the bucket, looking for a matching hash
int lastk;
do {
if (hashValues[k] == hash) {
E old = elements[k];
elements[k] = val;
return old;
}
lastk = k;
k = nextPtrs[k];
} while (k != -1);
// ... if not there, append to end of bucket
k = nextHashValuePos++;
nextPtrs[lastk] = k;
}
// Append value, either to end of bucket or
// to start of new bucket
hashValues[k] = hash;
nextPtrs[k] = -1;
elements[k] = val;
size++;
return null;
}
If you enjoy this Java programming article, please share with friends and colleagues. Follow the author on Twitter for the latest news and rants.
Editorial page content written by Neil Coffey. Copyright © Javamex UK 2021. All rights reserved.