import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
public class HashAlgorithmTest {
static Random ran = new Random();
/** key's count */
private static final Integer EXE_TIMES = 100000;
private static final Integer NODE_COUNT = 5;
private static final Integer VIRTUAL_NODE_COUNT = 160;
public static void main(String[] args) {
HashAlgorithmTest test = new HashAlgorithmTest();
/** Records the times of locating node*/
Map<Node, Integer> nodeRecord = new HashMap<Node, Integer>();
List<Node> allNodes = test.getNodes(NODE_COUNT);
KetamaNodeLocator locator = new KetamaNodeLocator(allNodes, HashAlgorithm.KETAMA_HASH, VIRTUAL_NODE_COUNT);
List<String> allKeys = test.getAllStrings();
for (String key : allKeys) {
Node node = locator.getPrimary(key);
Integer times = nodeRecord.get(node);
if (times == null) {
nodeRecord.put(node, 1);
} else {
nodeRecord.put(node, times + 1);
}
}
System.out.println("Nodes count : " + NODE_COUNT + ", Keys count : " + EXE_TIMES + ", Normal percent : " + (float) 100 / NODE_COUNT + "%");
System.out.println("-------------------- boundary ----------------------");
for (Map.Entry<Node, Integer> entry : nodeRecord.entrySet()) {
System.out.println("Node name :" + entry.getKey() + " - Times : " + entry.getValue() + " - Percent : " + (float)entry.getValue() / EXE_TIMES * 100 + "%");
}
}
/**
* Gets the mock node by the material parameter
*
* @param nodeCount
* the count of node wanted
* @return
* the node list
*/
private List<Node> getNodes(int nodeCount) {
List<Node> nodes = new ArrayList<Node>();
for (int k = 1; k <= nodeCount; k++) {
Node node = new Node("node" + k);
nodes.add(node);
}
return nodes;
}
/**
* All the keys
*/
private List<String> getAllStrings() {
List<String> allStrings = new ArrayList<String>(EXE_TIMES);
for (int i = 0; i < EXE_TIMES; i++) {
allStrings.add(generateRandomString(ran.nextInt(50)));
}
return allStrings;
}
/**
* To generate the random string by the random algorithm
* <br>
* The char between 32 and 127 is normal char
*
* @param length
* @return
*/
private String generateRandomString(int length) {
StringBuffer sb = new StringBuffer(length);
for (int i = 0; i < length; i++) {
sb.append((char) (ran.nextInt(95) + 32));
}
return sb.toString();
}
}
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
public class HashAlgorithmPercentTest {
static Random ran = new Random();
/** key's count */
private static final Integer EXE_TIMES = 100000;
private static final Integer NODE_COUNT = 50;
private static final Integer VIRTUAL_NODE_COUNT = 160;
static List<String> allKeys = null;
static {
allKeys = getAllStrings();
}
public static void main(String[] args) {
Map<String, List<Node>> map = generateRecord();
List<Node> allNodes = getNodes(NODE_COUNT);
System.out.println("Normal case : nodes count : " + allNodes.size());
call(allNodes, map);
allNodes = getNodes(NODE_COUNT + 8);
System.out.println("Added case : nodes count : " + allNodes.size());
call(allNodes, map);
allNodes = getNodes(NODE_COUNT - 10);
System.out.println("Reduced case : nodes count : " + allNodes.size());
call(allNodes, map);
int addCount = 0;
int reduceCount = 0;
for (Map.Entry<String, List<Node>> entry : map.entrySet()) {
List<Node> list = entry.getValue();
if (list.size() == 3) {
if (list.get(0).equals(list.get(1))) {
addCount++;
}
if (list.get(0).equals(list.get(2))) {
reduceCount++;
}
} else {
System.out.println("It's wrong size of list, key is " + entry.getKey() + ", size is " + list.size());
}
}
System.out.println(addCount + " --- " + reduceCount);
System.out.println("Same percent in added case : " + (float) addCount * 100/ EXE_TIMES + "%");
System.out.println("Same percent in reduced case : " + (float) reduceCount * 100/ EXE_TIMES + "%");
}
private static void call(List<Node> nodes, Map<String, List<Node>> map) {
KetamaNodeLocator locator = new KetamaNodeLocator(nodes, HashAlgorithm.KETAMA_HASH, VIRTUAL_NODE_COUNT);
for (Map.Entry<String, List<Node>> entry : map.entrySet()) {
Node node = locator.getPrimary(entry.getKey());
if (node != null) {
List<Node> list = entry.getValue();
list.add(node);
}
}
}
private static Map<String, List<Node>> generateRecord() {
Map<String, List<Node>> record = new HashMap<String, List<Node>>(EXE_TIMES);
for (String key : allKeys) {
List<Node> list = record.get(key);
if (list == null) {
list = new ArrayList<Node>();
record.put(key, list);
}
}
return record;
}
/**
* Gets the mock node by the material parameter
*
* @param nodeCount
* the count of node wanted
* @return
* the node list
*/
private static List<Node> getNodes(int nodeCount) {
List<Node> nodes = new ArrayList<Node>();
for (int k = 1; k <= nodeCount; k++) {
Node node = new Node("node" + k);
nodes.add(node);
}
return nodes;
}
/**
* All the keys
*/
private static List<String> getAllStrings() {
List<String> allStrings = new ArrayList<String>(EXE_TIMES);
for (int i = 0; i < EXE_TIMES; i++) {
allStrings.add(generateRandomString(ran.nextInt(50)));
}
return allStrings;
}
/**
* To generate the random string by the random algorithm
* <br>
* The char between 32 and 127 is normal char
*
* @param length
* @return
*/
private static String generateRandomString(int length) {
StringBuffer sb = new StringBuffer(length);
for (int i = 0; i < length; i++) {
sb.append((char) (ran.nextInt(95) + 32));
}
return sb.toString();
}
}
import java.util.List;import java.util.SortedMap;import java.util.TreeMap;public final class KetamaNodeLocator {private TreeMap<Long, Node> ketamaNodes;private HashAlgorithm hashAlg;private int numReps = 160; public KetamaNodeLocator(List<Node> nodes, HashAlgorithm alg, int nodeCopies) {hashAlg = alg;ketamaNodes=new TreeMap<Long, Node>(); numReps= nodeCopies; for (Node node : nodes) {for (int i = 0; i < numReps / 4; i++) {byte[] digest = hashAlg.computeMd5(node.getName() + i);for(int h = 0; h < 4; h++) {long m = hashAlg.hash(digest, h);ketamaNodes.put(m, node);}}} }public Node getPrimary(final String k) {byte[] digest = hashAlg.computeMd5(k);Node rv=getNodeForKey(hashAlg.hash(digest, 0));return rv;}Node getNodeForKey(long hash) {final Node rv;Long key = hash;if(!ketamaNodes.containsKey(key)) {SortedMap<Long, Node> tailMap=ketamaNodes.tailMap(key);if(tailMap.isEmpty()) {key=ketamaNodes.firstKey();} else {key=tailMap.firstKey();}//For JDK1.6 version// key = ketamaNodes.ceilingKey(key);// if (key == null) {// key = ketamaNodes.firstKey();// }}rv=ketamaNodes.get(key);return rv;}}
import java.io.UnsupportedEncodingException;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;public enum HashAlgorithm {/** * MD5-based hash algorithm used by ketama. */KETAMA_HASH;public long hash(byte[] digest, int nTime) {long rv = ((long) (digest[3+nTime*4] & 0xFF) << 24)| ((long) (digest[2+nTime*4] & 0xFF) << 16)| ((long) (digest[1+nTime*4] & 0xFF) << 8)| (digest[0+nTime*4] & 0xFF);return rv & 0xffffffffL; /* Truncate to 32-bits */}/** * Get the md5 of the given key. */public byte[] computeMd5(String k) {MessageDigest md5;try {md5 = MessageDigest.getInstance("MD5");} catch (NoSuchAlgorithmException e) {throw new RuntimeException("MD5 not supported", e);}md5.reset();byte[] keyBytes = null;try {keyBytes = k.getBytes("UTF-8");} catch (UnsupportedEncodingException e) {throw new RuntimeException("Unknown string :" + k, e);}md5.update(keyBytes);return md5.digest();}}