package com.e2u.hash;
import java.util.*;
public class TestHash
{
private static int counter = 0;
private static Map<String, HashAlg> map = new TreeMap<String, HashAlg>();
private static void init()
{
map.put("00-Remain", new HashAlg()
{
public int hash(int h)
{
return h;
}
});
map.put("01-hx127", new HashAlg()
{
public int hash(int h)
{
return h - (h << 7); // i.e., -127 * h
}
});
/**
* HashMap's supplemental hash function is a simplified version of the
* shift-add hash function in the Linux buffer cache. It is described in
* CITI Technical Report 00-1,
* "Linux Kernel Hash Table Behavior: Analysis and Improvements", by
* Chuck Lever
* (http://www.citi.umich.edu/techreports/reports/citi-tr-00-1.pdf). The
* full version of this hash function is:
*
* @param h
* @return
*/
map.put("02-Linux", new HashAlg()
{
public int hash(int h)
{
return ((h << 7) - h + (h >>> 9) + (h >>> 17));
}
});
map.put("03-discussion", new HashAlg()
{
public int hash(int key)
{
key += ~(key << 9);
key ^= (key >>> 14);
key += (key << 4);
key ^= (key >>> 10);
return key;
}
});
/**
* From: HashMap Applies a supplemental hash function to a given
* hashCode, which defends against poor quality hash functions. This is
* critical because HashMap uses power-of-two length hash tables, that
* otherwise encounter collisions for hashCodes that do not differ in
* lower bits. Note: Null keys always map to hash 0, thus index 0.
*/
map.put("04-HashMap", new HashAlg()
{
public int hash(int h)
{
// This function ensures that hashCodes that differ only by
// constant multiples at each bit position have a bounded
// number of collisions (approximately 8 at default load
// factor).
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
});
}
private static void find(Map map, Object key)
{
if(map.containsKey(key))
{
System.out.println(String.format("Find key=%s OK. Value=%s", key,
map.get(key)));
}
else
{
System.out.println(String.format("Find key=%s Failed.", key));
}
}
public static void testHash()
{
Hashtable<HashTester, Integer> map = new Hashtable<HashTester, Integer>();
HashTester ht1 = new HashTester(1, 1);
HashTester ht2 = new HashTester(1, 2);
HashTester ht3 = new HashTester(1, 12);
map.put(ht1, ++counter);
map.put(ht2, ++counter);
map.put(ht3, ++counter);
find(map, ht1); // OK. This key itself is in the map
find(map, ht2); // OK. Same reason as above
find(map, ht3); // OK. Same reason as above
// OK. This key has a same hashCode and equals to an object in the map.
find(map, new HashTester(1, 2));
// Failed. There's no object in the map has a same hashCode with this
// input param
// Though it's equals to several keys in the map.
find(map, new HashTester(1, 3));
// Failed. Same reason as above
find(map, new HashTester(1, 11));
// OK. The cloned object has the same hashCode and equals to the
// original object.
HashTester tmp = (HashTester) ht1.clone();
find(map, tmp);
}
/**
* From: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4669519
*/
private static void testHashAlg()
{
final int SIZE = 500;
final int TABLE_LENGTH = 1024;
Set s = new HashSet();
for(Iterator<String> iter = map.keySet().iterator(); iter.hasNext(); )
{
String algName = iter.next();
HashAlg ha = map.get(algName);
for(int i = 0; i < SIZE; i++)
{
int h = new Double(i).hashCode();
int hPrime = ha.hash(h);
int bucket = hPrime & (TABLE_LENGTH - 1);
s.add(new Integer(bucket));
}
String output = String.format("Alg Name = %s, size = %d", algName, s.size());
System.out.println(output);
s.clear();
}
}
/**
* @param args
*/
public static void main(String[] args)
{
init();
testHash();
testHashAlg();
}
}
class HashTester implements Cloneable
{
private int hash;
private int id;
public HashTester(int id, int hash)
{
this.id = id;
this.hash = hash;
}
public boolean equals(Object obj)
{
if(!(obj instanceof HashTester))
{
return false;
}
HashTester other = (HashTester) obj;
return id == other.id;
}
public int hashCode()
{
return hash;
}
public String toString()
{
return "{id=" + id + ", hash=" + hash + "}";
}
public Object clone()
{
HashTester result = null;
try
{
result = (HashTester) super.clone();
}
catch(CloneNotSupportedException e)
{
// assert false;
e.printStackTrace();
}
return result;
}
}
interface HashAlg
{
public int hash(int h);
}