Proper implementation for List.of, Set.of, Map.of/ofEntries

This commit is contained in:
Alexey Andreev 2020-10-07 16:32:20 +03:00
parent 74cc1d8d7d
commit 55ba9be16a
11 changed files with 1236 additions and 321 deletions

View File

@ -1,95 +0,0 @@
/*
* 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

@ -76,7 +76,7 @@ public abstract class TAbstractMap<K, V> extends TObject implements TMap<K, V> {
@Override
public String toString() {
return String.valueOf(getKey()) + "=" + String.valueOf(getValue());
return getKey() + "=" + getValue();
}
}
@ -127,7 +127,7 @@ public abstract class TAbstractMap<K, V> extends TObject implements TMap<K, V> {
@Override
public String toString() {
return String.valueOf(getKey()) + "=" + String.valueOf(getValue());
return getKey() + "=" + getValue();
}
}

View File

@ -21,7 +21,7 @@ import org.teavm.classlib.java.util.TMap.Entry;
public class TCollections extends TObject {
@SuppressWarnings("rawtypes")
public static final TSet EMPTY_SET = new TAbstractSet<Object>() {
public static final TSet EMPTY_SET = new TTemplateCollections.AbstractImmutableSet<Object>() {
@Override public int size() {
return 0;
}
@ -40,7 +40,7 @@ public class TCollections extends TObject {
};
@SuppressWarnings("rawtypes")
public static final TMap EMPTY_MAP = new TAbstractMap<Object, Object>() {
public static final TMap EMPTY_MAP = new TTemplateCollections.AbstractImmutableMap<Object, Object>() {
@Override public TSet<Entry<Object, Object>> entrySet() {
return emptySet();
}
@ -63,7 +63,7 @@ public class TCollections extends TObject {
};
@SuppressWarnings("rawtypes")
public static final TList EMPTY_LIST = new TAbstractList<Object>() {
public static final TList EMPTY_LIST = new TTemplateCollections.AbstractImmutableList<Object>() {
@Override public Object get(int index) {
throw new TIndexOutOfBoundsException();
}
@ -164,47 +164,12 @@ public class TCollections extends TObject {
return (TMap<K, V>) EMPTY_MAP;
}
public static <T> TList<T> singletonList(final T o) {
return new TAbstractList<T>() {
@Override public T get(int index) {
if (index != 0) {
throw new TIndexOutOfBoundsException();
}
return o;
}
@Override public int size() {
return 1;
}
};
public static <T> TList<T> singletonList(T o) {
return new TTemplateCollections.SingleElementList<>(o);
}
public static <T> TSet<T> singleton(final T o) {
return new TAbstractSet<T>() {
@Override public int size() {
return 1;
}
@Override public TIterator<T> iterator() {
return new TIterator<T>() {
private boolean read;
@Override public boolean hasNext() {
return !read;
}
@Override public T next() {
if (read) {
throw new TNoSuchElementException();
}
read = true;
return o;
}
@Override public void remove() {
throw new TUnsupportedOperationException();
}
};
}
@Override public boolean contains(Object o2) {
return TObjects.equals(o, o2);
}
};
public static <T> TSet<T> singleton(T o) {
return new TTemplateCollections.SingleElementSet<>(o);
}
public static <K, V> TMap<K, V> singletonMap(final K key, final V value) {

View File

@ -15,7 +15,7 @@
*/
package org.teavm.classlib.java.util;
import java.util.Arrays;
import java.util.Objects;
import org.teavm.classlib.java.util.function.TUnaryOperator;
public interface TList<E> extends TCollection<E> {
@ -55,49 +55,104 @@ public interface TList<E> extends TCollection<E> {
}
static <E> TList<E> of(E e) {
return CollectionsFactory.createList(e);
return TCollections.singletonList(e);
}
static <E> TList<E> of(E e1, E e2) {
return CollectionsFactory.createList(e1, e2);
Objects.requireNonNull(e1);
Objects.requireNonNull(e2);
return new TTemplateCollections.TwoElementsList<>(e1, e2);
}
static <E> TList<E> of(E e1, E e2, E e3) {
return CollectionsFactory.createList(e1, e2, e3);
Objects.requireNonNull(e1);
Objects.requireNonNull(e2);
Objects.requireNonNull(e3);
return new TTemplateCollections.ImmutableArrayList<>(e1, e2, e3);
}
static <E> TList<E> of(E e1, E e2, E e3, E e4) {
return CollectionsFactory.createList(e1, e2, e3, e4);
Objects.requireNonNull(e1);
Objects.requireNonNull(e2);
Objects.requireNonNull(e3);
Objects.requireNonNull(e4);
return new TTemplateCollections.ImmutableArrayList<>(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);
Objects.requireNonNull(e1);
Objects.requireNonNull(e2);
Objects.requireNonNull(e3);
Objects.requireNonNull(e4);
Objects.requireNonNull(e5);
return new TTemplateCollections.ImmutableArrayList<>(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);
Objects.requireNonNull(e1);
Objects.requireNonNull(e2);
Objects.requireNonNull(e3);
Objects.requireNonNull(e4);
Objects.requireNonNull(e5);
Objects.requireNonNull(e6);
return new TTemplateCollections.ImmutableArrayList<>(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);
Objects.requireNonNull(e1);
Objects.requireNonNull(e2);
Objects.requireNonNull(e3);
Objects.requireNonNull(e4);
Objects.requireNonNull(e5);
Objects.requireNonNull(e6);
Objects.requireNonNull(e7);
return new TTemplateCollections.ImmutableArrayList<>(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);
Objects.requireNonNull(e1);
Objects.requireNonNull(e2);
Objects.requireNonNull(e3);
Objects.requireNonNull(e4);
Objects.requireNonNull(e5);
Objects.requireNonNull(e6);
Objects.requireNonNull(e7);
Objects.requireNonNull(e8);
return new TTemplateCollections.ImmutableArrayList<>(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);
Objects.requireNonNull(e1);
Objects.requireNonNull(e2);
Objects.requireNonNull(e3);
Objects.requireNonNull(e4);
Objects.requireNonNull(e5);
Objects.requireNonNull(e6);
Objects.requireNonNull(e7);
Objects.requireNonNull(e8);
Objects.requireNonNull(e9);
return new TTemplateCollections.ImmutableArrayList<>(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);
Objects.requireNonNull(e1);
Objects.requireNonNull(e2);
Objects.requireNonNull(e3);
Objects.requireNonNull(e4);
Objects.requireNonNull(e5);
Objects.requireNonNull(e6);
Objects.requireNonNull(e7);
Objects.requireNonNull(e8);
Objects.requireNonNull(e9);
Objects.requireNonNull(e10);
return new TTemplateCollections.ImmutableArrayList<>(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));
for (E element : elements) {
Objects.requireNonNull(element);
}
return new TTemplateCollections.ImmutableArrayList<>(elements.clone());
}
}

