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}