introduce uniform Map binding functions

This commit is contained in:
Jonathan Shook 2020-07-17 18:38:48 -05:00
parent b5879e2aff
commit 6d99deba3e
8 changed files with 500 additions and 4 deletions

View File

@ -44,10 +44,10 @@ public class VirtDataConversions {
}
public static <F, T> List<T> adaptFunctionList(F[] funcs, Class<T> resultType, Class<Object>... resultSignature) {
public static <F, T> List<T> adaptFunctionList(F[] funcs, Class<T> functionType, Class<Object>... resultSignature) {
List<T> functions = new ArrayList<>();
for (Object func : funcs) {
T adapted = adaptFunction(func, resultType, resultSignature);
T adapted = adaptFunction(func, functionType, resultSignature);
functions.add(adapted);
}
return functions;
@ -62,14 +62,14 @@ public class VirtDataConversions {
* @param resultSignature The signature of all output types, linearized for use after type-erasure.
* @return An instance of T
*/
public static <F, T> T adaptFunction(F func, Class<T> resultType, Class<?>... resultSignature) {
public static <F, T> T adaptFunction(F func, Class<T> functionType, Class<?>... resultSignature) {
FuncType funcType = FuncType.valueOf(func.getClass());
List<Class<?>> signature = new ArrayList<>();
List<Class<?>> fromSignature = linearizeObjectSignature(func);
List<Class<?>> resultTypes = new ArrayList<>();
resultTypes.add(resultType);
resultTypes.add(functionType);
for (Class<?> aClass : resultSignature) {
resultTypes.add(aClass);
}
@ -141,6 +141,26 @@ public class VirtDataConversions {
}
}
/**
* Slice the incoming object list into a set of functions, based on a grouping interval and an offset.
* @param mod The grouping interval, or modulo to slice the function groups into
* @param offset The offset within the group for the provided function
* @param funcs A list of source objects to convert to functions.
* @return
*/
public static <T> List<T> getFunctions(int mod, int offset, Class<? extends T> functionType, Object... funcs) {
// if ((funcs.length%mod)!=0) {
// throw new RuntimeException("uneven division of functions, where multiples of " + mod + " are expected.");
// }
List<T> functions = new ArrayList<>();
for (int i = offset; i < funcs.length; i+=mod) {
Object func = funcs[i];
T longFunction = VirtDataConversions.adaptFunction(func, functionType, Object.class);
functions.add(longFunction);
}
return functions;
}
private static boolean isAssignableFromTo(List<Class<?>> fromSignature, List<Class<?>> toSignature) {
if (fromSignature.size() != toSignature.size()) {
return false;

View File

@ -0,0 +1,55 @@
package io.nosqlbench.virtdata.library.basics.shared.from_long.to_collection;
import io.nosqlbench.virtdata.api.annotations.Categories;
import io.nosqlbench.virtdata.api.annotations.Category;
import io.nosqlbench.virtdata.api.annotations.Example;
import io.nosqlbench.virtdata.api.annotations.ThreadSafeMapper;
import io.nosqlbench.virtdata.api.bindings.VirtDataConversions;
import io.nosqlbench.virtdata.api.bindings.VirtDataFunctions;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.function.LongFunction;
/**
* Create a Map from a long input based on a set of provided key and value functions.
* Any duplicate entries produced by the key functions are elided.
*
* As a 'Pair-wise' function, the size of the resulting collection is determined directly by the
* number of provided element functions. Since this is a map, the functions come in pairs, each
* even numbered function is a key function and each odd numbered function is the corresponding value function.
*
* As neither a 'Stepped' nor a 'Hashed' function, the input value used by each key and value function is the same
* as that provided to the outer function.
*/
@Categories({Category.collections})
@ThreadSafeMapper
public class MapFunctions implements LongFunction<java.util.Map<Object,Object>> {
private final List<LongFunction> valueFuncs;
private final List<LongFunction> keyFuncs;
private final int size;
@Example({
"MapFunctions(NumberNameToString(),NumberNameToString(),ToString(),ToString())",
"Create a map of object values. Produces values like {'one':'one'1:1}."
})
public MapFunctions(Object... funcs) {
this.keyFuncs = VirtDataConversions.getFunctions(2, 0, LongFunction.class, funcs);
this.valueFuncs = VirtDataConversions.getFunctions(2,1,LongFunction.class, funcs);
this.size = valueFuncs.size();
}
@Override
public java.util.Map<Object,Object> apply(long value) {
java.util.Map<Object,Object> map = new HashMap<>(size);
for (int i = 0; i < size; i++) {
Object keyObject = keyFuncs.get(i).apply(value);
Object valueObject = valueFuncs.get(i).apply(value);
map.put(keyObject,valueObject);
}
return map;
}
}

View File

@ -0,0 +1,61 @@
package io.nosqlbench.virtdata.library.basics.shared.from_long.to_collection;
import io.nosqlbench.virtdata.api.annotations.Categories;
import io.nosqlbench.virtdata.api.annotations.Category;
import io.nosqlbench.virtdata.api.annotations.Example;
import io.nosqlbench.virtdata.api.annotations.ThreadSafeMapper;
import io.nosqlbench.virtdata.api.bindings.VirtDataConversions;
import io.nosqlbench.virtdata.library.basics.shared.from_long.to_long.Hash;
import java.util.HashMap;
import java.util.List;
import java.util.function.LongFunction;
import java.util.function.LongToIntFunction;
/**
* Create a Map from a long input based on a set of provided key and value functions.
* Any duplicate entries produced by the key functions are elided.
*
* As a 'Pair-wise' function, the size of the resulting collection is determined directly by the
* number of provided element functions. Since this is a map, the functions come in pairs, each
* even numbered function is a key function and each odd numbered function is the corresponding value function.
*
* As a 'Hashed' function, the input value is hashed again before being used by each key and value function.
*/
@Categories({Category.collections})
@ThreadSafeMapper
public class MapHashed implements LongFunction<java.util.Map<Object,Object>> {
private final List<LongFunction> valueFuncs;
private final List<LongFunction> keyFuncs;
private final Hash hasher = new Hash();
private final int size;
@Example({
"MapHashed(NumberNameToString(),NumberNameToString(),ToString(),ToString())",
"Create a map of object values. Produces values like {'one':'one','4464361019114304900','4464361019114304900'}."
})
public MapHashed(Object... funcs) {
this.keyFuncs = VirtDataConversions.getFunctions(2, 0, LongFunction.class, funcs);
this.valueFuncs = VirtDataConversions.getFunctions(2,1, LongFunction.class, funcs);
this.size = keyFuncs.size();
}
@Override
public java.util.Map<Object,Object> apply(long value) {
long hash = value;
java.util.Map<Object,Object> map = new HashMap<>(size);
for (int i = 0; i < size; i++) {
int keySelector = Math.min(i, keyFuncs.size() - 1);
int valSelector = Math.min(i, valueFuncs.size() -1);
hash = hasher.applyAsLong(hash);
Object keyObject = keyFuncs.get(keySelector).apply(hash);
Object valueObject = valueFuncs.get(valSelector).apply(hash);
map.put(keyObject,valueObject);
}
return map;
}
}

View File

@ -0,0 +1,58 @@
package io.nosqlbench.virtdata.library.basics.shared.from_long.to_collection;
import io.nosqlbench.virtdata.api.annotations.Categories;
import io.nosqlbench.virtdata.api.annotations.Category;
import io.nosqlbench.virtdata.api.annotations.Example;
import io.nosqlbench.virtdata.api.annotations.ThreadSafeMapper;
import io.nosqlbench.virtdata.api.bindings.VirtDataConversions;
import java.util.HashMap;
import java.util.List;
import java.util.function.LongFunction;
import java.util.function.LongToIntFunction;
/**
* Create a Map from a long input based on a set of provided key and value functions.
* Any duplicate entries produced by the key functions are elided.
*
* As a 'Sized' function, the first argument is a function which determines the size of the resulting map.
* Additional functions provided are used to generate the elements to add to the collection, as in the pair-wise
* mode of {@link MapFunctions}. If the size is larger than the number of provided functions, the last provided
* function is used repeatedly as needed. (respectively for key functions as well as value functions)
*
* As neither a 'Stepped' nor a 'Hashed' function, the input value used by each key and value function is the same
* as that provided to the outer function.
*/
@Categories({Category.collections})
@ThreadSafeMapper
public class MapSized implements LongFunction<java.util.Map<Object,Object>> {
private final List<LongFunction> valueFuncs;
private final List<LongFunction> keyFuncs;
private final LongToIntFunction sizeFunc;
@Example({
"MapSized(1, NumberNameToString(),NumberNameToString(),ToString(),ToString())",
"Create a map of object values. Produces values like {'one':'one'1:1}."
})
public MapSized(Object sizeFunc, Object... funcs) {
this.sizeFunc = VirtDataConversions.adaptFunction(sizeFunc, LongToIntFunction.class);
this.keyFuncs = VirtDataConversions.getFunctions(2, 0, LongFunction.class, funcs);
this.valueFuncs = VirtDataConversions.getFunctions(2,1,LongFunction.class, funcs);
}
@Override
public java.util.Map<Object,Object> apply(long value) {
int size = sizeFunc.applyAsInt(value);
java.util.Map<Object,Object> map = new HashMap<>(size);
for (int i = 0; i < size; i++) {
int keySelector = Math.min(i, keyFuncs.size() - 1);
int valSelector = Math.min(i, valueFuncs.size() -1);
Object keyObject = keyFuncs.get(keySelector).apply(value);
Object valueObject = valueFuncs.get(valSelector).apply(value);
map.put(keyObject,valueObject);
}
return map;
}
}

View File

@ -0,0 +1,67 @@
package io.nosqlbench.virtdata.library.basics.shared.from_long.to_collection;
import io.nosqlbench.virtdata.api.annotations.Categories;
import io.nosqlbench.virtdata.api.annotations.Category;
import io.nosqlbench.virtdata.api.annotations.Example;
import io.nosqlbench.virtdata.api.annotations.ThreadSafeMapper;
import io.nosqlbench.virtdata.api.bindings.VirtDataConversions;
import io.nosqlbench.virtdata.library.basics.shared.from_long.to_long.Hash;
import java.util.HashMap;
import java.util.List;
import java.util.function.LongFunction;
import java.util.function.LongToIntFunction;
/**
* Create a Map from a long input based on a set of provided key and value functions.
* Any duplicate entries produced by the key functions are elided.
*
* As a 'Sized' function, the first argument is a function which determines the size of the resulting map.
* Additional functions provided are used to generate the elements to add to the collection, as in the pair-wise
* mode of {@link MapFunctions}. If the size is larger than the number of provided functions, the last provided
* function is used repeatedly as needed. (respectively for key functions as well as value functions)
*
* As a 'Hashed' function, the input value is hashed again before being used by each key and value function.
*/
@Categories({Category.collections})
@ThreadSafeMapper
public class MapSizedHashed implements LongFunction<java.util.Map<Object,Object>> {
private final List<LongFunction> valueFuncs;
private final List<LongFunction> keyFuncs;
private final LongToIntFunction sizeFunc;
private final Hash hasher = new Hash();
@Example({
"MapSizedHashed(1, NumberNameToString(),NumberNameToString(),ToString(),ToString())",
"Create a map of object values. Produces values like {'one':'one'1:1}."
})
@Example({
"MapSizedHashed(HashRange(3,5), NumberNameToString(),NumberNameToString())",
"Create a map of object values. Produces values like {'one':'one'1:1}."
})
public MapSizedHashed(Object sizeFunc, Object... funcs) {
this.sizeFunc = VirtDataConversions.adaptFunction(sizeFunc, LongToIntFunction.class);
this.keyFuncs = VirtDataConversions.getFunctions(2, 0, LongFunction.class, funcs);
this.valueFuncs = VirtDataConversions.getFunctions(2,1, LongFunction.class, funcs);
}
@Override
public java.util.Map<Object,Object> apply(long value) {
int size = sizeFunc.applyAsInt(value);
long hash = value;
java.util.Map<Object,Object> map = new HashMap<>(size);
for (int i = 0; i < size; i++) {
int keySelector = Math.min(i, keyFuncs.size() - 1);
int valSelector = Math.min(i, valueFuncs.size() -1);
hash = hasher.applyAsLong(hash);
Object keyObject = keyFuncs.get(keySelector).apply(hash);
Object valueObject = valueFuncs.get(valSelector).apply(hash);
map.put(keyObject,valueObject);
}
return map;
}
}

View File

@ -0,0 +1,57 @@
package io.nosqlbench.virtdata.library.basics.shared.from_long.to_collection;
import io.nosqlbench.virtdata.api.annotations.Categories;
import io.nosqlbench.virtdata.api.annotations.Category;
import io.nosqlbench.virtdata.api.annotations.Example;
import io.nosqlbench.virtdata.api.annotations.ThreadSafeMapper;
import io.nosqlbench.virtdata.api.bindings.VirtDataConversions;
import java.util.HashMap;
import java.util.List;
import java.util.function.LongFunction;
import java.util.function.LongToIntFunction;
/**
* Create a Map from a long input based on a set of provided key and value functions.
* Any duplicate entries produced by the key functions are elided.
*
* As a 'Sized' function, the first argument is a function which determines the size of the resulting map.
* Additional functions provided are used to generate the elements to add to the collection, as in the pair-wise
* mode of {@link MapFunctions}. If the size is larger than the number of provided functions, the last provided
* function is used repeatedly as needed. (respectively for key functions as well as value functions)
*
* As a 'Stepped' function, the input value is incremented before being used by each key or value function.
*/
@Categories({Category.collections})
@ThreadSafeMapper
public class MapSizedStepped implements LongFunction<java.util.Map<Object,Object>> {
private final List<LongFunction> valueFuncs;
private final List<LongFunction> keyFuncs;
private final LongToIntFunction sizeFunc;
@Example({
"MapSizedStepped(1, NumberNameToString(),NumberNameToString())",
"Create a map of object values. Produces values like {'one':'one'1:1}."
})
public MapSizedStepped(Object sizeFunc, Object... funcs) {
this.sizeFunc = VirtDataConversions.adaptFunction(sizeFunc, LongToIntFunction.class);
this.keyFuncs = VirtDataConversions.getFunctions(2, 0, LongFunction.class, funcs);
this.valueFuncs = VirtDataConversions.getFunctions(2,1, LongFunction.class, funcs);
}
@Override
public java.util.Map<Object,Object> apply(long value) {
int size = sizeFunc.applyAsInt(value);
java.util.Map<Object,Object> map = new HashMap<>(size);
for (int i = 0; i < size; i++) {
int keySelector = Math.min(i, keyFuncs.size() - 1);
int valSelector = Math.min(i, valueFuncs.size() -1);
Object keyObject = keyFuncs.get(keySelector).apply(value+i);
Object valueObject = valueFuncs.get(valSelector).apply(value+i);
map.put(keyObject,valueObject);
}
return map;
}
}

View File

@ -0,0 +1,55 @@
package io.nosqlbench.virtdata.library.basics.shared.from_long.to_collection;
import io.nosqlbench.virtdata.api.annotations.Categories;
import io.nosqlbench.virtdata.api.annotations.Category;
import io.nosqlbench.virtdata.api.annotations.Example;
import io.nosqlbench.virtdata.api.annotations.ThreadSafeMapper;
import io.nosqlbench.virtdata.api.bindings.VirtDataConversions;
import java.util.HashMap;
import java.util.List;
import java.util.function.LongFunction;
import java.util.function.LongToIntFunction;
/**
* Create a Map from a long input based on a set of provided key and value functions.
* Any duplicate entries produced by the key functions are elided.
*
* As a 'Pair-wise' function, the size of the resulting collection is determined directly by the
* number of provided element functions. Since this is a map, the functions come in pairs, each
* even numbered function is a key function and each odd numbered function is the corresponding value function.
*
* As a 'Stepped' function, the input value is incremented before being used by each key or value function.
*/
@Categories({Category.collections})
@ThreadSafeMapper
public class MapStepped implements LongFunction<java.util.Map<Object,Object>> {
private final List<LongFunction> valueFuncs;
private final List<LongFunction> keyFuncs;
private final int size;
@Example({
"MapStepped(1, NumberNameToString(),NumberNameToString(),ToString(),ToString())",
"Create a map of object values. Produces values like {'one':'one'1:1}."
})
public MapStepped(Object... funcs) {
this.keyFuncs = VirtDataConversions.getFunctions(2, 0, LongFunction.class, funcs);
this.valueFuncs = VirtDataConversions.getFunctions(2,1,LongFunction.class, funcs);
this.size = keyFuncs.size();
}
@Override
public java.util.Map<Object,Object> apply(long value) {
java.util.Map<Object,Object> map = new HashMap<>(size);
for (int i = 0; i < size; i++) {
int keySelector = Math.min(i, keyFuncs.size() - 1);
int valSelector = Math.min(i, valueFuncs.size() -1);
Object keyObject = keyFuncs.get(keySelector).apply(value+i);
Object valueObject = valueFuncs.get(valSelector).apply(value+i);
map.put(keyObject,valueObject);
}
return map;
}
}

View File

@ -0,0 +1,123 @@
package io.nosqlbench.virtdata.library.basics.shared.from_long.to_collection;
import org.junit.Test;
import java.util.Map;
import java.util.function.LongFunction;
import java.util.function.LongUnaryOperator;
import static org.assertj.core.api.Assertions.assertThat;
public class MapFunctionsTest {
@Test
public void testMapFunctions() {
MapFunctions mf1 = new MapFunctions((LongUnaryOperator) l -> l, (LongUnaryOperator) m -> m);
Map<Object, Object> mv1 = mf1.apply(3L);
assertThat(mv1).containsAllEntriesOf(Map.of(3L, 3L));
MapFunctions mf2 = new MapFunctions(
(LongFunction<String>) a -> "Ayyy",
(LongFunction<String>) b -> "Byyy",
(LongFunction<Double>) c -> 123.456d,
(LongFunction<Double>) d -> 789.1011d);
Map<Object, Object> mv2 = mf2.apply(13L);
assertThat(mv2).containsAllEntriesOf(Map.of("Ayyy", "Byyy", 123.456d, 789.1011d));
}
@Test
public void testMapSized() {
MapSized mf1 = new MapSized(
(LongUnaryOperator) s -> s,
(LongFunction<Double>) c -> 123.456d,
(LongFunction<Double>) d -> (double) d,
(LongFunction<String>) String::valueOf,
(LongFunction<String>) b -> "Byyy"
);
Map<Object, Object> mv1 = mf1.apply(5L);
assertThat(mv1).containsAllEntriesOf(Map.of(
"5", "Byyy",
123.456d, 5.0
));
// Notice that the trailing function is used repeatedly, which affects
// when duplicate values occur, as compared to the above function.
MapSized mf2 = new MapSized(
(LongUnaryOperator) s -> s,
(LongFunction<String>) String::valueOf,
(LongFunction<String>) b -> "Byyy",
(LongFunction<Double>) c -> 123.456d,
(LongFunction<Double>) d -> (double) d);
Map<Object, Object> mv2 = mf2.apply(5L);
assertThat(mv2).containsAllEntriesOf(Map.of(
"5", "Byyy",
123.456d, 5.0
));
}
@Test
public void testMapSizedStepped() {
MapSizedStepped mf2 = new MapSizedStepped(
(LongUnaryOperator) s -> s,
(LongFunction<Double>) a -> 123.456d,
(LongFunction<String>) b -> "Byyy",
(LongFunction<String>) String::valueOf,
(LongFunction<Double>) d -> (double) d);
Map<Object, Object> mv2 = mf2.apply(5L);
assertThat(mv2).containsAllEntriesOf(Map.of(
123.456, "Byyy",
"6", 6.0d,
"7", 7.0d,
"8", 8.0d,
"9", 9.0d
));
}
@Test
public void testMapStepped() {
MapStepped mf2 = new MapStepped(
(LongFunction<String>) String::valueOf,
(LongFunction<Double>) d -> (double) d,
(LongFunction<String>) String::valueOf,
(LongFunction<Double>) d -> (double) d
);
Map<Object, Object> mv2 = mf2.apply(5L);
assertThat(mv2).containsAllEntriesOf(Map.of(
"5", 5.0d,
"6", 6.0d
));
}
@Test
public void testMapHashed() {
MapHashed mf2 = new MapHashed(
(LongFunction<String>) String::valueOf,
(LongFunction<String>) String::valueOf,
(LongFunction<String>) String::valueOf,
(LongFunction<String>) String::valueOf
);
Map<Object, Object> mv2 = mf2.apply(5L);
assertThat(mv2).containsAllEntriesOf(Map.of(
"4464361019114304900","4464361019114304900",
"44643610191143049001","44643610191143049001"
));
}
@Test
public void testMapSizedHashed() {
MapSizedHashed mf2 = new MapSizedHashed(
(LongUnaryOperator) s -> s,
(LongFunction<String>) String::valueOf,
(LongFunction<String>) String::valueOf);
Map<Object, Object> mv2 = mf2.apply(5L);
assertThat(mv2).containsAllEntriesOf(
Map.of(
"4464361019114304900", "4464361019114304900"
)
);
}
}