001 package org.biojava3.core.util;
002
003 import java.lang.reflect.Array;
004
005 /**
006 * Contains helper methods for generating a HashCode without having to resort to
007 * the commons lang hashcode builders.
008 *
009 * Example where the property name is a String and the property age is an int
010 *
011 * <pre>
012 * public int hashCode() {
013 * int result = Hashcoder.SEED;
014 * result = Hashcoder.hash(result, this.getName());
015 * result = Hashcoder.hash(result, this.getAge());
016 * return result;
017 * }
018 * </pre>
019 *
020 * @author ayates
021 */
022 public class Hashcoder {
023
024 /**
025 * An initial value for a <code>hashCode</code>, to which we add
026 * contributions from fields. Using a non-zero value decreases collisions of
027 * <code>hashCode</code> values.
028 */
029 public static final int SEED = 9;
030
031 /**
032 * The prime number used to multiply any calculated hashcode seed by
033 *
034 * i.e. result = PRIME*result + c
035 *
036 * Where result is the result of the previous calculation (at first this will
037 * be seed) and c is the calculated int to add to result
038 */
039 public static final int PRIME = 79;
040
041 public static int hash(int seed, boolean b) {
042 return (PRIME * seed) + (b ? 1 : 0);
043 }
044
045 public static int hash(int seed, char c) {
046 return (PRIME * seed) + (int) c;
047 }
048
049 /**
050 * Used for ints, bytes and shorts
051 */
052 public static int hash(int seed, int i) {
053 return (PRIME * seed) + i;
054 }
055
056 /**
057 * long support done by shifting by 32 (using unsigned shift)
058 */
059 public static int hash(int seed, long l) {
060 return (PRIME * seed) + (int) (l ^ (l >>> 32));
061 }
062
063 /**
064 * float support done via {@link Float#floatToIntBits(float)} which allows
065 * the encoding of a float as an int. We can then hash as normal.
066 */
067 public static int hash(int seed, float f) {
068 return hash(seed, Float.floatToIntBits(f));
069 }
070
071 /**
072 * double support which is done using the same techinque as float hashing
073 * except we convert to a long not to an int.
074 */
075 public static int hash(int seed, double d) {
076 return hash(seed, Double.doubleToLongBits(d));
077 }
078
079 /**
080 * <code>o</code> is a possibly-null object field, and possibly an
081 * array.
082 *
083 * If <code>o</code> is an array, then each element may be a primitive
084 * or a possibly-null object.
085 */
086 public static int hash(int seed, Object o) {
087 int result = seed;
088 //If it was null then this is the result 0
089 if (o == null) {
090 result = hash(result, 0);
091 }
092 //If it wasn't an array then calculate the hashcode
093 else if (!o.getClass().isArray()) {
094 result = hash(result, o.hashCode());
095 }
096 //Otherwise loop &
097 else {
098 int length = Array.getLength(o);
099 for (int i = 0; i < length; i++) {
100 Object item = Array.get(o, i);
101 result = hash(result, item);// recursive call!
102 }
103 }
104 return result;
105 }
106 }