mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
classlib: add the copyOf method to List, Set and Map interfaces (#708)
The copyOf static method was added in Java 10 to the List, Set and Map interfaces. Since no additions were made since Java 10 this commit brings the List, Set and Map interfaces to 100% completion for the latest LTS (Java 17) at the time of writing.
This commit is contained in:
parent
d209a5f02e
commit
a409763f76
|
@ -155,4 +155,8 @@ public interface TList<E> extends TCollection<E> {
|
||||||
}
|
}
|
||||||
return new TTemplateCollections.ImmutableArrayList<>(elements.clone());
|
return new TTemplateCollections.ImmutableArrayList<>(elements.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static <E> TList<E> copyOf(TCollection<? extends E> collection) {
|
||||||
|
return new TTemplateCollections.ImmutableArrayList<>(collection);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -294,4 +294,13 @@ public interface TMap<K, V> {
|
||||||
static <K, V> TMap.Entry<K, V> entry(K k, V v) {
|
static <K, V> TMap.Entry<K, V> entry(K k, V v) {
|
||||||
return new TTemplateCollections.ImmutableEntry<>(requireNonNull(k), requireNonNull(v));
|
return new TTemplateCollections.ImmutableEntry<>(requireNonNull(k), requireNonNull(v));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
static <K, V> TMap<K, V> copyOf(TMap<? extends K, ? extends V> map) {
|
||||||
|
if (map instanceof TTemplateCollections.NEtriesMap) {
|
||||||
|
return (TTemplateCollections.NEtriesMap<K, V>) map;
|
||||||
|
} else {
|
||||||
|
return new TTemplateCollections.NEtriesMap<>(map);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,4 +73,8 @@ public interface TSet<E> extends TCollection<E> {
|
||||||
static <E> TSet<E> of(E... elements) {
|
static <E> TSet<E> of(E... elements) {
|
||||||
return new TTemplateCollections.NElementSet<>(elements);
|
return new TTemplateCollections.NElementSet<>(elements);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static <E> TSet<E> copyOf(TCollection<E> collection) {
|
||||||
|
return new TTemplateCollections.NElementSet<>(collection);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,23 @@ public final class TTemplateCollections {
|
||||||
this.list = list;
|
this.list = list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public ImmutableArrayList(TCollection<? extends T> collection) {
|
||||||
|
T[] list = (T[]) new Object[collection.size()];
|
||||||
|
|
||||||
|
TIterator<? extends T> iter = collection.iterator();
|
||||||
|
int index = 0;
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
T element = iter.next();
|
||||||
|
if (element == null) {
|
||||||
|
throw new NullPointerException();
|
||||||
|
}
|
||||||
|
list[index++] = element;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.list = list;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public T get(int index) {
|
public T get(int index) {
|
||||||
return list[index];
|
return list[index];
|
||||||
|
@ -193,6 +210,9 @@ public final class TTemplateCollections {
|
||||||
static class NElementSet<T> extends AbstractImmutableSet<T> {
|
static class NElementSet<T> extends AbstractImmutableSet<T> {
|
||||||
private T[] data;
|
private T[] data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws an exception on duplicate elements.
|
||||||
|
*/
|
||||||
@SafeVarargs
|
@SafeVarargs
|
||||||
NElementSet(T... data) {
|
NElementSet(T... data) {
|
||||||
T[] table = data.clone();
|
T[] table = data.clone();
|
||||||
|
@ -232,6 +252,42 @@ public final class TTemplateCollections {
|
||||||
this.data = table;
|
this.data = table;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Duplicate elements will be ignored (does NOT throw an exception).
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
NElementSet(TCollection<T> collection) {
|
||||||
|
T[] temp = (T[]) new Object[collection.size()];
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
outerLoop:
|
||||||
|
for (T element : (T[]) collection.toArray()) {
|
||||||
|
if (element == null) {
|
||||||
|
throw new NullPointerException(String.format("Element at index %s is null", index));
|
||||||
|
}
|
||||||
|
|
||||||
|
int indexTemp = Math.abs(element.hashCode()) % temp.length;
|
||||||
|
while (temp[indexTemp] != null) {
|
||||||
|
if (temp[indexTemp].equals(element)) {
|
||||||
|
continue outerLoop;
|
||||||
|
}
|
||||||
|
indexTemp = (indexTemp + 1) % temp.length;
|
||||||
|
}
|
||||||
|
temp[indexTemp] = element;
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
T[] result = (T[]) new Object[index];
|
||||||
|
index = 0;
|
||||||
|
for (T element : temp) {
|
||||||
|
if (element != null) {
|
||||||
|
result[index++] = element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.data = result;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TIterator<T> iterator() {
|
public TIterator<T> iterator() {
|
||||||
return new TIterator<T>() {
|
return new TIterator<T>() {
|
||||||
|
@ -416,17 +472,31 @@ public final class TTemplateCollections {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@SafeVarargs
|
@SafeVarargs
|
||||||
NEtriesMap(Entry<K, V>... data) {
|
NEtriesMap(Entry<K, V>... data) {
|
||||||
Entry<K, V>[] table = new Entry[data.length];
|
this.data = toEntryArray(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
NEtriesMap(TMap<? extends K, ? extends V> map) {
|
||||||
|
this.data = toEntryArray(map.entrySet().toArray(new TMap.Entry[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an array where the {@code Entry} elements are positioned in such a way they
|
||||||
|
* are compatible with the contract of {@link java.util.Map}.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private Entry<K, V>[] toEntryArray(Entry<K, V>[] entries) {
|
||||||
|
Entry<K, V>[] table = new Entry[entries.length];
|
||||||
Arrays.fill(table, null);
|
Arrays.fill(table, null);
|
||||||
|
|
||||||
for (Entry<K, V> entry : data) {
|
for (Entry<K, V> entry : entries) {
|
||||||
Objects.requireNonNull(entry.getKey());
|
Objects.requireNonNull(entry.getKey());
|
||||||
Objects.requireNonNull(entry.getValue());
|
Objects.requireNonNull(entry.getValue());
|
||||||
|
|
||||||
int suggestedIndex = Math.abs(entry.getKey().hashCode()) % data.length;
|
int suggestedIndex = Math.abs(entry.getKey().hashCode()) % entries.length;
|
||||||
int index = suggestedIndex;
|
int index = suggestedIndex;
|
||||||
boolean found = false;
|
boolean found = false;
|
||||||
while (index < data.length) {
|
while (index < entries.length) {
|
||||||
Entry<K, V> existingEntry = table[index];
|
Entry<K, V> existingEntry = table[index];
|
||||||
if (existingEntry == null) {
|
if (existingEntry == null) {
|
||||||
found = true;
|
found = true;
|
||||||
|
@ -451,7 +521,7 @@ public final class TTemplateCollections {
|
||||||
table[index] = new ImmutableEntry<>(entry.getKey(), entry.getValue());
|
table[index] = new ImmutableEntry<>(entry.getKey(), entry.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
this.data = table;
|
return table;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -19,6 +19,8 @@ import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
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.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
@ -48,6 +50,24 @@ public class ListTest {
|
||||||
List.of("q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "a"));
|
List.of("q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "a"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void copyOfWorks() {
|
||||||
|
testOf(new String[0], List.copyOf(new ArrayList<>()));
|
||||||
|
testOf(new String[] { "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "a" },
|
||||||
|
List.copyOf(Arrays.asList("q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "a")));
|
||||||
|
|
||||||
|
try {
|
||||||
|
// copyOf() must throw a NullPointerException on any 'null' element.
|
||||||
|
List<String> listWithNull = new ArrayList<>(1);
|
||||||
|
listWithNull.add(null);
|
||||||
|
|
||||||
|
List.copyOf(listWithNull);
|
||||||
|
fail("Expected NullPointerException");
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
// ok
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void testOf(String[] expected, List<String> actual) {
|
private void testOf(String[] expected, List<String> actual) {
|
||||||
if (actual.size() != expected.length) {
|
if (actual.size() != expected.length) {
|
||||||
fail("Expected size is " + expected.length + ", actual size is " + actual.size());
|
fail("Expected size is " + expected.length + ", actual size is " + actual.size());
|
||||||
|
|
|
@ -18,6 +18,7 @@ package org.teavm.classlib.java.util;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertSame;
|
||||||
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.HashMap;
|
||||||
|
@ -54,6 +55,24 @@ public class MapTest {
|
||||||
Map.entry("p", 9), Map.entry("a", 10)));
|
Map.entry("p", 9), Map.entry("a", 10)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void copyOfWorks() {
|
||||||
|
testOf(new String[0], Map.copyOf(new HashMap<>()));
|
||||||
|
testOf(new String[] { "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "a" },
|
||||||
|
Map.copyOf(
|
||||||
|
Map.ofEntries(Map.entry("q", 0), Map.entry("w", 1), Map.entry("e", 2), Map.entry("r", 3),
|
||||||
|
Map.entry("t", 4), Map.entry("y", 5), Map.entry("u", 6), Map.entry("i", 7), Map.entry("o", 8),
|
||||||
|
Map.entry("p", 9), Map.entry("a", 10))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void copyOfOptimized() {
|
||||||
|
Map<String, Integer> mapCopy1 = Map.copyOf(Map.of("q", 0, "w", 1, "e", 2));
|
||||||
|
Map<String, Integer> mapCopy2 = Map.copyOf(mapCopy1);
|
||||||
|
|
||||||
|
assertSame("Must not create copies of immutable collections", mapCopy1, mapCopy2);
|
||||||
|
}
|
||||||
|
|
||||||
private void testOf(String[] expected, Map<String, Integer> actual) {
|
private void testOf(String[] expected, Map<String, Integer> actual) {
|
||||||
if (actual.size() != expected.length) {
|
if (actual.size() != expected.length) {
|
||||||
fail("Expected size is " + expected.length + ", actual size is " + actual.size());
|
fail("Expected size is " + expected.length + ", actual size is " + actual.size());
|
||||||
|
|
|
@ -20,6 +20,8 @@ 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.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -61,6 +63,24 @@ public class SetTest {
|
||||||
expectIAE(() -> Set.of("q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "q"));
|
expectIAE(() -> Set.of("q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "q"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void copyOfWorks() {
|
||||||
|
testOf(new String[0], Set.copyOf(new HashSet<>()));
|
||||||
|
testOf(new String[] { "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "a" },
|
||||||
|
Set.copyOf(Arrays.asList("q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "a")));
|
||||||
|
// Duplicates must be silently removed by copyOf(). Unlike of() where they throw an exception.
|
||||||
|
testOf(new String[] { "q", "e", "r", "u", "i", "o", "p" },
|
||||||
|
Set.copyOf(Arrays.asList("q", "q", "e", "r", "q", "q", "u", "i", "o", "p", "q")));
|
||||||
|
|
||||||
|
try {
|
||||||
|
// copyOf() must throw a NullPointerException on any 'null' element.
|
||||||
|
Set.copyOf(Arrays.asList("q", "q", "e", "r", "q", "q", "u", "i", "o", "p", "q", null));
|
||||||
|
fail("Expected NullPointerException");
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
// ok
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void expectIAE(Runnable r) {
|
private void expectIAE(Runnable r) {
|
||||||
try {
|
try {
|
||||||
r.run();
|
r.run();
|
||||||
|
|
Loading…
Reference in New Issue
Block a user