001/** 002 * Copyright (c) 2011, The University of Southampton and the individual contributors. 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without modification, 006 * are permitted provided that the following conditions are met: 007 * 008 * * Redistributions of source code must retain the above copyright notice, 009 * this list of conditions and the following disclaimer. 010 * 011 * * Redistributions in binary form must reproduce the above copyright notice, 012 * this list of conditions and the following disclaimer in the documentation 013 * and/or other materials provided with the distribution. 014 * 015 * * Neither the name of the University of Southampton nor the names of its 016 * contributors may be used to endorse or promote products derived from this 017 * software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030package org.openimaj.util.hash; 031 032import java.lang.reflect.Array; 033 034/** 035 * Collected methods which allow easy implementation of <code>hashCode</code>. 036 * The following utility class allows simple construction of an effective hashCode method. 037 * It is based on the recommendations of Effective Java, by Joshua Bloch. 038 * 039 * Example use case: 040 * <pre> 041 * public int hashCode(){ 042 * int result = HashCodeUtil.SEED; 043 * //collect the contributions of various fields 044 * result = HashCodeUtil.hash(result, fPrimitive); 045 * result = HashCodeUtil.hash(result, fObject); 046 * result = HashCodeUtil.hash(result, fArray); 047 * return result; 048 * } 049 * </pre> 050 */ 051public final class HashCodeUtil { 052 053 /** 054 * An initial value for a <code>hashCode</code>, to which is added contributions 055 * from fields. Using a non-zero value decreases collisions of <code>hashCode</code> 056 * values. 057 */ 058 public static final int SEED = 23; 059 060 /** 061 * booleans. 062 * @param aSeed the hash value to append to 063 * @param aBoolean the value to hash 064 * @return the new hash value 065 */ 066 public static int hash( int aSeed, boolean aBoolean ) { 067 return firstTerm( aSeed ) + ( aBoolean ? 1 : 0 ); 068 } 069 070 /** 071 * chars. 072 * @param aSeed the hash value to append to 073 * @param aChar the value to hash 074 * @return the new hash value 075 */ 076 public static int hash( int aSeed, char aChar ) { 077 return firstTerm( aSeed ) + (int)aChar; 078 } 079 080 /** 081 * ints. 082 * @param aSeed the hash value to append to 083 * @param aInt the value to hash 084 * @return the new hash value 085 */ 086 public static int hash( int aSeed , int aInt ) { 087 /* 088 * Implementation Note 089 * Note that byte and short are handled by this method, through 090 * implicit conversion. 091 */ 092 return firstTerm( aSeed ) + aInt; 093 } 094 095 /** 096 * longs. 097 * @param aSeed the hash value to append to 098 * @param aLong the value to hash 099 * @return the new hash value 100 */ 101 public static int hash( int aSeed , long aLong ) { 102 return firstTerm(aSeed) + (int)( aLong ^ (aLong >>> 32) ); 103 } 104 105 /** 106 * floats. 107 * @param aSeed the hash value to append to 108 * @param aFloat the value to hash 109 * @return the new hash value 110 */ 111 public static int hash( int aSeed , float aFloat ) { 112 return hash( aSeed, Float.floatToIntBits(aFloat) ); 113 } 114 115 /** 116 * doubles. 117 * @param aSeed the hash value to append to 118 * @param aDouble the value to hash 119 * @return the new hash value 120 */ 121 public static int hash( int aSeed , double aDouble ) { 122 return hash( aSeed, Double.doubleToLongBits(aDouble) ); 123 } 124 125 /** 126 * <code>aObject</code> is a possibly-null object field, and possibly an array. 127 * 128 * If <code>aObject</code> is an array, then each element may be a primitive 129 * or a possibly-null object. 130 * 131 * @param aSeed the hash value to append to 132 * @param aObject the value to hash 133 * @return the new hash value 134 */ 135 public static int hash( int aSeed , Object aObject ) { 136 int result = aSeed; 137 if ( aObject == null) { 138 result = hash(result, 0); 139 } 140 else if ( ! isArray(aObject) ) { 141 result = hash(result, aObject.hashCode()); 142 } 143 else { 144 int length = Array.getLength(aObject); 145 for ( int idx = 0; idx < length; ++idx ) { 146 Object item = Array.get(aObject, idx); 147 //recursive call! 148 result = hash(result, item); 149 } 150 } 151 return result; 152 } 153 154 /** 155 * boolean array 156 * 157 * @param aSeed the hash value to append to 158 * @param array the value to hash 159 * @return the new hash value 160 */ 161 public static int hash(int aSeed, boolean[] array){ 162 int result = aSeed; 163 for ( int idx = 0; idx < array.length; ++idx ) { 164 result = hash(result, array[idx]); 165 } 166 return result; 167 } 168 169 170 /// PRIVATE /// 171 private static final int fODD_PRIME_NUMBER = 37; 172 173 private static int firstTerm( int aSeed ){ 174 return fODD_PRIME_NUMBER * aSeed; 175 } 176 177 private static boolean isArray(Object aObject){ 178 return aObject.getClass().isArray(); 179 } 180}