Java 9 collection factories (#478)

Adds the collection factories List.of(...), Set.of(...), Map.of(...) and Map.ofEntries(...) which were introduced to the JDK in Java 9
This commit is contained in:
Kris Scheibe 2020-03-02 09:20:43 +01:00 committed by GitHub
parent 60d0ad902c
commit 160fa2b1fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 473 additions and 1 deletions

View File

@ -0,0 +1,95 @@
/*
* Copyright 2020 Alexey Andreev.
*
* 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 org.teavm.classlib.java.util;
import static java.util.Objects.requireNonNull;
import java.util.RandomAccess;
/**
* Factory-methods for List/Set/Map.of(...).
*/
class CollectionsFactory {
private CollectionsFactory() {
}
@SafeVarargs
static <E> TList<E> createList(E... elements) {
if (elements.length == 0) {
return TCollections.emptyList();
}
// don't permit null elements
for (E element : elements) {
requireNonNull(element, "element");
}
return new ImmutableArrayList<>(elements);
}
@SafeVarargs
static <E> TSet<E> createSet(E... elements) {
if (elements.length == 0) {
return TCollections.emptySet();
}
// don't permit null or duplicate elements
final TSet<E> set = new THashSet<>();
for (E element : elements) {
if (!set.add(requireNonNull(element, "element"))) {
throw new IllegalArgumentException("duplicate element: " + element);
}
}
return TCollections.unmodifiableSet(set);
}
@SafeVarargs
static <K, V> TMap<K, V> createMap(TMap.Entry<K, V>... entries) {
if (entries.length == 0) {
return TCollections.emptyMap();
}
// don't permit null or duplicate keys and don't permit null values
final TMap<K, V> map = new THashMap<>();
for (TMap.Entry<K, V> entry : entries) {
if (map.put(requireNonNull(entry.getKey(), "key"), requireNonNull(entry.getValue(), "value")) != null) {
throw new IllegalArgumentException("duplicate key: " + entry.getKey());
}
}
return TCollections.unmodifiableMap(map);
}
static class ImmutableArrayList<T> extends TAbstractList<T> implements RandomAccess {
private final T[] list;
public ImmutableArrayList(T[] list) {
this.list = list;
}
@Override
public T get(int index) {
return list[index];
}
@Override
public int size() {
return list.length;
}
}
}

View File

@ -15,6 +15,7 @@
*/
package org.teavm.classlib.java.util;
import java.util.Arrays;
import org.teavm.classlib.java.util.function.TUnaryOperator;
public interface TList<E> extends TCollection<E> {
@ -48,4 +49,55 @@ public interface TList<E> extends TCollection<E> {
default void sort(TComparator<? super E> c) {
TCollections.sort(this, c);
}
static <E> TList<E> of() {
return TCollections.emptyList();
}
static <E> TList<E> of(E e) {
return CollectionsFactory.createList(e);
}
static <E> TList<E> of(E e1, E e2) {
return CollectionsFactory.createList(e1, e2);
}
static <E> TList<E> of(E e1, E e2, E e3) {
return CollectionsFactory.createList(e1, e2, e3);
}
static <E> TList<E> of(E e1, E e2, E e3, E e4) {
return CollectionsFactory.createList(e1, e2, e3, e4);
}
static <E> TList<E> of(E e1, E e2, E e3, E e4, E e5) {
return CollectionsFactory.createList(e1, e2, e3, e4, e5);
}
static <E> TList<E> of(E e1, E e2, E e3, E e4, E e5, E e6) {
return CollectionsFactory.createList(e1, e2, e3, e4, e5, e6);
}
static <E> TList<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7) {
return CollectionsFactory.createList(e1, e2, e3, e4, e5, e6, e7);
}
static <E> TList<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8) {
return CollectionsFactory.createList(e1, e2, e3, e4, e5, e6, e7, e8);
}
static <E> TList<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9) {
return CollectionsFactory.createList(e1, e2, e3, e4, e5, e6, e7, e8, e9);
}
static <E> TList<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10) {
return CollectionsFactory.createList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);
}
@SafeVarargs
static <E> TList<E> of(E... elements) {
// the returned list reuses the given array
// create a copy to prevent modifying the list by modifying the original array
return CollectionsFactory.createList(Arrays.copyOf(elements, elements.length));
}
}

View File

