classlib: add Collectors grouping by

This commit is contained in:
Ivan Hetman 2023-05-16 10:57:13 +03:00 committed by GitHub
parent c39efdc6d7
commit 697ad73762
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 100 additions and 0 deletions

View File

@ -34,6 +34,7 @@ package org.teavm.classlib.java.util;
import java.util.Arrays; import java.util.Arrays;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer; import java.util.function.Consumer;
import org.teavm.classlib.java.io.TSerializable; import org.teavm.classlib.java.io.TSerializable;
import org.teavm.classlib.java.lang.*; import org.teavm.classlib.java.lang.*;
@ -668,6 +669,23 @@ public class THashMap<K, V> extends TAbstractMap<K, V> implements TCloneable, TS
} }
} }
@Override
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
if (elementCount > 0) {
int prevModCount = modCount;
for (int i = 0; i < elementData.length; i++) {
HashEntry<K, V> entry = elementData[i];
while (entry != null) {
entry.value = function.apply(entry.key, entry.value);
entry = entry.next;
if (prevModCount != modCount) {
throw new TConcurrentModificationException();
}
}
}
}
}
static int computeHashCode(Object key) { static int computeHashCode(Object key) {
return key.hashCode(); return key.hashCode();
} }

View File

@ -170,6 +170,14 @@ public interface TMap<K, V> {
} }
} }
default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
TIterator<Entry<K, V>> iterator = entrySet().iterator();
while (iterator.hasNext()) {
TMap.Entry<K, V> next = iterator.next();
next.setValue(function.apply(next.getKey(), next.getValue()));
}
}
static <K, V> TMap<K, V> of() { static <K, V> TMap<K, V> of() {
return TCollections.emptyMap(); return TCollections.emptyMap();
} }

View File

@ -124,6 +124,55 @@ public final class TCollectors {
TCollector.Characteristics.IDENTITY_FINISH); TCollector.Characteristics.IDENTITY_FINISH);
} }
public static <E, K> TCollector<E, ?, Map<K, List<E>>> groupingBy(Function<? super E, ? extends K> keyExtractor) {
return groupingBy(keyExtractor, toList());
}
public static <E, K, V, I> TCollector<E, ?, Map<K, V>> groupingBy(
Function<? super E, ? extends K> keyExtractor,
TCollector<? super E, I, V> downstream) {
return groupingBy(keyExtractor, HashMap::new, downstream);
}
public static <E, K, V, I, M extends Map<K, V>> TCollector<E, ?, M> groupingBy(
Function<? super E, ? extends K> keyExtractor,
Supplier<M> mapFactory,
TCollector<? super E, I, V> downstream) {
BiConsumer<Map<K, I>, E> mapAppender = (m, t) -> {
K key = keyExtractor.apply(t);
I container = m.computeIfAbsent(key, k -> downstream.supplier().get());
downstream.accumulator().accept(container, t);
};
BinaryOperator<Map<K, I>> mapMerger = (m1, m2) -> {
for (Map.Entry<K, I> e : m2.entrySet()) {
m1.merge(e.getKey(), e.getValue(), downstream.combiner());
}
return m1;
};
if (downstream.characteristics().contains(TCollector.Characteristics.IDENTITY_FINISH)) {
return TCollector.of(castFactory(mapFactory), mapAppender, mapMerger,
castFunction(Function.identity()), TCollector.Characteristics.IDENTITY_FINISH);
} else {
Function<I, I> replacer = castFunction(downstream.finisher());
Function<Map<K, I>, M> finisher = toReplace -> {
toReplace.replaceAll((k, v) -> replacer.apply(v));
return (M) toReplace;
};
return TCollector.of(castFactory(mapFactory), mapAppender, mapMerger, finisher);
}
}
@SuppressWarnings("unchecked")
private static <A, C> Supplier<A> castFactory(Supplier<C> supp) {
return (Supplier<A>) supp;
}
@SuppressWarnings("unchecked")
private static <A, B, C, D> Function<A, B> castFunction(Function<C, D> func) {
return (Function<A, B>) func;
}
public static <T, A, R, K> TCollector<T, A, K> collectingAndThen( public static <T, A, R, K> TCollector<T, A, K> collectingAndThen(
TCollector<T, A, R> downstream, TCollector<T, A, R> downstream,
Function<R, K> finisher) { Function<R, K> finisher) {

View File

@ -20,7 +20,9 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.TreeMap;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.teavm.junit.TeaVMTestRunner; import org.teavm.junit.TeaVMTestRunner;
@ -122,4 +124,15 @@ public class MapTest {
assertNull("Iterator did not return all of expected elements", e); assertNull("Iterator did not return all of expected elements", e);
} }
} }
@Test
public void testReplaceAll() {
Map<String, Integer> base = Map.of("a", 1, "b", 2);
Map<String, Integer> hashMap = new HashMap<>(base);
Map<String, Integer> treeMap = new TreeMap<>(base);
hashMap.replaceAll((k, v) -> v * 10);
treeMap.replaceAll((k, v) -> v * 10);
assertEquals(Map.of("a", 10, "b", 20), hashMap);
assertEquals(Map.of("a", 10, "b", 20), treeMap);
}
} }

View File

@ -78,4 +78,16 @@ public class CollectorsTest {
assertEquals(expected, assertEquals(expected,
IntStream.range(1, 4).boxed().collect(Collectors.toMap(Function.identity(), Function.identity()))); IntStream.range(1, 4).boxed().collect(Collectors.toMap(Function.identity(), Function.identity())));
} }
@Test
public void groupingBy() {
List<Integer> numbers = List.of(1, 2, 2, 3, 3, 3, 4, 4, 4, 4);
assertEquals(Map.of(1, List.of(1), 2, List.of(2, 2),
3, List.of(3, 3, 3), 4, List.of(4, 4, 4, 4)),
numbers.stream().collect(Collectors.groupingBy(Function.identity())));
assertEquals(Map.of(1, 1, 2, 4, 3, 9, 4, 16),
numbers.stream().collect(Collectors.groupingBy(Function.identity(),
Collectors.collectingAndThen(Collectors.toList(),
l -> l.stream().mapToInt(i -> i).sum()))));
}
} }