View File

@ -159,20 +159,15 @@ public interface TMap<K, V> {
}
static <K, V> TMap<K, V> of(K k1, V v1) {
return CollectionsFactory.createMap(
entry(k1, v1)
);
return new TTemplateCollections.SingleEntryMap<>(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)
);
return new TTemplateCollections.TwoEntriesMap<>(k1, v1, k2, v2);
}
static <K, V> TMap<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3) {
return CollectionsFactory.createMap(
return new TTemplateCollections.NEtriesMap<>(
entry(k1, v1),
entry(k2, v2),
entry(k3, v3)
@ -180,7 +175,7 @@ public interface TMap<K, V> {
}
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(
return new TTemplateCollections.NEtriesMap<>(
entry(k1, v1),
entry(k2, v2),
entry(k3, v3),
@ -189,7 +184,7 @@ public interface TMap<K, V> {
}
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(
return new TTemplateCollections.NEtriesMap<>(
entry(k1, v1),
entry(k2, v2),
entry(k3, v3),
@ -199,7 +194,7 @@ public interface TMap<K, V> {
}
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(
return new TTemplateCollections.NEtriesMap<>(
entry(k1, v1),
entry(k2, v2),
entry(k3, v3),
@ -211,7 +206,7 @@ public interface TMap<K, V> {
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(
return new TTemplateCollections.NEtriesMap<>(
entry(k1, v1),
entry(k2, v2),
entry(k3, v3),
@ -224,7 +219,7 @@ public interface TMap<K, V> {
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(
return new TTemplateCollections.NEtriesMap<>(
entry(k1, v1),
entry(k2, v2),
entry(k3, v3),
@ -238,7 +233,7 @@ public interface TMap<K, V> {
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(
return new TTemplateCollections.NEtriesMap<>(
entry(k1, v1),
entry(k2, v2),
entry(k3, v3),
@ -253,7 +248,7 @@ public interface TMap<K, V> {
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(
return new TTemplateCollections.NEtriesMap<>(
entry(k1, v1),
entry(k2, v2),
entry(k3, v3),
@ -269,10 +264,10 @@ public interface TMap<K, V> {
@SafeVarargs
static <K, V> TMap<K, V> ofEntries(TMap.Entry<K, V>... entries) {
return CollectionsFactory.createMap(entries);
return new TTemplateCollections.NEtriesMap<>(entries);
}
static <K, V> TMap.Entry<K, V> entry(K k, V v) {
return new TMapEntry<>(requireNonNull(k), requireNonNull(v));
return new TTemplateCollections.ImmutableEntry<>(requireNonNull(k), requireNonNull(v));
}
}

View File

@ -15,11 +15,8 @@
*/
package org.teavm.classlib.java.util;
/**
*
* @author Alexey Andreev
* @param <E>
*/
import java.util.Objects;
public interface TSet<E> extends TCollection<E> {
static <E> TSet<E> of() {
@ -27,48 +24,53 @@ public interface TSet<E> extends TCollection<E> {
}
static <E> TSet<E> of(E e) {
return CollectionsFactory.createSet(e);
Objects.requireNonNull(e);
return new TTemplateCollections.SingleElementSet<>(e);
}
static <E> TSet<E> of(E e1, E e2) {
return CollectionsFactory.createSet(e1, e2);
Objects.requireNonNull(e1);
Objects.requireNonNull(e2);
if (e1.equals(e2)) {
throw new IllegalArgumentException();
}
return new TTemplateCollections.TwoElementsSet<>(e1, e2);
}
static <E> TSet<E> of(E e1, E e2, E e3) {
return CollectionsFactory.createSet(e1, e2, e3);
return new TTemplateCollections.NElementSet<>(e1, e2, e3);
}
static <E> TSet<E> of(E e1, E e2, E e3, E e4) {
return CollectionsFactory.createSet(e1, e2, e3, e4);
return new TTemplateCollections.NElementSet<>(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);
return new TTemplateCollections.NElementSet<>(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);
return new TTemplateCollections.NElementSet<>(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);
return new TTemplateCollections.NElementSet<>(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);
return new TTemplateCollections.NElementSet<>(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);
return new TTemplateCollections.NElementSet<>(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);
return new TTemplateCollections.NElementSet<>(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);
}
@SafeVarargs
static <E> TSet<E> of(E... elements) {
return CollectionsFactory.createSet(elements);
return new TTemplateCollections.NElementSet<>(elements);
}
}

View File

@ -0,0 +1,764 @@
/*
* 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 java.util.Arrays;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.RandomAccess;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
class TTemplateCollections {
private TTemplateCollections() {
}
static class ImmutableArrayList<T> extends AbstractImmutableList<T> implements RandomAccess {
private final T[] list;
@SafeVarargs
ImmutableArrayList(T... list) {
this.list = list;
}
@Override
public T get(int index) {
return list[index];
}
@Override
public int size() {
return list.length;
}
}
static class SingleElementList<T> extends AbstractImmutableList<T> implements RandomAccess {
private T value;
SingleElementList(T value) {
this.value = value;
}
@Override
public int size() {
return 1;
}
@Override
public T get(int index) {
if (index == 0) {
return value;
}
throw new IndexOutOfBoundsException();
}
@Override
public void sort(TComparator<? super T> c) {
}
}
static class TwoElementsList<T> extends AbstractImmutableList<T> implements RandomAccess {
private T first;
private T second;
TwoElementsList(T first, T second) {
this.first = first;
this.second = second;
}
@Override
public int size() {
return 2;
}
@Override
public T get(int index) {
if (index == 0) {
return first;
} else if (index == 1) {
return second;
}
throw new IndexOutOfBoundsException();
}
}
static class SingleElementSet<T> extends AbstractImmutableSet<T> {
private T element;
SingleElementSet(T element) {
this.element = element;
}
@Override
public TIterator<T> iterator() {
return new TIterator<T>() {
private boolean more = true;
@Override
public boolean hasNext() {
return more;
}
@Override
public T next() {
if (!more) {
throw new NoSuchElementException();
}
more = false;
return element;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@Override
public int size() {
return 1;
}
@Override
public boolean contains(Object o) {
return Objects.equals(o, element);
}
}
static class TwoElementsSet<T> extends AbstractImmutableSet<T> {
private T first;
private T second;
TwoElementsSet(T first, T second) {
this.first = first;
this.second = second;
}
@Override
public boolean contains(Object o) {
return Objects.equals(o, first) || Objects.equals(o, second);
}
@Override
public TIterator<T> iterator() {
return new TIterator<T>() {
private int index;
@Override
public boolean hasNext() {
return index < 2;
}
@Override
public T next() {
switch (index++) {
case 0:
return first;
case 1:
return second;
default:
throw new NoSuchElementException();
}
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@Override
public int size() {
return 2;
}
}
static class NElementSet<T> extends AbstractImmutableSet<T> {
private T[] data;
@SafeVarargs
NElementSet(T... data) {
T[] table = data.clone();
Arrays.fill(table, null);
for (T element : data) {
Objects.requireNonNull(element);
int suggestedIndex = element.hashCode() % data.length;
int index = suggestedIndex;
boolean found = false;
while (index < data.length) {
T existingElement = table[index];
if (existingElement == null) {
found = true;
break;
} else if (existingElement.equals(element)) {
throw new IllegalArgumentException();
}
++index;
}
if (!found) {
index = 0;
while (index < suggestedIndex) {
T existingElement = table[index];
if (existingElement == null) {
break;
} else if (existingElement.equals(element)) {
throw new IllegalArgumentException();
}
++index;
}
}
table[index] = element;
}
this.data = table;
}
@Override
public TIterator<T> iterator() {
return new TIterator<T>() {
private int index;
@Override
public boolean hasNext() {
return index < data.length;
}
@Override
public T next() {
if (index >= data.length) {
throw new NoSuchElementException();
}
return data[index++];
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@Override
public int size() {
return data.length;
}
@Override
public boolean contains(Object o) {
if (data.length == 0 || o == null) {
return false;
}
int suggestedIndex = o.hashCode() % data.length;
for (int i = suggestedIndex; i < data.length; ++i) {
if (data[i].equals(o)) {
return true;
}
}
for (int i = 0; i < suggestedIndex; ++i) {
if (data[i].equals(o)) {
return true;
}
}
return false;
}
}
static class SingleEntryMap<K, V> extends AbstractImmutableMap<K, V> {
private Entry<K, V> entry;
private AbstractImmutableSet<Entry<K, V>> entrySet;
private AbstractImmutableSet<K> keySet;
private AbstractImmutableList<V> values;
SingleEntryMap(K key, V value) {
this.entry = new ImmutableEntry<>(key, value);
}
@Override
public TSet<Entry<K, V>> entrySet() {
if (entrySet == null) {
entrySet = new SingleElementSet<>(entry);
}
return entrySet;
}
@Override
public V get(Object key) {
return entry.getKey().equals(key) ? entry.getValue() : null;
}
@Override
public int size() {
return 1;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public boolean containsValue(Object value) {
return entry.getValue().equals(value);
}
@Override
public boolean containsKey(Object key) {
return entry.getKey().equals(key);
}
@Override
public TSet<K> keySet() {
if (keySet == null) {
keySet = new SingleElementSet<>(entry.getKey());
}
return keySet;
}
@Override
public TCollection<V> values() {
if (values == null) {
values = new SingleElementList<>(entry.getValue());
}
return values;
}
}
static class TwoEntriesMap<K, V> extends AbstractImmutableMap<K, V> {
private Entry<K, V> first;
private Entry<K, V> second;
private AbstractImmutableSet<Entry<K, V>> entrySet;
private AbstractImmutableSet<K> keySet;
private AbstractImmutableList<V> values;
TwoEntriesMap(K k1, V v1, K k2, V v2) {
this.first = new ImmutableEntry<>(k1, v1);
this.second = new ImmutableEntry<>(k2, v2);
}
@Override
public TSet<Entry<K, V>> entrySet() {
if (entrySet == null) {
entrySet = new TwoElementsSet<>(first, second);
}
return entrySet;
}
@Override
public V get(Object key) {
return first.getKey().equals(key)
? first.getValue()
: second.getKey().equals(key) ? second.getValue() : null;
}
@Override
public int size() {
return 2;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public boolean containsValue(Object value) {
return first.getValue().equals(value) || second.getValue().equals(value);
}
@Override
public boolean containsKey(Object key) {
return first.getKey().equals(key) || second.getKey().equals(key);
}
@Override
public TSet<K> keySet() {
if (keySet == null) {
keySet = new TwoElementsSet<>(first.getKey(), second.getKey());
}
return keySet;
}
@Override
public TCollection<V> values() {
if (values == null) {
values = new TwoElementsList<>(first.getValue(), second.getValue());
}
return values;
}
}
static class NEtriesMap<K, V> extends AbstractImmutableMap<K, V> {
private Entry<K, V>[] data;
private AbstractImmutableSet<Entry<K, V>> entrySet;
@SafeVarargs
NEtriesMap(Entry<K, V>... data) {
Entry<K, V>[] table = data.clone();
Arrays.fill(table, null);
for (Entry<K, V> entry : data) {
Objects.requireNonNull(entry.getKey());
Objects.requireNonNull(entry.getValue());
int suggestedIndex = entry == null ? 0 : entry.getKey().hashCode() % data.length;
int index = suggestedIndex;
boolean found = false;
while (index < data.length) {
Entry<K, V> existingEntry = table[index];
if (existingEntry == null) {
found = true;
break;
} else if (existingEntry.getKey().equals(entry.getKey())) {
throw new IllegalArgumentException();
}
++index;
}
if (!found) {
index = 0;
while (index < suggestedIndex) {
Entry<K, V> existingElement = table[index];
if (existingElement == null) {
break;
} else if (existingElement.getKey().equals(entry.getKey())) {
throw new IllegalArgumentException();
}
++index;
}
}
table[index] = new ImmutableEntry<>(entry.getKey(), entry.getValue());
}
this.data = table;
}
@Override
public int size() {
return data.length;
}
@Override
public boolean isEmpty() {
return data.length == 0;
}
@Override
public boolean containsValue(Object value) {
if (value == null) {
return false;
}
for (Entry<K, V> entry : data) {
if (entry.getValue().equals(value)) {
return true;
}
}
return false;
}
@Override
public boolean containsKey(Object key) {
if (key == null) {
return false;
}
int suggestedIndex = key.hashCode() % data.length;
for (int i = suggestedIndex; i < data.length; ++i) {
if (data[i].getKey().equals(key)) {
return true;
}
}
for (int i = 0; i < suggestedIndex; ++i) {
if (data[i].getKey().equals(key)) {
return true;
}
}
return false;
}
@Override
public V get(Object key) {
if (key == null) {
return null;
}
int suggestedIndex = key.hashCode() % data.length;
for (int i = suggestedIndex; i < data.length; ++i) {
Entry<K, V> entry = data[i];
if (entry.getKey().equals(key)) {
return entry.getValue();
}
}
for (int i = 0; i < suggestedIndex; ++i) {
Entry<K, V> entry = data[i];
if (entry.getKey().equals(key)) {
return entry.getValue();
}
}
return null;
}
@Override
public TSet<Entry<K, V>> entrySet() {
if (entrySet == null) {
entrySet = new AbstractImmutableSet<Entry<K, V>>() {
@Override
public int size() {
return data.length;
}
@Override
public TIterator<Entry<K, V>> iterator() {
return new TIterator<Entry<K, V>>() {
int index;
@Override
public boolean hasNext() {
return index < data.length;
}
@Override
public Entry<K, V> next() {
if (index == data.length) {
throw new NoSuchElementException();
}
return data[index++];
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@Override
public boolean contains(Object o) {
if (!(o instanceof Entry)) {
return false;
}
Entry<?, ?> e = (Entry<?, ?>) o;
Object key = e.getKey();
if (key == null) {
return false;
}
int suggestedIndex = key.hashCode() % data.length;
for (int i = suggestedIndex; i < data.length; ++i) {
Entry<K, V> entry = data[i];
if (entry.getKey().equals(key)) {
return entry.getValue().equals(e.getValue());
}
}
for (int i = 0; i < suggestedIndex; ++i) {
Entry<K, V> entry = data[i];
if (entry.getKey().equals(key)) {
return entry.getValue().equals(e.getValue());
}
}
return false;
}
};
}
return entrySet;
}
}
static abstract class AbstractImmutableList<T> extends TAbstractList<T> implements RandomAccess {
@Override
public void clear() {
throw new UnsupportedOperationException();
}
@Override
public T remove(int index) {
throw new UnsupportedOperationException();
}
@Override
protected void removeRange(int start, int end) {
throw new UnsupportedOperationException();
}
@Override
public boolean removeAll(TCollection<?> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean removeIf(Predicate<? super T> filter) {
throw new UnsupportedOperationException();
}
@Override
public boolean retainAll(TCollection<?> c) {
throw new UnsupportedOperationException();
}
}
static abstract class AbstractImmutableSet<T> extends TAbstractSet<T> {
@Override
public void clear() {
throw new UnsupportedOperationException();
}
@Override
public boolean remove(Object o) {
throw new UnsupportedOperationException();
}
@Override
public boolean removeAll(TCollection<?> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean removeIf(Predicate<? super T> filter) {
throw new UnsupportedOperationException();
}
@Override
public boolean retainAll(TCollection<?> c) {
throw new UnsupportedOperationException();
}
}
static abstract class AbstractImmutableMap<K, V> extends TAbstractMap<K, V> {
@Override
public V put(K key, V value) {
throw new UnsupportedOperationException();
}
@Override
public void putAll(TMap<? extends K, ? extends V> m) {
throw new UnsupportedOperationException();
}
@Override
public V remove(Object key) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
@Override
public boolean remove(Object key, Object value) {
throw new UnsupportedOperationException();
}
@Override
public boolean replace(K key, V value, V newValue) {
throw new UnsupportedOperationException();
}
@Override
public V replace(K key, V value) {
throw new UnsupportedOperationException();
}
@Override
public V putIfAbsent(K key, V value) {
throw new UnsupportedOperationException();
}
@Override
public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
throw new UnsupportedOperationException();
}
@Override
public V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
throw new UnsupportedOperationException();
}
@Override
public V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
throw new UnsupportedOperationException();
}
@Override
public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
throw new UnsupportedOperationException();
}
}
static class ImmutableEntry<K, V> implements TMap.Entry<K, V>, Cloneable {
K key;
V value;
ImmutableEntry(K theKey, V theValue) {
key = theKey;
value = theValue;
}
@Override
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
return null;
}
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object instanceof TMap.Entry) {
TMap.Entry<?, ?> entry = (TMap.Entry<?, ?>) object;
return (key == null ? entry.getKey() == null : key.equals(entry.getKey()))
&& (value == null ? entry.getValue() == null : value
.equals(entry.getValue()));
}
return false;
}
@Override
public K getKey() {
return key;
}
@Override
public V getValue() {
return value;
}
@Override
public int hashCode() {
return (key == null ? 0 : key.hashCode())
^ (value == null ? 0 : value.hashCode());
}
@Override
public V setValue(V object) {
throw new UnsupportedOperationException();
}
@Override
public String toString() {
return key + "=" + value;
}
}
}

View File

@ -1,134 +0,0 @@
/*
* 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);
}
}

View File

@ -0,0 +1,114 @@
/*
* Copyright 2020 konsoletyper.
*
* 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.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.teavm.junit.TeaVMTestRunner;
import org.teavm.junit.WholeClassCompilation;
@RunWith(TeaVMTestRunner.class)
@WholeClassCompilation
public class ListTest {
@Test
public void of() {
testOf(new String[0], List.of());
testOf(new String[] { "q" }, List.of("q"));
testOf(new String[] { "q", "w" }, List.of("q", "w"));
testOf(new String[] { "q", "w", "e" }, List.of("q", "w", "e"));
testOf(new String[] { "q", "w", "e", "r" }, List.of("q", "w", "e", "r"));
testOf(new String[] { "q", "w", "e", "r", "t" }, List.of("q", "w", "e", "r", "t"));
testOf(new String[] { "q", "w", "e", "r", "t", "y" }, List.of("q", "w", "e", "r", "t", "y"));
testOf(new String[] { "q", "w", "e", "r", "t", "y", "u" }, List.of("q", "w", "e", "r", "t", "y", "u"));
testOf(new String[] { "q", "w", "e", "r", "t", "y", "u", "i" },
List.of("q", "w", "e", "r", "t", "y", "u", "i"));
testOf(new String[] { "q", "w", "e", "r", "t", "y", "u", "i", "o" },
List.of("q", "w", "e", "r", "t", "y", "u", "i", "o"));
testOf(new String[] { "q", "w", "e", "r", "t", "y", "u", "i", "o", "p" },
List.of("q", "w", "e", "r", "t", "y", "u", "i", "o", "p"));
testOf(new String[] { "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "a" },
List.of("q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "a"));
}
private void testOf(String[] expected, List<String> actual) {
if (actual.size() != expected.length) {
fail("Expected size is " + expected.length + ", actual size is " + actual.size());
}
for (int i = 0; i < expected.length; ++i) {
assertEquals("Element #" + i, expected[i], actual.get(i));
}
try {
actual.get(-1);
fail("get out of bounds does not throw exception");
} catch (IndexOutOfBoundsException e) {
// ok;
}
try {
actual.get(expected.length);
fail("get out of bounds does not throw exception");
} catch (IndexOutOfBoundsException e) {
// ok;
}
try {
actual.set(0, "1");
fail("set should not work");
} catch (UnsupportedOperationException e) {
// ok;
}
try {
actual.remove(0);
fail("remove should not work");
} catch (UnsupportedOperationException e) {
// ok;
}
try {
actual.add("2");
fail("add should not work");
} catch (UnsupportedOperationException e) {
// ok;
}
try {
actual.clear();
fail("clear should not work");
} catch (UnsupportedOperationException e) {
// ok;
}
for (int i = 0; i < expected.length; ++i) {
assertEquals("indexOf of element #" + i + " is correct", i, actual.indexOf(expected[i]));
}
for (String value : expected) {
assertTrue("contains returns true for existing elements", actual.contains(value));
}
assertFalse("contains return false for non-existing element", actual.contains("*"));
assertEquals("isEmpty works properly", expected.length == 0, actual.isEmpty());
}
}

View File

@ -0,0 +1,125 @@
/*
* Copyright 2020 konsoletyper.
*
* 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.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.teavm.junit.TeaVMTestRunner;
import org.teavm.junit.WholeClassCompilation;
@RunWith(TeaVMTestRunner.class)
@WholeClassCompilation
public class MapTest {
@Test
public void of() {
testOf(new String[0], Map.of());
testOf(new String[] { "q" }, Map.of("q", 0));
testOf(new String[] { "q", "w" }, Map.of("q", 0, "w", 1));
testOf(new String[] { "q", "w", "e" }, Map.of("q", 0, "w", 1, "e", 2));
testOf(new String[] { "q", "w", "e", "r" }, Map.of("q", 0, "w", 1, "e", 2, "r", 3));
testOf(new String[] { "q", "w", "e", "r", "t" }, Map.of("q", 0, "w", 1, "e", 2, "r", 3, "t", 4));
testOf(new String[] { "q", "w", "e", "r", "t", "y" }, Map.of("q", 0, "w", 1, "e", 2, "r", 3, "t", 4, "y", 5));
testOf(new String[] { "q", "w", "e", "r", "t", "y", "u" },
Map.of("q", 0, "w", 1, "e", 2, "r", 3, "t", 4, "y", 5, "u", 6));
testOf(new String[] { "q", "w", "e", "r", "t", "y", "u", "i" },
Map.of("q", 0, "w", 1, "e", 2, "r", 3, "t", 4, "y", 5, "u", 6, "i", 7));
testOf(new String[] { "q", "w", "e", "r", "t", "y", "u", "i", "o" },
Map.of("q", 0, "w", 1, "e", 2, "r", 3, "t", 4, "y", 5, "u", 6, "i", 7, "o", 8));
testOf(new String[] { "q", "w", "e", "r", "t", "y", "u", "i", "o", "p" },
Map.of("q", 0, "w", 1, "e", 2, "r", 3, "t", 4, "y", 5, "u", 6, "i", 7, "o", 8, "p", 9));
testOf(new String[] { "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "a" },
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)));
}
private void testOf(String[] expected, Map<String, Integer> actual) {
if (actual.size() != expected.length) {
fail("Expected size is " + expected.length + ", actual size is " + actual.size());
}
try {
actual.remove("*");
fail("remove should not work");
} catch (UnsupportedOperationException e) {
// ok;
}
try {
actual.put("*", -1);
fail("add should not work");
} catch (UnsupportedOperationException e) {
// ok;
}
try {
actual.clear();
fail("clear should not work");
} catch (UnsupportedOperationException e) {
// ok;
}
for (int i = 0; i < expected.length; i++) {
String key = expected[i];
assertTrue("containsKey returns true for existing elements", actual.containsKey(key));
assertTrue("containsValue returns true for existing elements", actual.containsValue(i));
assertTrue("contains returns true for existing elements", actual.entrySet().contains(Map.entry(key, i)));
}
assertFalse("containsKey return false for non-existing element", actual.containsKey("*"));
assertFalse("containsValue return false for non-existing element", actual.containsValue(-1));
for (String key : expected) {
assertFalse("contains return false for non-existing element",
actual.entrySet().contains(Map.entry(key, -1)));
}
assertEquals("isEmpty works properly", expected.length == 0, actual.isEmpty());
String[] expectedCopy = expected.clone();
for (Map.Entry<String, Integer> entry : actual.entrySet()) {
boolean found = false;
for (int i = 0; i < expectedCopy.length; ++i) {
if (entry.getKey().equals(expectedCopy[i])) {
assertEquals("Strange value of entry.getValue()", (Object) i, entry.getValue());
expectedCopy[i] = null;
found = true;
break;
}
}
assertTrue("iterator returned strange value", found);
}
for (Map.Entry<String, Integer> entry : actual.entrySet()) {
try {
entry.setValue(-1);
fail("Entries should be immutable");
} catch (UnsupportedOperationException e) {
// ok
}
}
for (String e : expectedCopy) {
assertNull("Iterator did not return all of expected elements", e);
}
}
}

View File

@ -0,0 +1,124 @@
/*
* Copyright 2020 konsoletyper.
*
* 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.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.Set;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.teavm.junit.TeaVMTestRunner;
import org.teavm.junit.WholeClassCompilation;
@RunWith(TeaVMTestRunner.class)
@WholeClassCompilation
public class SetTest {
@Test
public void of() {
testOf(new String[0], Set.of());
testOf(new String[] { "q" }, Set.of("q"));
testOf(new String[] { "q", "w" }, Set.of("q", "w"));
testOf(new String[] { "q", "w", "e" }, Set.of("q", "w", "e"));
testOf(new String[] { "q", "w", "e", "r" }, Set.of("q", "w", "e", "r"));
testOf(new String[] { "q", "w", "e", "r", "t" }, Set.of("q", "w", "e", "r", "t"));
testOf(new String[] { "q", "w", "e", "r", "t", "y" }, Set.of("q", "w", "e", "r", "t", "y"));
testOf(new String[] { "q", "w", "e", "r", "t", "y", "u" }, Set.of("q", "w", "e", "r", "t", "y", "u"));
testOf(new String[] { "q", "w", "e", "r", "t", "y", "u", "i" },
Set.of("q", "w", "e", "r", "t", "y", "u", "i"));
testOf(new String[] { "q", "w", "e", "r", "t", "y", "u", "i", "o" },
Set.of("q", "w", "e", "r", "t", "y", "u", "i", "o"));
testOf(new String[] { "q", "w", "e", "r", "t", "y", "u", "i", "o", "p" },
Set.of("q", "w", "e", "r", "t", "y", "u", "i", "o", "p"));
testOf(new String[] { "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "a" },
Set.of("q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "a"));
expectIAE(() -> Set.of("q", "q"));
expectIAE(() -> Set.of("q", "w", "q"));
expectIAE(() -> Set.of("q", "w", "e", "q"));
expectIAE(() -> Set.of("q", "w", "e", "r", "q"));
expectIAE(() -> Set.of("q", "w", "e", "r", "t", "q"));
expectIAE(() -> Set.of("q", "w", "e", "r", "t", "y", "q"));
expectIAE(() -> Set.of("q", "w", "e", "r", "t", "y", "u", "q"));
expectIAE(() -> Set.of("q", "w", "e", "r", "t", "y", "u", "i", "q"));
expectIAE(() -> Set.of("q", "w", "e", "r", "t", "y", "u", "i", "o", "q"));
expectIAE(() -> Set.of("q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "q"));
}
private void expectIAE(Runnable r) {
try {
r.run();
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
// ok
}
}
private void testOf(String[] expected, Set<String> actual) {
if (actual.size() != expected.length) {
fail("Expected size is " + expected.length + ", actual size is " + actual.size());
}
try {
actual.remove("*");
fail("remove should not work");
} catch (UnsupportedOperationException e) {
// ok;
}
try {
actual.add("2");
fail("add should not work");
} catch (UnsupportedOperationException e) {
// ok;
}
try {
actual.clear();
fail("clear should not work");
} catch (UnsupportedOperationException e) {
// ok;
}
for (String value : expected) {
assertTrue("contains returns true for existing elements", actual.contains(value));
}
assertFalse("contains return false for non-existing element", actual.contains("*"));
assertEquals("isEmpty works properly", expected.length == 0, actual.isEmpty());
String[] expectedCopy = expected.clone();
for (String elem : actual) {
boolean found = false;
for (int i = 0; i < expectedCopy.length; ++i) {
if (elem.equals(expectedCopy[i])) {
expectedCopy[i] = null;
found = true;
break;
}
}
assertTrue("iterator returned strange value", found);
}
for (String e : expectedCopy) {
assertNull("Iterator did not return all of expected elements", e);
}
}
}