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