diff --git a/drv/Maps.drv b/drv/Maps.drv index 4862993f..f8ad8c5e 100644 --- a/drv/Maps.drv +++ b/drv/Maps.drv @@ -37,6 +37,11 @@ import java.util.Map; import java.util.function.Consumer; import PACKAGE.MAP.FastEntrySet; +import java.util.function.BiConsumer; +import java.util.function.BinaryOperator; +import java.util.function.Supplier; +import java.util.stream.Collector; + /** A class providing static methods and objects that do useful things with type-specific maps. * * @see java.util.Collections @@ -680,4 +685,54 @@ public final class MAPS { */ public static KEY_VALUE_GENERIC MAP KEY_VALUE_GENERIC unmodifiable(final MAP KEY_VALUE_EXTENDS_GENERIC m) { return new UnmodifiableMap KEY_VALUE_GENERIC_DIAMOND(m); } +#if KEYS_PRIMITIVE && VALUES_PRIMITIVE + static +#elif KEYS_PRIMITIVE && VALUES_REFERENCE + static > +#elif KEYS_REFERENCE && VALUES_PRIMITIVE + static > +#else + static > +#endif + BinaryOperator merger(BIFUNCTION mergeFunction) { + return (m1, m2) -> { + fastForEach( + m2, + entry -> m1.MERGE_VALUE(entry.ENTRY_GET_KEY(), entry.ENTRY_GET_VALUE(), mergeFunction) + ); + return m1; + }; + } + +#if KEYS_PRIMITIVE && VALUES_PRIMITIVE + public static Collector collector( +#elif KEYS_PRIMITIVE && VALUES_REFERENCE + public static > Collector collector( +#elif KEYS_REFERENCE && VALUES_PRIMITIVE + public static > Collector collector( +#else + public static > Collector toMap( +#endif + METHOD_GENERIC_TO_KEY keyMapper, + METHOD_GENERIC_TO_VALUE valueMapper, + BIFUNCTION mergeFunction, + Supplier creator + ) { + BiConsumer accumulator = + (map, element) -> + map.MERGE_VALUE( + METHOD_GENERIC_TO_KEY_APPLY(element), + METHOD_GENERIC_TO_VALUE_APPLY(element), +#if VALUE_CLASS_Boolean + mergeFunction::apply +#else + mergeFunction +#endif + ); + return Collector.of( + creator, + accumulator, + merger(mergeFunction) + ); + } } diff --git a/gencsource.sh b/gencsource.sh index 0baa4100..8951be5e 100755 --- a/gencsource.sh +++ b/gencsource.sh @@ -475,6 +475,33 @@ $(if [[ "${CLASS[$k]}" != "" && "${CLASS[$v]}" != "" ]]; then\ "#define VALUE_OPERATOR_APPLY apply\n"\ "#endif\n"\ \ +"#if KEYS_INT_LONG_DOUBLE\n"\ +"#define METHOD_GENERIC_TO_KEY java.util.function.To${TYPE_CAP[$k]}Function\n"\ +"#define METHOD_GENERIC_TO_KEY_APPLY keyMapper.applyAs${TYPE_CAP[$wk]}\n"\ +"#elif KEY_CLASS_Boolean\n"\ +"#define METHOD_GENERIC_TO_KEY java.util.function.Predicate\n"\ +"#define METHOD_GENERIC_TO_KEY_APPLY keyMapper.test\n"\ +"#elif KEY_CLASS_Object || KEY_CLASS_Reference\n"\ +"#define METHOD_GENERIC_TO_KEY java.util.function.Function\n"\ +"#define METHOD_GENERIC_TO_KEY_APPLY keyMapper.apply\n"\ +"#else\n"\ +"#define METHOD_GENERIC_TO_KEY it.unimi.dsi.fastutil.objects.Object2${TYPE_CAP[$k]}Function\n"\ +"#define METHOD_GENERIC_TO_KEY_APPLY keyMapper.get${TYPE_CAP[$k]}\n"\ +"#endif\n"\ +"#if VALUES_INT_LONG_DOUBLE\n"\ +"#define METHOD_GENERIC_TO_VALUE java.util.function.To${TYPE_CAP[$v]}Function\n"\ +"#define METHOD_GENERIC_TO_VALUE_APPLY valueMapper.applyAs${TYPE_CAP[$wv]}\n"\ +"#elif VALUE_CLASS_Boolean\n"\ +"#define METHOD_GENERIC_TO_VALUE java.util.function.Predicate\n"\ +"#define METHOD_GENERIC_TO_VALUE_APPLY valueMapper.test\n"\ +"#elif VALUE_CLASS_Object || VALUE_CLASS_Reference\n"\ +"#define METHOD_GENERIC_TO_VALUE java.util.function.Function\n"\ +"#define METHOD_GENERIC_TO_VALUE_APPLY valueMapper.apply\n"\ +"#else\n"\ +"#define METHOD_GENERIC_TO_VALUE it.unimi.dsi.fastutil.objects.Object2${TYPE_CAP[$v]}Function\n"\ +"#define METHOD_GENERIC_TO_VALUE_APPLY valueMapper.get${TYPE_CAP[$v]}\n"\ +"#endif\n"\ +\ \ "/* Abstract implementations (keys) */\n"\ \ @@ -522,6 +549,13 @@ $(if [[ "${CLASS[$k]}" != "" && "${CLASS[$v]}" != "" ]]; then\ "#define BIG_LISTS ${TYPE_CAP[$k]}BigLists\n"\ "#define MAPS ${TYPE_CAP[$k]}2${TYPE_CAP[$v]}Maps\n"\ "#define FUNCTIONS ${TYPE_CAP[$k]}2${TYPE_CAP[$v]}Functions\n"\ +"#if VALUES_REFERENCE\n"\ +"#define BIFUNCTION java.util.function.BiFunction\n"\ +"#elif VALUES_INT_LONG_DOUBLE\n"\ +"#define BIFUNCTION java.util.function.${TYPE_CAP[$v]}BinaryOperator\n"\ +"#else\n"\ +"#define BIFUNCTION VALUE_PACKAGE.VALUE_BINARY_OPERATOR\n"\ +"#endif\n"\ "#define SORTED_MAPS ${TYPE_CAP[$k]}2${TYPE_CAP[$v]}SortedMaps\n"\ "#define PRIORITY_QUEUES ${TYPE_CAP2[$k]}PriorityQueues\n"\ "#define HEAPS ${TYPE_CAP2[$k]}Heaps\n"\ @@ -670,7 +704,11 @@ $(if [[ "${CLASS[$k]}" != "" && "${CLASS[$v]}" != "" ]]; then\ "/* Methods (values) */\n"\ \ \ +"#if VALUE_CLASS_Boolean\n"\ +"#define MERGE_VALUE merge\n"\ +"#else\n"\ "#define MERGE_VALUE merge${TYPE_STD[$v]}\n"\ +"#endif\n"\ "#define NEXT_VALUE next${TYPE_STD[$v]}\n"\ "#define PREV_VALUE previous${TYPE_STD[$v]}\n"\ "#define READ_VALUE read${TYPE_CAP2[$v]}\n"\ diff --git a/test/it/unimi/dsi/fastutil/ints/Int2BooleanMapsTest.java b/test/it/unimi/dsi/fastutil/ints/Int2BooleanMapsTest.java new file mode 100644 index 00000000..e12e3daa --- /dev/null +++ b/test/it/unimi/dsi/fastutil/ints/Int2BooleanMapsTest.java @@ -0,0 +1,59 @@ +package it.unimi.dsi.fastutil.ints; + +import org.junit.Test; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Predicate; +import java.util.stream.Stream; + +import static org.junit.Assert.assertEquals; + +public class Int2BooleanMapsTest { + + static Predicate getPredicate() { + AtomicInteger calls = new AtomicInteger(); + return i -> { + int c = calls.getAndIncrement(); + return c % 2 == 0; + }; + } + + @Test + public void testInt2BooleanMap() { + final Int2BooleanMap result = + Stream.of(1, 2, 3, 3) + .collect( + Int2BooleanMaps.collector( + i -> i, + getPredicate(), + Boolean::logicalAnd, + Int2BooleanArrayMap::new + ) + ); + Int2BooleanMap expected = + new Int2BooleanArrayMap( + new int[] {1, 2, 3}, + new boolean[] {true, false, false} + ); + + assertEquals(result, expected); + } + + @Test + public void testInt2BooleanMapEmpty() { + final Int2BooleanMap result = + Stream.empty() + .collect( + Int2BooleanMaps.collector( + i -> i, + getPredicate(), + Boolean::logicalAnd, + Int2BooleanArrayMap::new + ) + ); + Int2BooleanMap expected = Int2BooleanMaps.EMPTY_MAP; + + assertEquals(result, expected); + } + +} diff --git a/test/it/unimi/dsi/fastutil/ints/Int2CharMapsTest.java b/test/it/unimi/dsi/fastutil/ints/Int2CharMapsTest.java new file mode 100644 index 00000000..ba53b90c --- /dev/null +++ b/test/it/unimi/dsi/fastutil/ints/Int2CharMapsTest.java @@ -0,0 +1,49 @@ +package it.unimi.dsi.fastutil.ints; + +import org.junit.Test; + +import java.util.stream.Stream; + +import static org.junit.Assert.assertEquals; + +public class Int2CharMapsTest { + + @Test + public void testInt2CharMap() { + final Int2CharMap result = + Stream.of(1, 2, 3, 3) + .collect( + Int2CharMaps.collector( + i -> (char) i.intValue(), + i -> (Character) i, + (char c0, char c1) -> (char) ((int) c0 + (int) c1), + Int2CharArrayMap::new + ) + ); + Int2CharMap expected = + new Int2CharArrayMap( + new int[] {1, 2, 3}, + new char[] {'\u0001', '\u0002', '\u0006'} + ); + + assertEquals(result, expected); + } + + @Test + public void testInt2CharMapEmpty() { + final Int2CharMap result = + Stream.empty() + .collect( + Int2CharMaps.collector( + i -> (char) i.intValue(), + i -> (Character) i, + (char c0, char c1) -> (char) ((int) c0 + (int) c1), + Int2CharArrayMap::new + ) + ); + Int2CharMap expected = Int2CharMaps.EMPTY_MAP; + + assertEquals(result, expected); + } + +} diff --git a/test/it/unimi/dsi/fastutil/ints/Int2IntMapsTest.java b/test/it/unimi/dsi/fastutil/ints/Int2IntMapsTest.java index 7157954b..4af2e66f 100644 --- a/test/it/unimi/dsi/fastutil/ints/Int2IntMapsTest.java +++ b/test/it/unimi/dsi/fastutil/ints/Int2IntMapsTest.java @@ -24,6 +24,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.Set; +import java.util.stream.Stream; import org.junit.Test; @@ -75,4 +76,42 @@ public void testFastIteratorHelpers() { Int2IntMaps.fastIterable(m).forEach(s::add); assertEquals(1, s.size()); // Should be always the same entry, mutated } + + @Test + public void testInt2IntMap() { + final Int2IntMap result = + Stream.of(1, 2, 3, 3) + .collect( + Int2IntMaps.collector( + i -> i, + i -> i, + Integer::sum, + Int2IntArrayMap::new + ) + ); + Int2IntMap expected = + new Int2IntArrayMap( + new int[] {1, 2, 3}, + new int[] {1, 2, 6} + ); + + assertEquals(result, expected); + } + + @Test + public void testInt2IntMapEmpty() { + final Int2IntMap result = + Stream.empty() + .collect( + Int2IntMaps.collector( + i -> i, + i -> i, + Integer::sum, + Int2IntArrayMap::new + ) + ); + Int2IntMap expected = Int2IntMaps.EMPTY_MAP; + + assertEquals(result, expected); + } }