classlib: implement last part of JEP-431 (#810)

TreeMap, TreeSet
This commit is contained in:
Ivan Hetman 2023-10-09 22:26:42 +03:00 committed by GitHub
parent 05454380d9
commit bcc2c0ff35
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 818 additions and 32 deletions

View File

@ -292,7 +292,7 @@ public abstract class TAbstractMap<K, V> extends TObject implements TMap<K, V> {
private class KeySet extends TAbstractSet<K> { private class KeySet extends TAbstractSet<K> {
@Override @Override
public TIterator<K> iterator() { public TIterator<K> iterator() {
final TIterator<TMap.Entry<K, V>> it = TAbstractMap.this.entrySet().iterator(); final TIterator<TMap.Entry<K, V>> it = entrySet().iterator();
return new TIterator<>() { return new TIterator<>() {
@Override public boolean hasNext() { @Override public boolean hasNext() {
return it.hasNext(); return it.hasNext();
@ -320,16 +320,16 @@ public abstract class TAbstractMap<K, V> extends TObject implements TMap<K, V> {
@Override @Override
public TIterator<V> iterator() { public TIterator<V> iterator() {
final TIterator<TMap.Entry<K, V>> iter = TAbstractMap.this.entrySet().iterator(); final TIterator<TMap.Entry<K, V>> it = entrySet().iterator();
return new TIterator<V>() { return new TIterator<>() {
@Override public boolean hasNext() { @Override public boolean hasNext() {
return iter.hasNext(); return it.hasNext();
} }
@Override public V next() { @Override public V next() {
return iter.next().getValue(); return it.next().getValue();
} }
@Override public void remove() { @Override public void remove() {
iter.remove(); it.remove();
} }
}; };
} }

View File

@ -15,7 +15,6 @@
*/ */
package org.teavm.classlib.java.util; package org.teavm.classlib.java.util;
import java.util.Spliterator;
import java.util.function.IntFunction; import java.util.function.IntFunction;
import java.util.function.Predicate; import java.util.function.Predicate;
import org.teavm.classlib.java.lang.TIterable; import org.teavm.classlib.java.lang.TIterable;
@ -59,7 +58,7 @@ public interface TCollection<E> extends TIterable<E> {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
default TStream<E> stream() { default TStream<E> stream() {
return new TStreamOverSpliterator<>((Spliterator<E>) spliterator()); return new TStreamOverSpliterator<>(spliterator());
} }
default boolean removeIf(Predicate<? super E> filter) { default boolean removeIf(Predicate<? super E> filter) {

View File

@ -57,4 +57,14 @@ public interface TNavigableMap<K, V> extends TSortedMap<K, V> {
TNavigableMap<K, V> headMap(K toKey, boolean inclusive); TNavigableMap<K, V> headMap(K toKey, boolean inclusive);
TNavigableMap<K, V> tailMap(K fromKey, boolean inclusive); TNavigableMap<K, V> tailMap(K fromKey, boolean inclusive);
@Override
default TSequencedSet<K> sequencedKeySet() {
return navigableKeySet();
}
@Override
default TNavigableMap<K, V> reversed() {
return descendingMap();
}
} }

View File

@ -42,4 +42,27 @@ public interface TNavigableSet<E> extends TSortedSet<E> {
TNavigableSet<E> headSet(E toElement, boolean inclusive); TNavigableSet<E> headSet(E toElement, boolean inclusive);
TNavigableSet<E> tailSet(E fromElement, boolean inclusive); TNavigableSet<E> tailSet(E fromElement, boolean inclusive);
@Override
default E removeFirst() {
if (isEmpty()) {
throw new TNoSuchElementException();
} else {
return pollFirst();
}
}
@Override
default E removeLast() {
if (isEmpty()) {
throw new TNoSuchElementException();
} else {
return pollLast();
}
}
@Override
default TNavigableSet<E> reversed() {
return descendingSet();
}
} }

View File

@ -0,0 +1,320 @@
/*
* Copyright 2023 ihromant.
*
* 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;
class TReversedSortedSet<E> extends TAbstractSet<E> implements TSortedSet<E> {
private final TSortedSet<E> base;
TReversedSortedSet(TSortedSet<E> base) {
this.base = base;
}
@Override
public TIterator<E> iterator() {
return new DescendingSortedSetIterator<>(base);
}
@Override
public boolean add(E e) {
base.add(e);
return true;
}
@Override
public boolean addAll(TCollection<? extends E> c) {
return base.addAll(c);
}
@Override
public void clear() {
base.clear();
}
@Override
public boolean contains(Object o) {
return base.contains(o);
}
@Override
public boolean containsAll(TCollection<?> c) {
return base.containsAll(c);
}
@Override
public boolean isEmpty() {
return base.isEmpty();
}
@Override
public boolean remove(Object o) {
return base.remove(o);
}
@Override
public E removeFirst() {
return base.removeLast();
}
@Override
public E removeLast() {
return base.removeFirst();
}
@Override
public boolean removeAll(TCollection<?> c) {
return base.removeAll(c);
}
@Override
public boolean retainAll(TCollection<?> c) {
return base.retainAll(c);
}
@Override
public int size() {
return base.size();
}
@Override
public TComparator<? super E> comparator() {
TComparator<? super E> comp = base.comparator();
return comp != null ? comp.reversed() : TCollections.reverseOrder();
}
@Override
public E first() {
return base.last();
}
@Override
public E last() {
return base.first();
}
@Override
public E getFirst() {
return base.last();
}
@Override
public E getLast() {
return base.first();
}
@Override
public TSortedSet<E> headSet(E to) {
return new DescSubset<>(base, null, to);
}
@Override
public TSortedSet<E> subSet(E from, E to) {
return new DescSubset<>(base, from, to);
}
@Override
public TSortedSet<E> tailSet(E from) {
return new DescSubset<>(base, from, null);
}
static class DescSubset<E> extends TAbstractSet<E> implements TSortedSet<E> {
final TSortedSet<E> base;
final E head;
final E tail;
private final TComparator<E> reversed;
@SuppressWarnings("unchecked")
DescSubset(TSortedSet<E> base, E head, E tail) {
this.base = base;
this.head = head;
this.tail = tail;
this.reversed = (TComparator<E>) TCollections.reverseOrder(base.comparator());
}
private boolean aboveHeadInc(E e) {
return head == null || reversed.compare(e, head) >= 0;
}
private boolean belowTailExc(E e) {
return tail == null || reversed.compare(e, tail) < 0;
}
@Override
public TIterator<E> iterator() {
return new TIterator<>() {
private final TIterator<E> it = new DescendingSortedSetIterator<>(base);
private E current;
private boolean finished;
@Override
public boolean hasNext() {
if (finished) {
return false;
}
if (current != null) {
return true;
}
while (it.hasNext()) {
E e = it.next();
if (!aboveHeadInc(e)) {
continue;
}
if (!belowTailExc(e)) {
finished = true;
return false;
}
current = e;
return true;
}
return false;
}
@Override
public E next() {
if (hasNext()) {
E e = current;
current = null;
return e;
} else {
throw new TNoSuchElementException();
}
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@Override
public boolean add(E e) {
if (aboveHeadInc(e) && belowTailExc(e)) {
return base.add(e);
} else {
throw new IllegalArgumentException();
}
}
@SuppressWarnings("unchecked")
@Override
public boolean remove(Object o) {
E e = (E) o;
if (aboveHeadInc(e) && belowTailExc(e)) {
return base.remove(o);
} else {
return false;
}
}
@Override
public int size() {
int size = 0;
for (var it = iterator(); it.hasNext(); it.next()) {
size++;
}
return size;
}
@Override
public TComparator<? super E> comparator() {
TComparator<? super E> comp = base.comparator();
return comp != null ? comp.reversed() : TCollections.reverseOrder();
}
@Override
public E first() {
return iterator().next();
}
@Override
public E last() {
TIterator<E> it = iterator();
if (!it.hasNext()) {
throw new TNoSuchElementException();
}
E last = it.next();
while (it.hasNext()) {
last = it.next();
}
return last;
}
@Override
public TSortedSet<E> subSet(E from, E to) {
if (aboveHeadInc(from) && belowTailExc(from)
&& aboveHeadInc(to) && belowTailExc(to)
&& reversed.compare(from, to) <= 0) {
return new DescSubset<>(base, from, to);
} else {
throw new IllegalArgumentException();
}
}
@Override
public TSortedSet<E> headSet(E to) {
if (aboveHeadInc(to) && belowTailExc(to)) {
return new DescSubset<>(base, head, to);
} else {
throw new IllegalArgumentException();
}
}
@Override
public TSortedSet<E> tailSet(E from) {
if (aboveHeadInc(from) && belowTailExc(from)) {
return new DescSubset<>(base, null, tail);
} else {
throw new IllegalArgumentException();
}
}
}
private static class DescendingSortedSetIterator<E> implements TIterator<E> {
private final TSortedSet<E> base;
private TSortedSet<E> remaining;
private E current;
private DescendingSortedSetIterator(TSortedSet<E> base) {
this.base = base;
this.remaining = base;
}
@Override
public boolean hasNext() {
return !remaining.isEmpty();
}
@Override
public E next() {
if (remaining.isEmpty()) {
throw new TNoSuchElementException();
}
current = remaining.last();
remaining = base.headSet(current);
return current;
}
@Override
public void remove() {
if (current == null) {
throw new IllegalStateException();
} else {
base.remove(current);
current = null;
}
}
}
}

View File

@ -21,7 +21,7 @@ package org.teavm.classlib.java.util;
* @param <K> * @param <K>
* @param <V> * @param <V>
*/ */
public interface TSortedMap<K, V> extends TMap<K, V> { public interface TSortedMap<K, V> extends TSequencedMap<K, V> {
TComparator<? super K> comparator(); TComparator<? super K> comparator();
TSortedMap<K, V> subMap(K fromKey, K toKey); TSortedMap<K, V> subMap(K fromKey, K toKey);

View File

@ -20,7 +20,7 @@ package org.teavm.classlib.java.util;
* @author Alexey Andreev * @author Alexey Andreev
* @param <E> * @param <E>
*/ */
public interface TSortedSet<E> extends TSet<E> { public interface TSortedSet<E> extends TSet<E>, TSequencedSet<E> {
TComparator<? super E> comparator(); TComparator<? super E> comparator();
TSortedSet<E> subSet(E fromElement, E toElement); TSortedSet<E> subSet(E fromElement, E toElement);
@ -32,4 +32,33 @@ public interface TSortedSet<E> extends TSet<E> {
E first(); E first();
E last(); E last();
@Override
default E getFirst() {
return first();
}
@Override
default E getLast() {
return last();
}
@Override
default E removeFirst() {
E e = this.first();
this.remove(e);
return e;
}
@Override
default E removeLast() {
E e = this.last();
this.remove(e);
return e;
}
@Override
default TSortedSet<E> reversed() {
return new TReversedSortedSet<>(this);
}
} }

View File

@ -364,10 +364,7 @@ public class TTreeMap<K, V> extends TAbstractMap<K, V> implements TCloneable, TS
@Override @Override
public TSet<Entry<K, V>> entrySet() { public TSet<Entry<K, V>> entrySet() {
if (cachedEntrySet == null) { return sequencedEntrySet();
cachedEntrySet = new EntrySet<>(this, null, true, false, null, true, false, false);
}
return cachedEntrySet;
} }
@Override @Override
@ -469,7 +466,7 @@ public class TTreeMap<K, V> extends TAbstractMap<K, V> implements TCloneable, TS
public Entry<K, V> pollFirstEntry() { public Entry<K, V> pollFirstEntry() {
TreeNode<K, V> node = firstNode(false); TreeNode<K, V> node = firstNode(false);
if (node != null) { if (node != null) {
root = deleteNode(root, node.getKey()); remove(node.getKey());
} }
return node; return node;
} }
@ -478,11 +475,32 @@ public class TTreeMap<K, V> extends TAbstractMap<K, V> implements TCloneable, TS
public Entry<K, V> pollLastEntry() { public Entry<K, V> pollLastEntry() {
TreeNode<K, V> node = firstNode(true); TreeNode<K, V> node = firstNode(true);
if (node != null) { if (node != null) {
root = deleteNode(root, node.getKey()); remove(node.getKey());
} }
return node; return node;
} }
@Override
public TCollection<V> values() {
return sequencedValues();
}
@Override
public TSequencedCollection<V> sequencedValues() {
if (cachedValues == null) {
cachedValues = new NavigableMapValues<>(this);
}
return (TSequencedCollection<V>) cachedValues;
}
@Override
public TSequencedSet<Entry<K, V>> sequencedEntrySet() {
if (cachedEntrySet == null) {
cachedEntrySet = new EntrySet<>(this, null, true, false, null, true, false, false);
}
return cachedEntrySet;
}
@Override @Override
public TNavigableMap<K, V> descendingMap() { public TNavigableMap<K, V> descendingMap() {
return new MapView<>(this, null, false, false, null, false, false, true); return new MapView<>(this, null, false, false, null, false, false, true);
@ -542,7 +560,7 @@ public class TTreeMap<K, V> extends TAbstractMap<K, V> implements TCloneable, TS
return copy; return copy;
} }
static class EntrySet<K, V> extends TAbstractSet<Entry<K, V>> { static class EntrySet<K, V> extends TAbstractSet<Entry<K, V>> implements TSequencedSet<Entry<K, V>> {
private int modCount = -1; private int modCount = -1;
private TTreeMap<K, V> owner; private TTreeMap<K, V> owner;
private K from; private K from;
@ -619,7 +637,7 @@ public class TTreeMap<K, V> extends TAbstractMap<K, V> implements TCloneable, TS
} else { } else {
toPath = owner.pathToFirst(true); toPath = owner.pathToFirst(true);
} }
return new EntryIterator<>(owner, toPath, from, fromIncluded, fromChecked, true); return new EntryIterator<>(owner, toPath, from, fromChecked, fromIncluded, true);
} }
@Override @Override
@ -651,6 +669,11 @@ public class TTreeMap<K, V> extends TAbstractMap<K, V> implements TCloneable, TS
public boolean isEmpty() { public boolean isEmpty() {
return size() == 0; return size() == 0;
} }
@Override
public TSequencedSet<Entry<K, V>> reversed() {
return new EntrySet<>(owner, from, fromIncluded, fromChecked, to, toIncluded, toChecked, !reverse);
}
} }
static class EntryIterator<K, V> implements TIterator<Entry<K, V>> { static class EntryIterator<K, V> implements TIterator<Entry<K, V>> {
@ -772,11 +795,7 @@ public class TTreeMap<K, V> extends TAbstractMap<K, V> implements TCloneable, TS
@Override @Override
public TSet<Entry<K, V>> entrySet() { public TSet<Entry<K, V>> entrySet() {
if (entrySetCache == null) { return sequencedEntrySet();
entrySetCache = new EntrySet<>(owner, from, fromIncluded,
fromChecked, to, toIncluded, toChecked, reverse);
}
return entrySetCache;
} }
@Override @Override
@ -1021,7 +1040,7 @@ public class TTreeMap<K, V> extends TAbstractMap<K, V> implements TCloneable, TS
public Entry<K, V> pollFirstEntry() { public Entry<K, V> pollFirstEntry() {
TreeNode<K, V> node = !reverse ? firstNode() : lastNode(); TreeNode<K, V> node = !reverse ? firstNode() : lastNode();
if (node != null) { if (node != null) {
owner.deleteNode(owner.root, node.getKey()); owner.remove(node.getKey());
} }
return node; return node;
} }
@ -1030,11 +1049,33 @@ public class TTreeMap<K, V> extends TAbstractMap<K, V> implements TCloneable, TS
public Entry<K, V> pollLastEntry() { public Entry<K, V> pollLastEntry() {
TreeNode<K, V> node = !reverse ? lastNode() : firstNode(); TreeNode<K, V> node = !reverse ? lastNode() : firstNode();
if (node != null) { if (node != null) {
owner.deleteNode(owner.root, node.getKey()); owner.remove(node.getKey());
} }
return node; return node;
} }
@Override
public TCollection<V> values() {
return sequencedValues();
}
@Override
public TSequencedCollection<V> sequencedValues() {
if (cachedValues == null) {
cachedValues = new NavigableMapValues<>(this);
}
return (TSequencedCollection<V>) cachedValues;
}
@Override
public TSequencedSet<Entry<K, V>> sequencedEntrySet() {
if (entrySetCache == null) {
entrySetCache = new EntrySet<>(owner, from, fromIncluded,
fromChecked, to, toIncluded, toChecked, reverse);
}
return entrySetCache;
}
@Override @Override
public TNavigableMap<K, V> descendingMap() { public TNavigableMap<K, V> descendingMap() {
return new MapView<>(owner, from, fromIncluded, fromChecked, to, toIncluded, toChecked, !reverse); return new MapView<>(owner, from, fromIncluded, fromChecked, to, toIncluded, toChecked, !reverse);
@ -1086,13 +1127,13 @@ public class TTreeMap<K, V> extends TAbstractMap<K, V> implements TCloneable, TS
if (!reverse) { if (!reverse) {
return new MapView<>(owner, fromKey, inclusive, true, to, toIncluded, toChecked, false); return new MapView<>(owner, fromKey, inclusive, true, to, toIncluded, toChecked, false);
} else { } else {
return new MapView<>(owner, from, fromIncluded, toChecked, fromKey, inclusive, true, true); return new MapView<>(owner, from, fromIncluded, fromChecked, fromKey, inclusive, true, true);
} }
} }
} }
static class NavigableKeySet<K, V> extends TAbstractSet<K> implements TNavigableSet<K> { static class NavigableKeySet<K, V> extends TAbstractSet<K> implements TNavigableSet<K> {
private TNavigableMap<K, V> map; private final TNavigableMap<K, V> map;
NavigableKeySet(TNavigableMap<K, V> map) { NavigableKeySet(TNavigableMap<K, V> map) {
this.map = map; this.map = map;
@ -1115,7 +1156,7 @@ public class TTreeMap<K, V> extends TAbstractMap<K, V> implements TCloneable, TS
@Override @Override
public TSortedSet<K> tailSet(K fromElement) { public TSortedSet<K> tailSet(K fromElement) {
return map.headMap(fromElement, true).navigableKeySet(); return map.tailMap(fromElement, true).navigableKeySet();
} }
@Override @Override
@ -1192,7 +1233,41 @@ public class TTreeMap<K, V> extends TAbstractMap<K, V> implements TCloneable, TS
@Override @Override
public TNavigableSet<K> tailSet(K fromElement, boolean inclusive) { public TNavigableSet<K> tailSet(K fromElement, boolean inclusive) {
return map.headMap(fromElement, inclusive).navigableKeySet(); return map.tailMap(fromElement, inclusive).navigableKeySet();
}
}
static class NavigableMapValues<K, V> extends TAbstractCollection<V> implements TSequencedCollection<V> {
private final TNavigableMap<K, V> map;
NavigableMapValues(TNavigableMap<K, V> map) {
this.map = map;
}
@Override
public int size() {
return map.size();
}
@Override
public TIterator<V> iterator() {
final TIterator<TMap.Entry<K, V>> it = map.entrySet().iterator();
return new TIterator<>() {
@Override public boolean hasNext() {
return it.hasNext();
}
@Override public V next() {
return it.next().getValue();
}
@Override public void remove() {
it.remove();
}
};
}
@Override
public TSequencedCollection<V> reversed() {
return new NavigableMapValues<>(map.reversed());
} }
} }
} }

View File

@ -17,6 +17,7 @@ package org.teavm.classlib.java.util.stream;
import java.util.Spliterator; import java.util.Spliterator;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.teavm.classlib.java.util.TSpliterator;
import org.teavm.classlib.java.util.stream.impl.TStreamOverSpliterator; import org.teavm.classlib.java.util.stream.impl.TStreamOverSpliterator;
import org.teavm.classlib.java.util.stream.impl.TStreamOverSpliteratorSupplier; import org.teavm.classlib.java.util.stream.impl.TStreamOverSpliteratorSupplier;
@ -24,7 +25,7 @@ public final class TStreamSupport {
private TStreamSupport() { private TStreamSupport() {
} }
public static <T> TStream<T> stream(Spliterator<T> spliterator, boolean parallel) { public static <T> TStream<T> stream(TSpliterator<T> spliterator, boolean parallel) {
return new TStreamOverSpliterator<>(spliterator); return new TStreamOverSpliterator<>(spliterator);
} }

View File

@ -18,11 +18,12 @@ package org.teavm.classlib.java.util.stream.impl;
import java.util.Spliterator; import java.util.Spliterator;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Predicate; import java.util.function.Predicate;
import org.teavm.classlib.java.util.TSpliterator;
public class TStreamOverSpliterator<T> extends TSimpleStreamImpl<T> { public class TStreamOverSpliterator<T> extends TSimpleStreamImpl<T> {
private Spliterator<T> spliterator; private TSpliterator<T> spliterator;
public TStreamOverSpliterator(Spliterator<T> spliterator) { public TStreamOverSpliterator(TSpliterator<T> spliterator) {
this.spliterator = spliterator; this.spliterator = spliterator;
} }

View File

@ -47,6 +47,7 @@
*/ */
package org.teavm.classlib.java.util; package org.teavm.classlib.java.util;
import static org.junit.Assert.assertArrayEquals;
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.assertNotSame; import static org.junit.Assert.assertNotSame;
@ -55,15 +56,21 @@ 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.io.Serializable; import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.NavigableMap; import java.util.NavigableMap;
import java.util.NoSuchElementException;
import java.util.SequencedMap;
import java.util.Set; import java.util.Set;
import java.util.SortedMap; import java.util.SortedMap;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.function.Function;
import java.util.stream.Collectors;
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;
@ -728,4 +735,124 @@ public class TreeMapTest {
} }
} }
} }
private static final List<Integer> BASE_LIST = Arrays.asList(1, 6, 2, 5, 3, 4);
private SequencedMap<Integer, String> generateMap() {
return BASE_LIST.stream().collect(Collectors.toMap(Function.identity(), i -> Integer.toString(i),
(a, b) -> a, TreeMap::new));
}
@Test
public void testSequencedMap() {
SequencedMap<Integer, String> map = generateMap();
assertEquals(Map.entry(1, "1"), map.pollFirstEntry());
assertArrayEquals(new Integer[] { 2, 3, 4, 5, 6 }, map.keySet().toArray(new Integer[0]));
assertEquals(Map.entry(6, "6"), map.pollLastEntry());
assertArrayEquals(new Integer[] { 2, 3, 4, 5 }, map.keySet().toArray(new Integer[0]));
assertEquals(Map.entry(5, "5"), map.pollLastEntry());
assertArrayEquals(new Integer[] { 2, 3, 4 }, map.keySet().toArray(new Integer[0]));
assertEquals(Map.entry(2, "2"), map.firstEntry());
assertEquals(Map.entry(4, "4"), map.lastEntry());
try {
map.putFirst(1, "1");
fail();
} catch (UnsupportedOperationException e) {
// ok
}
map.put(7, "7");
try {
map.putLast(3, "3");
fail();
} catch (UnsupportedOperationException e) {
// ok
}
assertArrayEquals(new Integer[] { 2, 3, 4, 7 }, map.keySet().toArray(new Integer[0]));
map = generateMap().reversed();
assertEquals(Map.entry(6, "6"), map.pollFirstEntry());
assertArrayEquals(new Integer[] { 5, 4, 3, 2, 1 }, map.keySet().toArray(new Integer[0]));
assertEquals(Map.entry(1, "1"), map.pollLastEntry());
assertArrayEquals(new Integer[] { 5, 4, 3, 2 }, map.keySet().toArray(new Integer[0]));
assertEquals(Map.entry(2, "2"), map.pollLastEntry());
assertArrayEquals(new Integer[] { 5, 4, 3 }, map.keySet().toArray(new Integer[0]));
assertEquals(Map.entry(5, "5"), map.firstEntry());
assertEquals(Map.entry(3, "3"), map.lastEntry());
try {
map.putFirst(1, "1");
fail();
} catch (UnsupportedOperationException e) {
// ok
}
map.put(7, "7");
try {
map.putLast(6, "6");
fail();
} catch (UnsupportedOperationException e) {
// ok
}
assertArrayEquals(new Integer[] { 7, 5, 4, 3 }, map.keySet().toArray(new Integer[0]));
}
@Test
public void testSequencedIterators() {
SequencedMap<Integer, String> map = generateMap();
Iterator<Integer> it = map.keySet().iterator();
assertTrue(it.hasNext());
assertEquals(1, it.next().intValue());
assertTrue(it.hasNext());
assertEquals(2, it.next().intValue());
it.remove();
assertArrayEquals(new Integer[] { 1, 3, 4, 5, 6 }, map.keySet().toArray(new Integer[0]));
map = map.reversed();
it = map.keySet().iterator();
assertTrue(it.hasNext());
assertEquals(6, it.next().intValue());
assertTrue(it.hasNext());
assertEquals(5, it.next().intValue());
it.remove();
assertArrayEquals(new Integer[] { 6, 4, 3, 1 }, map.keySet().toArray(new Integer[0]));
map = generateMap();
Iterator<String> sit = map.sequencedValues().iterator();
assertTrue(sit.hasNext());
assertEquals("1", sit.next());
assertTrue(sit.hasNext());
assertEquals("2", sit.next());
sit.remove();
assertArrayEquals(new String[] { "1", "3", "4", "5", "6" }, map.values().toArray(new String[0]));
map = map.reversed();
sit = map.sequencedValues().iterator();
assertTrue(sit.hasNext());
assertEquals("6", sit.next());
assertTrue(sit.hasNext());
assertEquals("5", sit.next());
sit.remove();
assertArrayEquals(new String[] { "6", "4", "3", "1" }, map.values().toArray(new String[0]));
}
@Test
public void testEmpty() {
var empty = new TreeMap<>();
assertNull(empty.pollFirstEntry());
assertNull(empty.pollLastEntry());
assertNull(empty.firstEntry());
assertNull(empty.lastEntry());
try {
empty.entrySet().iterator().next();
fail();
} catch (NoSuchElementException e) {
// ok
}
try {
empty.keySet().iterator().next();
fail();
} catch (NoSuchElementException e) {
// ok
}
try {
empty.values().iterator().next();
fail();
} catch (NoSuchElementException e) {
// ok
}
}
} }

View File

@ -0,0 +1,201 @@
/*
* Copyright 2023 ihromant.
*
* 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.assertArrayEquals;
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.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.SortedSet;
import java.util.TreeSet;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.teavm.junit.TeaVMTestRunner;
@RunWith(TeaVMTestRunner.class)
public class TreeSetTest {
@Test
public void testSortedSetReadOnly() {
SortedSet<String> set = new TreeSet<>(List.of("1", "2", "3", "4", "5", "6"));
SortedSet<String> reversed = set.reversed();
Iterator<String> it = reversed.iterator();
assertEquals("6", it.next());
assertEquals("5", it.next());
assertEquals("6", reversed.getFirst());
assertEquals("1", reversed.getLast());
SortedSet<String> subset = reversed.subSet("3", "1");
it = subset.iterator();
assertTrue(it.hasNext());
assertEquals("3", it.next());
assertTrue(it.hasNext());
assertEquals("2", it.next());
try {
assertFalse(it.hasNext());
it.next();
fail();
} catch (NoSuchElementException e) {
// ok
}
subset = reversed.headSet("4");
it = subset.iterator();
assertTrue(it.hasNext());
assertEquals("6", it.next());
assertTrue(it.hasNext());
assertEquals("5", it.next());
try {
assertFalse(it.hasNext());
it.next();
fail();
} catch (NoSuchElementException e) {
// ok
}
subset = reversed.tailSet("2");
it = subset.iterator();
assertTrue(it.hasNext());
assertEquals("2", it.next());
assertTrue(it.hasNext());
assertEquals("1", it.next());
try {
assertFalse(it.hasNext());
it.next();
fail();
} catch (NoSuchElementException e) {
// ok
}
}
@Test
public void testSequencedCollectionIterator() {
SortedSet<String> set = new TreeSet<>(List.of("a", "b", "c", "d")).reversed();
Iterator<String> it = set.iterator();
assertEquals("d", it.next());
assertEquals("c", it.next());
it.remove();
assertArrayEquals(new String[] { "d", "b", "a" }, set.toArray());
set = new TreeSet<>(List.of("a", "b", "c", "d")).reversed();
it = set.iterator();
assertEquals("d", it.next());
assertEquals("c", it.next());
it.remove();
assertEquals("b", it.next());
it.remove();
assertEquals("a", it.next());
try {
it.next();
fail();
} catch (NoSuchElementException e) {
// ok
}
assertArrayEquals(new String[] { "d", "a" }, set.toArray());
}
@Test
public void sequenceCollectionMethodsOnEmpty() {
var empty = new TreeSet<>();
try {
empty.getFirst();
fail();
} catch (NoSuchElementException e) {
// ok
}
try {
empty.getLast();
fail();
} catch (NoSuchElementException e) {
// ok
}
try {
empty.removeFirst();
fail();
} catch (NoSuchElementException e) {
// ok
}
try {
empty.removeLast();
fail();
} catch (NoSuchElementException e) {
// ok
}
try {
empty.addFirst("r");
fail();
} catch (UnsupportedOperationException e) {
// ok
}
try {
empty.addLast("r");
fail();
} catch (UnsupportedOperationException e) {
// ok
}
}
@Test
public void testMutationsIterator() {
SortedSet<String> set = new TreeSet<>(List.of("a", "b", "c", "d"));
set.add("e");
set.add("f");
Iterator<String> it = set.iterator();
assertEquals("a", it.next());
it.remove();
assertEquals("b", it.next());
it.remove();
assertEquals("c", it.next());
it.remove();
assertEquals("d", it.next());
it.remove();
assertEquals("e", it.next());
it.remove();
assertEquals("f", it.next());
it.remove();
assertTrue(set.isEmpty());
try {
it.next();
fail();
} catch (NoSuchElementException e) {
// ok
}
set = new TreeSet<>(List.of("a", "b", "c", "d"));
set.add("e");
set.add("f");
it = set.reversed().iterator();
assertEquals("f", it.next());
it.remove();
assertEquals("e", it.next());
it.remove();
assertEquals("d", it.next());
it.remove();
assertEquals("c", it.next());
it.remove();
assertEquals("b", it.next());
it.remove();
assertEquals("a", it.next());
it.remove();
assertTrue(set.isEmpty());
try {
it.next();
fail();
} catch (NoSuchElementException e) {
// ok
}
}
}