/* * Copyright 2013 Goldman Sachs. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gs.collections.impl.block.factory; import com.gs.collections.api.block.HashingStrategy; import com.gs.collections.api.block.function.Function; import com.gs.collections.api.block.function.primitive.BooleanFunction; import com.gs.collections.api.block.function.primitive.ByteFunction; import com.gs.collections.api.block.function.primitive.CharFunction; import com.gs.collections.api.block.function.primitive.DoubleFunction; import com.gs.collections.api.block.function.primitive.FloatFunction; import com.gs.collections.api.block.function.primitive.IntFunction; import com.gs.collections.api.block.function.primitive.LongFunction; import com.gs.collections.api.block.function.primitive.ShortFunction; public final class HashingStrategies { private static final HashingStrategy<Object> DEFAULT_HASHING_STRATEGY = new DefaultStrategy(); private static final HashingStrategy<Object> IDENTITY_HASHING_STRATEGY = new IdentityHashingStrategy(); private HashingStrategies() { throw new AssertionError("Suppress default constructor for noninstantiability"); } public static <T> HashingStrategy<T> defaultStrategy() { return (HashingStrategy<T>) DEFAULT_HASHING_STRATEGY; } public static <T> HashingStrategy<T> nullSafeHashingStrategy(HashingStrategy<T> nonNullSafeStrategy) { return new NullSafeHashingStrategy<T>(nonNullSafeStrategy); } public static <T, V> HashingStrategy<T> fromFunction(Function<? super T, ? extends V> function) { return new FunctionHashingStrategy<T, V>(function); } public static HashingStrategy<Object> identityStrategy() { return IDENTITY_HASHING_STRATEGY; } public static <T> HashingStrategy<T> chain(HashingStrategy<T>... hashingStrategies) { if (hashingStrategies.length == 0) { throw new IllegalArgumentException("Nothing to chain"); } return new ChainedHashingStrategy<T>(hashingStrategies); } public static <T, V1, V2> HashingStrategy<T> fromFunctions(Function<? super T, ? extends V1> one, Function<? super T, ? extends V2> two) { return HashingStrategies.chain( HashingStrategies.fromFunction(one), HashingStrategies.fromFunction(two)); } public static <T, V1, V2, V3> HashingStrategy<T> fromFunctions(Function<? super T, ? extends V1> one, Function<? super T, ? extends V2> two, Function<? super T, ? extends V3> three) { return HashingStrategies.chain( HashingStrategies.fromFunction(one), HashingStrategies.fromFunction(two), HashingStrategies.fromFunction(three)); } public static <T> HashingStrategy<T> fromBooleanFunction(BooleanFunction<? super T> function) { return new BooleanFunctionHashingStrategy<T>(function); } public static <T> HashingStrategy<T> fromByteFunction(ByteFunction<? super T> function) { return new ByteFunctionHashingStrategy<T>(function); } public static <T> HashingStrategy<T> fromCharFunction(CharFunction<? super T> function) { return new CharFunctionHashingStrategy<T>(function); } public static <T> HashingStrategy<T> fromDoubleFunction(DoubleFunction<? super T> function) { return new DoubleFunctionHashingStrategy<T>(function); } public static <T> HashingStrategy<T> fromFloatFunction(FloatFunction<? super T> function) { return new FloatFunctionHashingStrategy<T>(function); } public static <T> HashingStrategy<T> fromIntFunction(IntFunction<? super T> function) { return new IntFunctionHashingStrategy<T>(function); } public static <T> HashingStrategy<T> fromLongFunction(LongFunction<? super T> function) { return new LongFunctionHashingStrategy<T>(function); } public static <T> HashingStrategy<T> fromShortFunction(ShortFunction<? super T> function) { return new ShortFunctionHashingStrategy<T>(function); } private static class DefaultStrategy implements HashingStrategy<Object> { private static final long serialVersionUID = 1L; public int computeHashCode(Object object) { return object.hashCode(); } public boolean equals(Object object1, Object object2) { return object1.equals(object2); } } private static final class NullSafeHashingStrategy<T> implements HashingStrategy<T> { private static final long serialVersionUID = 1L; private final HashingStrategy<T> nonNullSafeStrategy; private NullSafeHashingStrategy(HashingStrategy<T> nonNullSafeStrategy) { this.nonNullSafeStrategy = nonNullSafeStrategy; } public int computeHashCode(T object) { return object == null ? 0 : this.nonNullSafeStrategy.computeHashCode(object); } public boolean equals(T object1, T object2) { return object1 == null || object2 == null ? object1 == object2 : this.nonNullSafeStrategy.equals(object1, object2); } } private static final class FunctionHashingStrategy<T, V> implements HashingStrategy<T> { private static final long serialVersionUID = 1L; private final Function<? super T, ? extends V> function; private FunctionHashingStrategy(Function<? super T, ? extends V> function) { this.function = function; } public int computeHashCode(T object) { return this.function.valueOf(object).hashCode(); } public boolean equals(T object1, T object2) { return this.function.valueOf(object1).equals(this.function.valueOf(object2)); } } private static final class BooleanFunctionHashingStrategy<T> implements HashingStrategy<T> { private static final long serialVersionUID = 1L; private final BooleanFunction<? super T> function; private BooleanFunctionHashingStrategy(BooleanFunction<? super T> function) { this.function = function; } public int computeHashCode(T object) { return this.function.booleanValueOf(object) ? Boolean.TRUE.hashCode() : Boolean.FALSE.hashCode(); } public boolean equals(T object1, T object2) { return this.function.booleanValueOf(object1) == this.function.booleanValueOf(object2); } } private static final class ByteFunctionHashingStrategy<T> implements HashingStrategy<T> { private static final long serialVersionUID = 1L; private final ByteFunction<? super T> function; private ByteFunctionHashingStrategy(ByteFunction<? super T> function) { this.function = function; } public int computeHashCode(T object) { return this.function.byteValueOf(object); } public boolean equals(T object1, T object2) { return this.function.byteValueOf(object1) == this.function.byteValueOf(object2); } } private static final class CharFunctionHashingStrategy<T> implements HashingStrategy<T> { private static final long serialVersionUID = 1L; private final CharFunction<? super T> function; private CharFunctionHashingStrategy(CharFunction<? super T> function) { this.function = function; } public int computeHashCode(T object) { return this.function.charValueOf(object); } public boolean equals(T object1, T object2) { return this.function.charValueOf(object1) == this.function.charValueOf(object2); } } private static final class DoubleFunctionHashingStrategy<T> implements HashingStrategy<T> { private static final long serialVersionUID = 1L; private final DoubleFunction<? super T> function; private DoubleFunctionHashingStrategy(DoubleFunction<? super T> function) { this.function = function; } public int computeHashCode(T object) { return HashingStrategies.longHashCode(Double.doubleToLongBits(this.function.doubleValueOf(object))); } public boolean equals(T object1, T object2) { return Double.compare(this.function.doubleValueOf(object1), this.function.doubleValueOf(object2)) == 0; } } private static final class FloatFunctionHashingStrategy<T> implements HashingStrategy<T> { private static final long serialVersionUID = 1L; private final FloatFunction<? super T> function; private FloatFunctionHashingStrategy(FloatFunction<? super T> function) { this.function = function; } public int computeHashCode(T object) { return Float.floatToIntBits(this.function.floatValueOf(object)); } public boolean equals(T object1, T object2) { return Float.compare(this.function.floatValueOf(object1), this.function.floatValueOf(object2)) == 0; } } private static final class IntFunctionHashingStrategy<T> implements HashingStrategy<T> { private static final long serialVersionUID = 1L; private final IntFunction<? super T> function; private IntFunctionHashingStrategy(IntFunction<? super T> function) { this.function = function; } public int computeHashCode(T object) { return this.function.intValueOf(object); } public boolean equals(T object1, T object2) { return this.function.intValueOf(object1) == this.function.intValueOf(object2); } } private static final class LongFunctionHashingStrategy<T> implements HashingStrategy<T> { private static final long serialVersionUID = 1L; private final LongFunction<? super T> function; private LongFunctionHashingStrategy(LongFunction<? super T> function) { this.function = function; } public int computeHashCode(T object) { return HashingStrategies.longHashCode(this.function.longValueOf(object)); } public boolean equals(T object1, T object2) { return this.function.longValueOf(object1) == this.function.longValueOf(object2); } } private static final class ShortFunctionHashingStrategy<T> implements HashingStrategy<T> { private static final long serialVersionUID = 1L; private final ShortFunction<? super T> function; private ShortFunctionHashingStrategy(ShortFunction<? super T> function) { this.function = function; } public int computeHashCode(T object) { return this.function.shortValueOf(object); } public boolean equals(T object1, T object2) { return this.function.shortValueOf(object1) == this.function.shortValueOf(object2); } } private static final class IdentityHashingStrategy implements HashingStrategy<Object> { private static final long serialVersionUID = 1L; public int computeHashCode(Object object) { return System.identityHashCode(object); } public boolean equals(Object object1, Object object2) { return object1 == object2; } } private static final class ChainedHashingStrategy<T> implements HashingStrategy<T> { private static final long serialVersionUID = 1L; private final HashingStrategy<T>[] hashingStrategies; private ChainedHashingStrategy(HashingStrategy<T>... hashingStrategies) { this.hashingStrategies = hashingStrategies; } public int computeHashCode(T object) { int hashCode = this.hashingStrategies[0].computeHashCode(object); for (int i = 1; i < this.hashingStrategies.length; i++) { hashCode = hashCode * 31 + this.hashingStrategies[i].computeHashCode(object); } return hashCode; } public boolean equals(T object1, T object2) { for (HashingStrategy<T> hashingStrategy : this.hashingStrategies) { if (!hashingStrategy.equals(object1, object2)) { return false; } } return true; } } /** * This implementation is equivalent to the JDK Long hashcode because there is no public static hashCode(long value) method on Long. * This method will be introduced in Java 1.8, at which point this can be replaced. * * @param value the long value to hash * @return hashcode for long, based on the {@link Long#hashCode()} */ private static int longHashCode(long value) { return (int) (value ^ (value >>> 32)); } }