package org.cdlib.xtf.util;
import org.apache.lucene.util.Prime;
/**
* Copyright (c) 2004, Regents of the University of California
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the University of California nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* A fast but inflexible hash table where the keys are strings and the size
* is fixed. Handles consecutive keys gracefully, but doesn't support resizing,
* deletion, or iteration.
*
* @author Martin Haye
*/
public class StringHash
{
private final int hashSize;
private final Ent[] ents;
private int curSize;
/**
* Create the hash table that can comfortably hold the specified number
* of entries. The actual table is created to be the smallest prime
* greater than size*2.
*
* @param maxSize Max # of entries
*/
public StringHash(int maxSize) {
this.hashSize = Prime.findAfter(maxSize * 2);
ents = new Ent[hashSize];
curSize = 0;
} // constructor
/**
* Sets the entry for the given key number. If one already exists, the old
* value is replaced. Using null for the value can be useful if one only
* needs to check for key presence using contains().
*/
public void put(String key, Object val)
{
int bucket = hashSlot(key);
// Is there already an entry for this key?
Ent e;
for (e = ents[bucket]; e != null; e = e.next)
{
if (key.equals(e.key)) {
e.val = val;
return;
}
} // for e
// Okay, make a new entry
e = new Ent();
e.key = key;
e.val = val;
// And link it in.
e.next = ents[bucket];
ents[bucket] = e;
// All done.
++curSize;
} // put()
/** Calculate the hash slot for a given key */
private final int hashSlot(String key) {
int code = key.hashCode();
if (code >= 0)
return code % hashSize;
return (-code) % hashSize;
} // hash()
/**
* Checks if the hash contains an entry for the given key.
*/
public boolean contains(String key) {
for (Ent e = ents[hashSlot(key)]; e != null; e = e.next)
if (key.equals(e.key))
return true;
return false;
} // contains()
/**
* Retrieves the entry for the given key.
*
* @param key Key to look for
* @return The associated value, or null if not found.
*/
public Object get(String key) {
for (Ent e = ents[hashSlot(key)]; e != null; e = e.next)
if (key.equals(e.key))
return e.val;
return null;
} // get()
/** Tells how many entries are currently in the hash table */
public int size() {
return curSize;
} // size()
/**
* Keeps track of a single entry in the hash table. Can be linked to form
* a chain.
*/
private class Ent {
String key;
Object val;
Ent next;
} // private class Ent
/**
* Basic regression test
*/
public static final Tester tester = new Tester("StringHash")
{
protected void testImpl()
{
StringHash hash = new StringHash(5);
hash.put("100", "hello");
assert hash.contains("100");
assert !hash.contains("111");
assert hash.get("100").equals("hello");
assert hash.size() == 1;
hash.put("200", "foo");
hash.put("211", "bar");
assert hash.contains("100");
assert hash.contains("200");
assert hash.contains("211");
assert !hash.contains("111");
assert !hash.contains("212");
assert hash.size() == 3;
assert hash.get("100").equals("hello");
assert hash.get("200").equals("foo");
assert hash.get("211").equals("bar");
} // testImpl()
};
} // class StringHash