@ -15,6 +15,9 @@
*/
package org.teavm.classlib.java.util;
import static java.util.Objects.requireNonNull;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
@ -45,6 +48,14 @@ public interface TMap<K, V> {
V remove(Object key);
default boolean remove(Object key, Object value) {
if (containsKey(key) && Objects.equals(get(key), value)) {
remove(key);
return true;
}
return false;
}
void putAll(TMap<? extends K, ? extends V> m);
void clear();
@ -54,7 +65,7 @@ public interface TMap<K, V> {
TCollection<V> values();
TSet<Entry<K, V>> entrySet();
default boolean replace(K key, V value, V newValue) {
if (containsKey(key) && TObjects.equals(get(key), value)) {
put(key, newValue);
@ -134,4 +145,134 @@ public interface TMap<K, V> {
}
return newValue;
}
default void forEach(BiConsumer<? super K, ? super V> action) {
final TIterator<Entry<K, V>> iterator = entrySet().iterator();
while (iterator.hasNext()) {
final Entry<K, V> entry = iterator.next();
action.accept(entry.getKey(), entry.getValue());
}
}
static <K, V> TMap<K, V> of() {
return TCollections.emptyMap();
}
static <K, V> TMap<K, V> of(K k1, V v1) {
return CollectionsFactory.createMap(
entry(k1, v1)
);
}
static <K, V> TMap<K, V> of(K k1, V v1, K k2, V v2) {
return CollectionsFactory.createMap(
entry(k1, v1),
entry(k2, v2)
);
}
static <K, V> TMap<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3) {
return CollectionsFactory.createMap(
entry(k1, v1),
entry(k2, v2),
entry(k3, v3)
);
}
static <K, V> TMap<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) {
return CollectionsFactory.createMap(
entry(k1, v1),
entry(k2, v2),
entry(k3, v3),
entry(k4, v4)
);
}
static <K, V> TMap<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) {
return CollectionsFactory.createMap(
entry(k1, v1),
entry(k2, v2),
entry(k3, v3),
entry(k4, v4),
entry(k5, v5)
);
}
static <K, V> TMap<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6) {
return CollectionsFactory.createMap(
entry(k1, v1),
entry(k2, v2),
entry(k3, v3),
entry(k4, v4),
entry(k5, v5),
entry(k6, v6)
);
}
static <K, V> TMap<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5,
K k6, V v6, K k7, V v7) {
return CollectionsFactory.createMap(
entry(k1, v1),
entry(k2, v2),
entry(k3, v3),
entry(k4, v4),
entry(k5, v5),
entry(k6, v6),
entry(k7, v7)
);
}
static <K, V> TMap<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5,
K k6, V v6, K k7, V v7, K k8, V v8) {
return CollectionsFactory.createMap(
entry(k1, v1),
entry(k2, v2),
entry(k3, v3),
entry(k4, v4),
entry(k5, v5),
entry(k6, v6),
entry(k7, v7),
entry(k8, v8)
);
}
static <K, V> TMap<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5,
K k6, V v6, K k7, V v7, K k8, V v8, K k9, V v9) {
return CollectionsFactory.createMap(
entry(k1, v1),
entry(k2, v2),
entry(k3, v3),
entry(k4, v4),
entry(k5, v5),
entry(k6, v6),
entry(k7, v7),
entry(k8, v8),
entry(k9, v9)
);
}
static <K, V> TMap<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5,
K k6, V v6, K k7, V v7, K k8, V v8, K k9, V v9, K k10, V v10) {
return CollectionsFactory.createMap(
entry(k1, v1),
entry(k2, v2),
entry(k3, v3),
entry(k4, v4),
entry(k5, v5),
entry(k6, v6),
entry(k7, v7),
entry(k8, v8),
entry(k9, v9),
entry(k10, v10)
);
}
@SafeVarargs
static <K, V> TMap<K, V> ofEntries(TMap.Entry<K, V>... entries) {
return CollectionsFactory.createMap(entries);
}
static <K, V> TMap.Entry<K, V> entry(K k, V v) {
return new TMapEntry<>(requireNonNull(k), requireNonNull(v));
}
}

View File

