You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
New user here. Thank you for the library. Not sure if this is useful to anyone, but I thought you might like feedback on other uses of the code:
The first thing I wanted to do with it was add the ability to hash an "almost arbitrary" Object with "minimal" HashFunnel. Where "almost arbitrary" means consisting of primitives, Collections, etc. Ideally, I wanted a put(Object obj) without a HashFunnel. This method would check the type of the argument and call the corresponding method. And it would walk arrays, collections and maps recursively. And when it encounter a class it did not know how to deal with, then hash the classname and the toString() method. I came up with:
/** * Designed for primitive objects and arrays/maps/collections of primitives. * If we fail to recognize the types of instance, we use toString() */publicHashStream128putObject(@NullableObjectobj) {
if (obj == null) {
putNull();
} elseif (obj.getClass().isArray()) {
putArrayPriv(obj);
} elseif ((objinstanceofList)) {
putList((List<?>) obj);
} elseif ((objinstanceofSet)) {
putSet((Set<?>) obj);
} elseif ((objinstanceofMap)) {
putMap((Map<?, ?>) obj);
} elseif (objinstanceofStringstr) {
putString(str);
} elseif (objinstanceofIntegerint_) {
putInt(int_.intValue());
} elseif (objinstanceofLonglng) {
putLong(lng.longValue());
} elseif (objinstanceofBooleanbool) {
putBoolean(bool.booleanValue());
} elseif (objinstanceofDoubledbl) {
putDouble(dbl.doubleValue());
} elseif (objinstanceofEnumenum_) {
putString(enum_.getClass().getCanonicalName());
putString(enum_.name());
} elseif (objinstanceofShortshrt) {
putShort(shrt.shortValue());
} elseif (objinstanceofCharacterchr) {
putChar(chr.charValue());
} elseif (objinstanceofByteobjByte) {
putByte(objByte.byteValue());
} elseif (objinstanceofFloatflt) {
putFloat(flt.floatValue());
} elseif (objinstanceofUUIDuuid) {
putUUID(uuid);
} else {
putString(obj.getClass().getCanonicalName());
putString(obj.toString());
}
returnthis;
}
/** * Want this to work for both Object[] and primitive arrays, so we take the argument as an Object type and not an array type */publicHashStream128putArray(Objectobj) {
if (obj.getClass().isArray()) {
returnputArrayPriv(obj);
}
thrownewIllegalArgumentException("Object is not an array: class=" + obj.getClass());
}
/** * We know the argument is an array, but it has to be declared as an Object, in order to handle objects and primitives. * Have to use reflection to access the items because we cannot cast to (Object[]). * Note that this method causes autoboxing, so a potential issue in high performance situations. * Alternative is to write more case statements to handle each type of primitive. */privateHashStream128putArrayPriv(Objectarr) {
intlen = Array.getLength(arr);
for (inti = 0; i < len; i++) {
putObject(Array.get(arr, i));
}
returnthis;
}
publicHashStream128putNull() {
// copy code from AbstractHashStreamputBoolean(false);
returnthis;
}
publicHashStream128putList(List<?> coll) {
intcounter = 0;
for (Objectobject : coll) {
putObject(object);
counter++;
}
putInt(counter);
returnthis;
}
publicvoidputSet(Set<?> set) {
// Need an elementHashFunction.// That requires access to the Hasher from within the HashStream, which isn't available.// Clone and reset the sink instead.// Assuming this is thread-safe.finalHashStream128hashStream = copy();
ToLongFunction<Object> elementHashFunction = obj -> {
hashStream.reset();
putObject(obj);
returnhashStream.getAsLong();
};
putUnorderedIterable(set, elementHashFunction);
}
publicHashStream128putMap(Map<?, ?> map) {
intcounter = 0;
for (Entry<?, ?> entry : map.entrySet()) {
putObject(entry.getKey());
putObject(entry.getValue());
counter++;
}
putInt(counter);
returnthis;
}
Generalizing this code so it can be written once and used by all the cases (HashStream38/64/128) identified a few issues:
The HashSink does not define copy() and reset(). So HashSink cannot be used as the "base class". Instead, we have to use HashStream32.
HashStream32 does not have a getAsLong() method, so the putSet() method needs to check the class and convert accordingly. But it is simple.
I didn't have access to the Hasher from within the HashStream so I had to clone the current HashStream in the putSet() method.
The biggest problem with this code is that I cannot get it into the main classes, so I have to write "delegate code" with my own classes. This isn't acceptable in the long run. If this code can't get into the main branch, forget it.
I also wrote the same code using the HashFunnel interface. I could have used a series of if-then-else code for the type checking but I have a utility class I call ClassMap that implements HashMap where the key is a class or interface and the value is a Consumer. This code is shown below:
/*** Accept Object type and check the type to call the correct method.* Recurse on Map, List and Set.* When a non-primitive Object is encountered, hash the classname and the toString() function*/publicclassObjectHashFunnelimplementsHashFunnel<Object> {
privatefinalClassMap<BiConsumer<Object, HashSink>> funnelMap = ClassMap.forClassesAndInterfaces(); //code not shownpublicObjectHashFunnel() {
funnelMap.put(Byte.TYPE, (byte_, sink) -> sink.putByte((byte) byte_));
funnelMap.put(byte[].class, (bytes, sink) -> sink.putBytes((byte[]) bytes));
funnelMap.put(Boolean.TYPE, (boolean_, sink) -> sink.putBoolean((boolean) boolean_));
funnelMap.put(boolean[].class, (booleans, sink) -> sink.putBooleans((boolean[]) booleans));
funnelMap.put(Short.TYPE, (short_, sink) -> sink.putShort((short) short_));
funnelMap.put(short[].class, (shorts, sink) -> sink.putShorts((short[]) shorts));
funnelMap.put(Character.TYPE, (char_, sink) -> sink.putChar((char) char_));
funnelMap.put(char[].class, (chars, sink) -> sink.putChars((char[]) chars));
funnelMap.put(String.class, (str, sink) -> sink.putString((String) str));
funnelMap.put(String[].class, (strs, sink) -> putStrings((String[]) strs, sink));
funnelMap.put(Integer.TYPE, (int_, sink) -> sink.putInt((int) int_));
funnelMap.put(int[].class, (ints, sink) -> sink.putInts((int[]) ints));
funnelMap.put(Long.TYPE, (long_, sink) -> sink.putLong((long) long_));
funnelMap.put(long[].class, (longs, sink) -> sink.putLongs((long[]) longs));
funnelMap.put(Float.TYPE, (float_, sink) -> sink.putFloat((float) float_));
funnelMap.put(float[].class, (floats, sink) -> sink.putFloats((float[]) floats));
funnelMap.put(Double.TYPE, (double_, sink) -> sink.putDouble((double) double_));
funnelMap.put(double[].class, (doubles, sink) -> sink.putDoubles((double[]) doubles));
funnelMap.put(UUID.class, (uuid, sink) -> sink.putUUID((UUID) uuid));
funnelMap.put(Object[].class, (arr, sink) -> putObjectArray((Object[]) arr, sink));
funnelMap.put(List.class, (list, sink) -> sink.putOrderedIterable((List) list, this));
funnelMap.put(Set.class, (set, sink) -> putSet((Set) set, sink));
funnelMap.put(Map.class, (map, sink) -> putMap((Map) map, sink));
}
public <T> voidaddHashFunnel(Class<T> klass, HashFunnel<T> funnel) {
BiConsumer<Object, HashSink> biCons = (obj, sink) -> sink.put((T) obj, funnel);
funnelMap.put(klass, biCons);
}
@Overridepublicvoidput(@NullableObjectobj, HashSinksink) {
if (obj == null) {
sink.putBoolean(false);
return;
}
BiConsumer<Object, HashSink> cons = funnelMap.get(obj.getClass());
if (cons != null) {
cons.accept(obj, sink);
return;
}
// No match, so default to the classname and the toString() value.sink.putString(obj.getClass().getCanonicalName());
sink.putString(obj.toString());
}
privatevoidputStrings(String[] strs, HashSinksink) {
for (Stringstring : strs) {
sink.putString(string);
}
}
privatevoidputMap(Map<?, ?> map, HashSinksink) {
if (map.isEmpty()) {
return;
}
intcounter = 0;
for (Entry<?, ?> entry : map.entrySet()) {
sink.put(entry.getKey(), this);
sink.put(entry.getValue(), this);
counter++;
}
sink.putInt(counter);
}
privatevoidputObjectArray(Object[] arr, HashSinksink) {
for (Objectitem : arr) {
sink.put(item, this);
}
}
privatevoidputSet(Set<?> set, HashSinksink) {
// Need an elementHashFunction.// That requires access to the Hasher from within the HashStream, which isn't available.// Clone and reset the sink instead.// Requiring this be thread-safe.if (set.isEmpty()) {
return;
}
finalHashStream64hasStream = ((HashStream64) sink).copy();
ToLongFunction<Object> elementHashFunction = obj -> {
hasStream.reset();
hasStream.put(obj, this);
returnhasStream.getAsLong();
};
sink.putUnorderedIterable(set, elementHashFunction);
}
New user here. Thank you for the library. Not sure if this is useful to anyone, but I thought you might like feedback on other uses of the code:
The first thing I wanted to do with it was add the ability to hash an "almost arbitrary" Object with "minimal" HashFunnel. Where "almost arbitrary" means consisting of primitives, Collections, etc. Ideally, I wanted a
put(Object obj)
without a HashFunnel. This method would check the type of the argument and call the corresponding method. And it would walk arrays, collections and maps recursively. And when it encounter a class it did not know how to deal with, then hash the classname and the toString() method. I came up with:Generalizing this code so it can be written once and used by all the cases (HashStream38/64/128) identified a few issues:
copy()
andreset()
. So HashSink cannot be used as the "base class". Instead, we have to use HashStream32.getAsLong()
method, so theputSet()
method needs to check the class and convert accordingly. But it is simple.putSet()
method.The biggest problem with this code is that I cannot get it into the main classes, so I have to write "delegate code" with my own classes. This isn't acceptable in the long run. If this code can't get into the main branch, forget it.
I also wrote the same code using the HashFunnel interface. I could have used a series of if-then-else code for the type checking but I have a utility class I call
ClassMap
that implements HashMap where the key is a class or interface and the value is a Consumer. This code is shown below:Here is a simple test to exercise the code:
I have done no performance tests or looked for memory leaks. Have you done any performance comparisons with the
Objects.hash()
method?The text was updated successfully, but these errors were encountered: