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    }