/* * Copyright (c) 2016 Fraunhofer IGD * * All rights reserved. This program and the accompanying materials are made * available under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution. If not, see <http://www.gnu.org/licenses/>. * * Contributors: * Fraunhofer IGD <http://www.igd.fraunhofer.de/> */ package de.fhg.igd.geom.util; import java.io.Serializable; /** * <p> * This class provides a simple and effective hash algorithm taken from Bloch, * "Effective Java: Programming Language Guide, p33". * </p> * <p> * Explanation, excerpt from Bloch, "Effective Java: Programming Language Guide, * p33 * </p> * <p> * A good hash function tends to produce unequal hash codes for unequal objects. * This is exactly what is meant by the third provision of the hashCode * contract. Ideally, a hash function should distribute any reasonable * collection of unequal instances uniformly across all possible hash values. * Achieving this ideal can be extremely difficult. * </p> * <p> * Luckily it is not too difficult to achieve a fair approximation. Here is a * simple recipe: * </p> * <p> * 1. Store some constant nonzero value, say 17, in an int variable called * result. 2. For each significant field f in your object (each field taken into * account by the equals method, that is), do the following: a. Compute an int * hash code c for the field: i. If the field is a boolean, compute (f ? 0 : 1). * ii. If the field is a byte, char, short, or int, compute (int)f. iii. If the * field is a long, compute (int)(f ^ (f >>> 32)). iv. If the field is a float * compute Float.floatToIntBits(f). v. If the field is a double, compute * Double.doubleToLongBits(f), and then hash the resulting long as in step * 2.a.iii. vi. If the field is an object reference and this class's equals * method compares the field by recursively invoking equals, recursively invoke * hashCode on the field. If a more complex comparison is required, compute a * "canonical representation" for this field and invoke hashCode on the * canonical representation. If the value of the field is null, return 0 (or * some other constant, but 0 is traditional). vii. If the field is an array, * treat it as if each element were a separate field. That is, compute a hash * code for each significant element by applying these rules recursively, and * combine these values as described in step 2.b. b. Combine the hash code c * computed in step a into result as follows: result = 37*result + c; 3. Return * result. 4. When you are done writing the hashCode method, ask yourself * whether equal instances have equal hash codes. If not, figure out why and fix * the problem. * </p> * <p> * It is acceptable to exclude redundant fields from the hash code computation. * In other words, it is acceptable to exclude any field whose value can be * computed from fields that are included in the computation. It is required * that you exclude any fields that are not used in equality comparisons. * Failure to exclude these fields may result in a violation of the second * provision of the hashCode contract. * </p> * <p> * A nonzero initial value is used in step 1, so the hash value will be affected * by initial fields whose hash value, as computed in step 2.a, is zero. If zero * was used as the initial value in step 1, the overall hash value would be * unaffected by any such initial fields, which could increase collisions. The * value 17 is arbitrary. * </p> * <p> * The multiplication in step 2.b makes the hash value depend on the order of * the fields, which results in a much better hash function if the class * contains multiple similar fields. For example, if the multiplication were * omitted from a String hash function built according to this recipe, all * anagrams would have identical hash codes. The multiplier 37 was chosen * because it is an odd prime. If it was even and the multiplication overflowed, * information Effective Java: Programming Language Guide 34 would be lost * because multiplication by two is equivalent to shifting. The advantages of * using a prime number are less clear, but it is traditional to use primes for * this purpose. * </p> */ public class BlochHashCode implements Serializable { /** * The serial version UID */ private static final long serialVersionUID = -6432905297232026617L; /** * a non-zero positive prime constant */ public final static int HASH_CONSTANT = 17; private volatile int myHash; /** * Default constructor */ public BlochHashCode() { myHash = HASH_CONSTANT; } /** * Adds a value * * @param val the value to add */ public void add(int val) { myHash = addFieldToHash(myHash, hashCode(val)); } /** * Adds a value * * @param val the value to add */ public void add(char val) { myHash = addFieldToHash(myHash, hashCode(val)); } /** * Adds a value * * @param val the value to add */ public void add(float val) { myHash = addFieldToHash(myHash, hashCode(val)); } /** * Adds a value * * @param val the value to add */ public void add(short val) { myHash = addFieldToHash(myHash, hashCode(val)); } /** * Adds a value * * @param val the value to add */ public void add(double val) { myHash = addFieldToHash(myHash, hashCode(val)); } /** * Adds a value * * @param val the value to add */ public void add(boolean val) { myHash = addFieldToHash(myHash, hashCode(val)); } /** * Adds a value * * @param val the value to add */ public void add(long val) { myHash = addFieldToHash(myHash, hashCode(val)); } /** * Adds an object * * @param object the object to add */ public void add(Object object) { myHash = addFieldToHash(myHash, hashCode(object)); } /** * Resets the hash */ public void reset() { this.myHash = HASH_CONSTANT; } /** * Adds a hash value to the current hash * * @param hashToAdd the hash code that shall be added */ public void addHash(int hashToAdd) { this.myHash = BlochHashCode.addHashToHash(this.myHash, hashToAdd); } /** * @return the current hash */ public int getHash() { return this.myHash; } /** * @return a hash code for the given field (see explanation above) * @param field the field to calculate the hash code for */ public static int hashCode(int field) { return field; } /** * @return a hash code for the given field (see explanation above) * @param field the field to calculate the hash code for */ public static int hashCode(char field) { return field; } /** * @return a hash code for the given field (see explanation above) * @param field the field to calculate the hash code for */ public static int hashCode(String field) { return field.hashCode(); } /** * @return a hash code for the given field (see explanation above) * @param field the field to calculate the hash code for */ public static int hashCode(byte field) { return field; } /** * @return a hash code for the given field (see explanation above) * @param field the field to calculate the hash code for */ public static int hashCode(short field) { return field; } /** * @return a hash code for the given field (see explanation above) * @param field the field to calculate the hash code for */ public static int hashCode(double field) { return hashCode(Double.doubleToLongBits(field)); } /** * @return a hash code for the given field (see explanation above) * @param field the field to calculate the hash code for */ public static int hashCode(long field) { return (int) (field ^ (field >>> 32)); } /** * @return a hash code for the given field (see explanation above) * @param field the field to calculate the hash code for */ public static int hashCode(boolean field) { return (field ? 0 : 1); } /** * @return a hash code for the given field (see explanation above) * @param field the field to calculate the hash code for */ public static int hashCode(float field) { return Float.floatToIntBits(field); } /** * @return a hash code for the given field (see explanation above) * @param field the field to calculate the hash code for */ public static int hashCode(Object field) { return field == null ? 0 : field.hashCode(); } /** * Convenience method for adding a new hash to a given hash code. * * @param currentHash the current hash code * @param hashToAdd the hash code that shall be added to the current hash * @return the new hash */ public static int addHashToHash(int currentHash, int hashToAdd) { return 37 * currentHash + hashToAdd; } /** * Convenience method for adding a field to a given hash code. * * @param currentHash the current hash code * @param fieldToAdd the field whose hash code shall be added to the current * hash * @return the new hash including the field's hash */ public static int addFieldToHash(int currentHash, int fieldToAdd) { return 37 * currentHash + hashCode(fieldToAdd); } /** * Convenience method for adding a field to a given hash code. * * @param currentHash the current hash code * @param fieldToAdd the field whose hash code shall be added to the current * hash * @return the new hash including the field's hash */ public static int addFieldToHash(int currentHash, char fieldToAdd) { return 37 * currentHash + hashCode(fieldToAdd); } /** * Convenience method for adding a field to a given hash code. * * @param currentHash the current hash code * @param fieldToAdd the field whose hash code shall be added to the current * hash * @return the new hash including the field's hash */ public static int addFieldToHash(int currentHash, byte fieldToAdd) { return 37 * currentHash + hashCode(fieldToAdd); } /** * Convenience method for adding a field to a given hash code. * * @param currentHash the current hash code * @param fieldToAdd the field whose hash code shall be added to the current * hash * @return the new hash including the field's hash */ public static int addFieldToHash(int currentHash, short fieldToAdd) { return 37 * currentHash + hashCode(fieldToAdd); } /** * Convenience method for adding a field to a given hash code. * * @param currentHash the current hash code * @param fieldToAdd the field whose hash code shall be added to the current * hash * @return the new hash including the field's hash */ public static int addFieldToHash(int currentHash, double fieldToAdd) { return 37 * currentHash + hashCode(fieldToAdd); } /** * Convenience method for adding a field to a given hash code. * * @param currentHash the current hash code * @param fieldToAdd the field whose hash code shall be added to the current * hash * @return the new hash including the field's hash */ public static int addFieldToHash(int currentHash, float fieldToAdd) { return 37 * currentHash + hashCode(fieldToAdd); } /** * Convenience method for adding a field to a given hash code. * * @param currentHash the current hash code * @param fieldToAdd the field whose hash code shall be added to the current * hash * @return the new hash including the field's hash */ public static int addFieldToHash(int currentHash, long fieldToAdd) { return 37 * currentHash + hashCode(fieldToAdd); } /** * Convenience method for adding a field to a given hash code. * * @param currentHash the current hash code * @param fieldToAdd the field whose hash code shall be added to the current * hash * @return the new hash including the field's hash */ public static int addFieldToHash(int currentHash, boolean fieldToAdd) { return 37 * currentHash + hashCode(fieldToAdd); } /** * Convenience method for adding a field to a given hash code. * * @param currentHash the current hash code * @param fieldToAdd the field whose hash code shall be added to the current * hash * @return the new hash including the field's hash */ public static int addFieldToHash(int currentHash, Object fieldToAdd) { return 37 * currentHash + hashCode(fieldToAdd); } @Override public boolean equals(Object o) { return o instanceof BlochHashCode && ((BlochHashCode) o).getHash() == this.myHash ? true : false; } @Override public int hashCode() { return this.getHash(); } @Override public String toString() { return "[BlochHashCode with hash value= " + this.myHash + "]"; } }