@ -21,4 +21,54 @@ package org.teavm.classlib.java.util;
* @param <E>
*/
public interface TSet<E> extends TCollection<E> {
static <E> TSet<E> of() {
return TCollections.emptySet();
}
static <E> TSet<E> of(E e) {
return CollectionsFactory.createSet(e);
}
static <E> TSet<E> of(E e1, E e2) {
return CollectionsFactory.createSet(e1, e2);
}
static <E> TSet<E> of(E e1, E e2, E e3) {
return CollectionsFactory.createSet(e1, e2, e3);
}
static <E> TSet<E> of(E e1, E e2, E e3, E e4) {
return CollectionsFactory.createSet(e1, e2, e3, e4);
}
static <E> TSet<E> of(E e1, E e2, E e3, E e4, E e5) {
return CollectionsFactory.createSet(e1, e2, e3, e4, e5);
}
static <E> TSet<E> of(E e1, E e2, E e3, E e4, E e5, E e6) {
return CollectionsFactory.createSet(e1, e2, e3, e4, e5, e6);
}
static <E> TSet<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7) {
return CollectionsFactory.createSet(e1, e2, e3, e4, e5, e6, e7);
}
static <E> TSet<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8) {
return CollectionsFactory.createSet(e1, e2, e3, e4, e5, e6, e7, e8);
}
static <E> TSet<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9) {
return CollectionsFactory.createSet(e1, e2, e3, e4, e5, e6, e7, e8, e9);
}
static <E> TSet<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10) {
return CollectionsFactory.createSet(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);
}
@SafeVarargs
static <E> TSet<E> of(E... elements) {
return CollectionsFactory.createSet(elements);
}
}

View File

@ -0,0 +1,134 @@
/*
* Copyright 2020 Alexey Andreev.
*
* 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 org.teavm.classlib.java.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import org.junit.Test;
public class CollectionsFactoryTest {
@Test
public void createList() {
assertEquals(
TArrays.asList(1, 2, 3),
CollectionsFactory.createList(1, 2, 3)
);
}
@Test
public void createList_zero_elements() {
assertSame(CollectionsFactory.createList(), TCollections.emptyList());
}
@Test(expected = NullPointerException.class)
public void createList_null_element() {
CollectionsFactory.createList(new Object[] { null });
}
@Test(expected = NullPointerException.class)
public void createList_null_array() {
CollectionsFactory.createList((Object[]) null);
}
@Test
public void createSet() {
assertEquals(
new THashSet<>(TArrays.asList(1, 2, 3)),
CollectionsFactory.createSet(1, 2, 3)
);
}
@Test(expected = IllegalArgumentException.class)
public void createSet_duplicate_elements() {
CollectionsFactory.createSet(1, 1, 1);
}
@Test
public void createSet_zero_elements() {
assertSame(CollectionsFactory.createSet(), TCollections.emptySet());
}
@Test(expected = NullPointerException.class)
public void createSet_null_element() {
CollectionsFactory.createSet(new Object[] { null });
}
@Test(expected = NullPointerException.class)
public void createSet_null_array() {
CollectionsFactory.createSet((Object[]) null);
}
@Test
public void createMap() {
final THashMap<Integer, String> hashMap = new THashMap<>();
hashMap.put(1, "one");
hashMap.put(2, "two");
hashMap.put(3, "three");
assertEquals(
hashMap,
CollectionsFactory.createMap(
TMap.entry(1, "one"),
TMap.entry(2, "two"),
TMap.entry(3, "three")
)
);
}
@Test(expected = NullPointerException.class)
public void createMap_null_key() {
CollectionsFactory.createMap(TMap.entry(null, "value"));
}
@Test(expected = IllegalArgumentException.class)
public void createMap_duplicate_key() {
CollectionsFactory.createMap(
TMap.entry(1, "value"),
TMap.entry(1, "another value")
);
}
@Test(expected = NullPointerException.class)
public void createMap_null_value() {
CollectionsFactory.createMap(TMap.entry("key", null));
}
@Test
public void createMap_duplicate_value() {
final THashMap<Integer, String> hashMap = new THashMap<>();
hashMap.put(1, "value");
hashMap.put(2, "value");
assertEquals(
hashMap,
CollectionsFactory.createMap(
TMap.entry(1, "value"),
TMap.entry(2, "value")
)
);
}
@Test
public void createMap_zero_elements() {
assertSame(CollectionsFactory.createMap(), TCollections.emptyMap());
}
@Test(expected = NullPointerException.class)
public void createMap_null_array() {
CollectionsFactory.createMap((TMap.Entry<Object, Object>[]) null);
}
}