mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
classlib: implement third part of JEP-431 (#771)
SequencedSet -> LinkedHashSet, SequencedMap -> LinkedHashMap
This commit is contained in:
parent
2d91f539c7
commit
d47fea0b55
|
@ -53,19 +53,19 @@ public abstract class TAbstractMap<K, V> extends TObject implements TMap<K, V> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (!(obj instanceof TMap.Entry)) {
|
if (this == obj) {
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
TMap.Entry<?, ?> other = (TMap.Entry<?, ?>) obj;
|
if (obj instanceof TMap.Entry) {
|
||||||
if (getKey() == null ? other.getKey() != null : !getKey().equals(other.getKey())) {
|
TMap.Entry<?, ?> entry = (TMap.Entry<?, ?>) obj;
|
||||||
return false;
|
return TObjects.equals(key, entry.getKey()) && TObjects.equals(value, entry.getValue());
|
||||||
}
|
}
|
||||||
return getValue() == null ? other.getValue() == null : getValue().equals(other.getValue());
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return (getKey() == null ? 0 : getKey().hashCode()) ^ (getValue() == null ? 0 : getValue().hashCode());
|
return TObjects.hashCode(key) ^ TObjects.hashCode(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -104,19 +104,19 @@ public abstract class TAbstractMap<K, V> extends TObject implements TMap<K, V> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (!(obj instanceof TMap.Entry)) {
|
if (this == obj) {
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
TMap.Entry<?, ?> other = (TMap.Entry<?, ?>) obj;
|
if (obj instanceof TMap.Entry) {
|
||||||
if (getKey() == null ? other.getKey() != null : !getKey().equals(other.getKey())) {
|
TMap.Entry<?, ?> entry = (TMap.Entry<?, ?>) obj;
|
||||||
return false;
|
return TObjects.equals(key, entry.getKey()) && TObjects.equals(value, entry.getValue());
|
||||||
}
|
}
|
||||||
return getValue() == null ? other.getValue() == null : getValue().equals(other.getValue());
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return (getKey() == null ? 0 : getKey().hashCode()) ^ (getValue() == null ? 0 : getValue().hashCode());
|
return TObjects.hashCode(key) ^ TObjects.hashCode(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -254,7 +254,7 @@ public abstract class TAbstractMap<K, V> extends TObject implements TMap<K, V> {
|
||||||
int result = 0;
|
int result = 0;
|
||||||
for (TIterator<? extends TMap.Entry<K, V>> iter = entrySet().iterator(); iter.hasNext();) {
|
for (TIterator<? extends TMap.Entry<K, V>> iter = entrySet().iterator(); iter.hasNext();) {
|
||||||
TMap.Entry<K, V> entry = iter.next();
|
TMap.Entry<K, V> entry = iter.next();
|
||||||
result ^= entry.hashCode();
|
result += entry.hashCode();
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -292,16 +292,16 @@ 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>> iter = TAbstractMap.this.entrySet().iterator();
|
final TIterator<TMap.Entry<K, V>> it = TAbstractMap.this.entrySet().iterator();
|
||||||
return new TIterator<K>() {
|
return new TIterator<>() {
|
||||||
@Override public boolean hasNext() {
|
@Override public boolean hasNext() {
|
||||||
return iter.hasNext();
|
return it.hasNext();
|
||||||
}
|
}
|
||||||
@Override public K next() {
|
@Override public K next() {
|
||||||
return iter.next().getKey();
|
return it.next().getKey();
|
||||||
}
|
}
|
||||||
@Override public void remove() {
|
@Override public void remove() {
|
||||||
iter.remove();
|
it.remove();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ import org.teavm.classlib.java.util.TMap.Entry;
|
||||||
|
|
||||||
public class TCollections extends TObject {
|
public class TCollections extends TObject {
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
public static final TSet EMPTY_SET = new TTemplateCollections.AbstractImmutableSet<Object>() {
|
public static final TSet EMPTY_SET = new TTemplateCollections.AbstractImmutableSet<>() {
|
||||||
@Override public int size() {
|
@Override public int size() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ public class TCollections extends TObject {
|
||||||
};
|
};
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
public static final TMap EMPTY_MAP = new TTemplateCollections.AbstractImmutableMap<Object, Object>() {
|
public static final TMap EMPTY_MAP = new TTemplateCollections.AbstractImmutableMap<>() {
|
||||||
@Override public TSet<Entry<Object, Object>> entrySet() {
|
@Override public TSet<Entry<Object, Object>> entrySet() {
|
||||||
return emptySet();
|
return emptySet();
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ public class TCollections extends TObject {
|
||||||
};
|
};
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
public static final TList EMPTY_LIST = new TTemplateCollections.AbstractImmutableList<Object>() {
|
public static final TList EMPTY_LIST = new TTemplateCollections.AbstractImmutableList<>() {
|
||||||
@Override public Object get(int index) {
|
@Override public Object get(int index) {
|
||||||
throw new TIndexOutOfBoundsException();
|
throw new TIndexOutOfBoundsException();
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ public class TCollections extends TObject {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final TIterator<?> EMPTY_ITERATOR = new TIterator<Object>() {
|
private static final TIterator<?> EMPTY_ITERATOR = new TIterator<>() {
|
||||||
@Override public boolean hasNext() {
|
@Override public boolean hasNext() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -98,7 +98,7 @@ public class TCollections extends TObject {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final TListIterator<?> EMPTY_LIST_ITERATOR = new TListIterator<Object>() {
|
private static final TListIterator<?> EMPTY_LIST_ITERATOR = new TListIterator<>() {
|
||||||
@Override public boolean hasNext() {
|
@Override public boolean hasNext() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -144,7 +144,7 @@ public class TCollections extends TObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> TEnumeration<T> emptyEnumeration() {
|
public static <T> TEnumeration<T> emptyEnumeration() {
|
||||||
return new TEnumeration<T>() {
|
return new TEnumeration<>() {
|
||||||
@Override public boolean hasMoreElements() {
|
@Override public boolean hasMoreElements() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -174,7 +174,7 @@ public class TCollections extends TObject {
|
||||||
|
|
||||||
public static <K, V> TMap<K, V> singletonMap(final K key, final V value) {
|
public static <K, V> TMap<K, V> singletonMap(final K key, final V value) {
|
||||||
final TSet<Entry<K, V>> entries = singleton(new TAbstractMap.SimpleImmutableEntry<>(key, value));
|
final TSet<Entry<K, V>> entries = singleton(new TAbstractMap.SimpleImmutableEntry<>(key, value));
|
||||||
return new TAbstractMap<K, V>() {
|
return new TAbstractMap<>() {
|
||||||
@Override public TSet<Entry<K, V>> entrySet() {
|
@Override public TSet<Entry<K, V>> entrySet() {
|
||||||
return entries;
|
return entries;
|
||||||
}
|
}
|
||||||
|
@ -182,7 +182,7 @@ public class TCollections extends TObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> TList<T> unmodifiableList(final TList<? extends T> list) {
|
public static <T> TList<T> unmodifiableList(final TList<? extends T> list) {
|
||||||
return new TAbstractList<T>() {
|
return new TAbstractList<>() {
|
||||||
@Override public T get(int index) {
|
@Override public T get(int index) {
|
||||||
return list.get(index);
|
return list.get(index);
|
||||||
}
|
}
|
||||||
|
@ -193,7 +193,7 @@ public class TCollections extends TObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> TList<T> nCopies(final int n, final T o) {
|
public static <T> TList<T> nCopies(final int n, final T o) {
|
||||||
return new TAbstractList<T>() {
|
return new TAbstractList<>() {
|
||||||
@Override public T get(int index) {
|
@Override public T get(int index) {
|
||||||
if (index < 0 || index >= n) {
|
if (index < 0 || index >= n) {
|
||||||
throw new TIndexOutOfBoundsException();
|
throw new TIndexOutOfBoundsException();
|
||||||
|
@ -461,7 +461,7 @@ public class TCollections extends TObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> TCollection<T> unmodifiableCollection(final TCollection<? extends T> c) {
|
public static <T> TCollection<T> unmodifiableCollection(final TCollection<? extends T> c) {
|
||||||
return new TAbstractCollection<T>() {
|
return new TAbstractCollection<>() {
|
||||||
@Override public TIterator<T> iterator() {
|
@Override public TIterator<T> iterator() {
|
||||||
return unmodifiableIterator(c.iterator());
|
return unmodifiableIterator(c.iterator());
|
||||||
}
|
}
|
||||||
|
@ -472,7 +472,7 @@ public class TCollections extends TObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T> TIterator<T> unmodifiableIterator(final TIterator<? extends T> c) {
|
private static <T> TIterator<T> unmodifiableIterator(final TIterator<? extends T> c) {
|
||||||
return new TIterator<T>() {
|
return new TIterator<>() {
|
||||||
@Override public boolean hasNext() {
|
@Override public boolean hasNext() {
|
||||||
return c.hasNext();
|
return c.hasNext();
|
||||||
}
|
}
|
||||||
|
@ -486,7 +486,7 @@ public class TCollections extends TObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> TSet<T> unmodifiableSet(final TSet<? extends T> s) {
|
public static <T> TSet<T> unmodifiableSet(final TSet<? extends T> s) {
|
||||||
return new TAbstractSet<T>() {
|
return new TAbstractSet<>() {
|
||||||
@Override public TIterator<T> iterator() {
|
@Override public TIterator<T> iterator() {
|
||||||
return unmodifiableIterator(s.iterator());
|
return unmodifiableIterator(s.iterator());
|
||||||
}
|
}
|
||||||
|
@ -497,7 +497,7 @@ public class TCollections extends TObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <K, V> TMap<K, V> unmodifiableMap(final TMap<? extends K, ? extends V> m) {
|
public static <K, V> TMap<K, V> unmodifiableMap(final TMap<? extends K, ? extends V> m) {
|
||||||
return new TAbstractMap<K, V>() {
|
return new TAbstractMap<>() {
|
||||||
@Override public TSet<Entry<K, V>> entrySet() {
|
@Override public TSet<Entry<K, V>> entrySet() {
|
||||||
return unmodifiableMapEntrySet(m.entrySet());
|
return unmodifiableMapEntrySet(m.entrySet());
|
||||||
}
|
}
|
||||||
|
@ -506,7 +506,7 @@ public class TCollections extends TObject {
|
||||||
|
|
||||||
private static <K, V> TSet<Entry<K, V>> unmodifiableMapEntrySet(
|
private static <K, V> TSet<Entry<K, V>> unmodifiableMapEntrySet(
|
||||||
final TSet<? extends Entry<? extends K, ? extends V>> c) {
|
final TSet<? extends Entry<? extends K, ? extends V>> c) {
|
||||||
return new TAbstractSet<Entry<K, V>>() {
|
return new TAbstractSet<>() {
|
||||||
@Override public int size() {
|
@Override public int size() {
|
||||||
return c.size();
|
return c.size();
|
||||||
}
|
}
|
||||||
|
@ -518,7 +518,7 @@ public class TCollections extends TObject {
|
||||||
|
|
||||||
private static <K, V> TIterator<Entry<K, V>> unmodifiableMapEntryIterator(
|
private static <K, V> TIterator<Entry<K, V>> unmodifiableMapEntryIterator(
|
||||||
final TIterator<? extends Entry<? extends K, ? extends V>> c) {
|
final TIterator<? extends Entry<? extends K, ? extends V>> c) {
|
||||||
return new TIterator<Entry<K, V>>() {
|
return new TIterator<>() {
|
||||||
@Override public boolean hasNext() {
|
@Override public boolean hasNext() {
|
||||||
return c.hasNext();
|
return c.hasNext();
|
||||||
}
|
}
|
||||||
|
@ -552,15 +552,19 @@ public class TCollections extends TObject {
|
||||||
return (TComparator<T>) reverseOrder;
|
return (TComparator<T>) reverseOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
private static TComparator<Object> reverseOrder = (o1, o2) -> ((TComparable<Object>) o2).compareTo(o1);
|
private static TComparator<Object> reverseOrder = (o1, o2) -> ((TComparable<Object>) o2).compareTo(o1);
|
||||||
|
|
||||||
public static <T> TComparator<T> reverseOrder(final TComparator<T> cmp) {
|
public static <T> TComparator<T> reverseOrder(final TComparator<T> cmp) {
|
||||||
|
if (cmp == null) {
|
||||||
|
return reverseOrder();
|
||||||
|
}
|
||||||
return (o1, o2) -> -cmp.compare(o1, o2);
|
return (o1, o2) -> -cmp.compare(o1, o2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> TEnumeration<T> enumeration(TCollection<T> c) {
|
public static <T> TEnumeration<T> enumeration(TCollection<T> c) {
|
||||||
final TIterator<T> iter = c.iterator();
|
final TIterator<T> iter = c.iterator();
|
||||||
return new TEnumeration<T>() {
|
return new TEnumeration<>() {
|
||||||
@Override public boolean hasMoreElements() {
|
@Override public boolean hasMoreElements() {
|
||||||
return iter.hasNext();
|
return iter.hasNext();
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,27 +13,9 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
/*
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file distributed with
|
|
||||||
* this work for additional information regarding copyright ownership.
|
|
||||||
* The ASF licenses this file to You 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;
|
package org.teavm.classlib.java.util;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
@ -61,7 +43,7 @@ public class THashMap<K, V> extends TAbstractMap<K, V> implements TCloneable, TS
|
||||||
|
|
||||||
HashEntry(K theKey, V theValue) {
|
HashEntry(K theKey, V theValue) {
|
||||||
super(theKey, theValue);
|
super(theKey, theValue);
|
||||||
origKeyHash = Objects.hashCode(theKey);
|
origKeyHash = TObjects.hashCode(theKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -210,9 +192,9 @@ public class THashMap<K, V> extends TAbstractMap<K, V> implements TCloneable, TS
|
||||||
public boolean remove(Object object) {
|
public boolean remove(Object object) {
|
||||||
if (object instanceof TMap.Entry) {
|
if (object instanceof TMap.Entry) {
|
||||||
TMap.Entry<?, ?> oEntry = (TMap.Entry<?, ?>) object;
|
TMap.Entry<?, ?> oEntry = (TMap.Entry<?, ?>) object;
|
||||||
TMap.Entry<K, V> entry = associatedMap.getEntry(oEntry.getKey());
|
TMap.Entry<K, V> entry = associatedMap.entryByKey(oEntry.getKey());
|
||||||
if (valuesEq(entry, oEntry)) {
|
if (entry != null && TObjects.equals(entry.getValue(), oEntry.getValue())) {
|
||||||
associatedMap.removeEntry(entry);
|
associatedMap.removeByKey(entry.getKey());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -223,19 +205,12 @@ public class THashMap<K, V> extends TAbstractMap<K, V> implements TCloneable, TS
|
||||||
public boolean contains(Object object) {
|
public boolean contains(Object object) {
|
||||||
if (object instanceof TMap.Entry) {
|
if (object instanceof TMap.Entry) {
|
||||||
TMap.Entry<?, ?> oEntry = (TMap.Entry<?, ?>) object;
|
TMap.Entry<?, ?> oEntry = (TMap.Entry<?, ?>) object;
|
||||||
TMap.Entry<K, V> entry = associatedMap.getEntry(oEntry.getKey());
|
TMap.Entry<K, V> entry = associatedMap.entryByKey(oEntry.getKey());
|
||||||
return valuesEq(entry, oEntry);
|
return entry != null && TObjects.equals(entry.getValue(), oEntry.getValue());
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean valuesEq(TMap.Entry<?, ?> entry, TMap.Entry<?, ?> oEntry) {
|
|
||||||
return entry != null
|
|
||||||
&& (entry.getValue() == null
|
|
||||||
? oEntry.getValue() == null
|
|
||||||
: areEqualValues(entry.getValue(), oEntry.getValue()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TIterator<TMap.Entry<K, V>> iterator() {
|
public TIterator<TMap.Entry<K, V>> iterator() {
|
||||||
return new EntryIterator<>(associatedMap);
|
return new EntryIterator<>(associatedMap);
|
||||||
|
@ -347,7 +322,7 @@ public class THashMap<K, V> extends TAbstractMap<K, V> implements TCloneable, TS
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean containsKey(Object key) {
|
public boolean containsKey(Object key) {
|
||||||
HashEntry<K, V> m = getEntry(key);
|
HashEntry<K, V> m = entryByKey(key);
|
||||||
return m != null;
|
return m != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -357,7 +332,7 @@ public class THashMap<K, V> extends TAbstractMap<K, V> implements TCloneable, TS
|
||||||
for (int i = 0; i < elementData.length; i++) {
|
for (int i = 0; i < elementData.length; i++) {
|
||||||
HashEntry<K, V> entry = elementData[i];
|
HashEntry<K, V> entry = elementData[i];
|
||||||
while (entry != null) {
|
while (entry != null) {
|
||||||
if (areEqualValues(value, entry.value)) {
|
if (value.equals(entry.value)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
entry = entry.next;
|
entry = entry.next;
|
||||||
|
@ -384,14 +359,14 @@ public class THashMap<K, V> extends TAbstractMap<K, V> implements TCloneable, TS
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public V get(Object key) {
|
public V get(Object key) {
|
||||||
HashEntry<K, V> m = getEntry(key);
|
HashEntry<K, V> m = entryByKey(key);
|
||||||
if (m != null) {
|
if (m != null) {
|
||||||
return m.value;
|
return m.value;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final HashEntry<K, V> getEntry(Object key) {
|
final HashEntry<K, V> entryByKey(Object key) {
|
||||||
HashEntry<K, V> m;
|
HashEntry<K, V> m;
|
||||||
if (key == null) {
|
if (key == null) {
|
||||||
m = findNullKeyEntry();
|
m = findNullKeyEntry();
|
||||||
|
@ -438,7 +413,7 @@ public class THashMap<K, V> extends TAbstractMap<K, V> implements TCloneable, TS
|
||||||
THashMap.this.clear();
|
THashMap.this.clear();
|
||||||
}
|
}
|
||||||
@Override public boolean remove(Object key) {
|
@Override public boolean remove(Object key) {
|
||||||
HashEntry<K, V> entry = THashMap.this.removeEntry(key);
|
HashEntry<K, V> entry = THashMap.this.removeByKey(key);
|
||||||
return entry != null;
|
return entry != null;
|
||||||
}
|
}
|
||||||
@Override public TIterator<K> iterator() {
|
@Override public TIterator<K> iterator() {
|
||||||
|
@ -469,7 +444,7 @@ public class THashMap<K, V> extends TAbstractMap<K, V> implements TCloneable, TS
|
||||||
return putImpl(key, value);
|
return putImpl(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
V putImpl(K key, V value) {
|
private V putImpl(K key, V value) {
|
||||||
HashEntry<K, V> entry;
|
HashEntry<K, V> entry;
|
||||||
if (key == null) {
|
if (key == null) {
|
||||||
entry = findNullKeyEntry();
|
entry = findNullKeyEntry();
|
||||||
|
@ -498,7 +473,7 @@ public class THashMap<K, V> extends TAbstractMap<K, V> implements TCloneable, TS
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
HashEntry<K, V> createHashedEntry(K key, int index, int hash) {
|
private HashEntry<K, V> createHashedEntry(K key, int index, int hash) {
|
||||||
HashEntry<K, V> entry = new HashEntry<>(key, hash);
|
HashEntry<K, V> entry = new HashEntry<>(key, hash);
|
||||||
entry.next = elementData[index];
|
entry.next = elementData[index];
|
||||||
elementData[index] = entry;
|
elementData[index] = entry;
|
||||||
|
@ -512,14 +487,13 @@ public class THashMap<K, V> extends TAbstractMap<K, V> implements TCloneable, TS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void putAllImpl(TMap<? extends K, ? extends V> map) {
|
void putAllImpl(TMap<? extends K, ? extends V> map) {
|
||||||
int capacity = elementCount + map.size();
|
int capacity = elementCount + map.size();
|
||||||
if (capacity > threshold) {
|
if (capacity > threshold) {
|
||||||
rehash(capacity);
|
rehash(capacity);
|
||||||
}
|
}
|
||||||
for (TIterator<? extends TMap.Entry<? extends K, ? extends V>> iter = map.entrySet().iterator();
|
for (var it = map.entrySet().iterator(); it.hasNext();) {
|
||||||
iter.hasNext();) {
|
TMap.Entry<? extends K, ? extends V> entry = it.next();
|
||||||
TMap.Entry<? extends K, ? extends V> entry = iter.next();
|
|
||||||
putImpl(entry.getKey(), entry.getValue());
|
putImpl(entry.getKey(), entry.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -549,30 +523,14 @@ public class THashMap<K, V> extends TAbstractMap<K, V> implements TCloneable, TS
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public V remove(Object key) {
|
public V remove(Object key) {
|
||||||
HashEntry<K, V> entry = removeEntry(key);
|
HashEntry<K, V> entry = removeByKey(key);
|
||||||
if (entry != null) {
|
if (entry != null) {
|
||||||
return entry.value;
|
return entry.value;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final void removeEntry(HashEntry<K, V> entry) {
|
final HashEntry<K, V> removeByKey(Object key) {
|
||||||
int index = entry.origKeyHash & (elementData.length - 1);
|
|
||||||
HashEntry<K, V> m = elementData[index];
|
|
||||||
if (m == entry) {
|
|
||||||
elementData[index] = entry.next;
|
|
||||||
} else {
|
|
||||||
while (m.next != entry) {
|
|
||||||
m = m.next;
|
|
||||||
}
|
|
||||||
m.next = entry.next;
|
|
||||||
|
|
||||||
}
|
|
||||||
modCount++;
|
|
||||||
elementCount--;
|
|
||||||
}
|
|
||||||
|
|
||||||
final HashEntry<K, V> removeEntry(Object key) {
|
|
||||||
int index = 0;
|
int index = 0;
|
||||||
HashEntry<K, V> entry;
|
HashEntry<K, V> entry;
|
||||||
HashEntry<K, V> last = null;
|
HashEntry<K, V> last = null;
|
||||||
|
@ -682,8 +640,4 @@ public class THashMap<K, V> extends TAbstractMap<K, V> implements TCloneable, TS
|
||||||
static boolean areEqualKeys(Object key1, Object key2) {
|
static boolean areEqualKeys(Object key1, Object key2) {
|
||||||
return (key1 == key2) || key1.equals(key2);
|
return (key1 == key2) || key1.equals(key2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean areEqualValues(Object value1, Object value2) {
|
|
||||||
return (value1 == value2) || value1.equals(value2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,33 +13,16 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
/*
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file distributed with
|
|
||||||
* this work for additional information regarding copyright ownership.
|
|
||||||
* The ASF licenses this file to You 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;
|
package org.teavm.classlib.java.util;
|
||||||
|
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.BiFunction;
|
||||||
import org.teavm.classlib.java.lang.TIllegalStateException;
|
|
||||||
|
|
||||||
public class TLinkedHashMap<K, V> extends THashMap<K, V> implements TMap<K, V> {
|
public class TLinkedHashMap<K, V> extends THashMap<K, V> implements TSequencedMap<K, V> {
|
||||||
private final boolean accessOrder;
|
private boolean accessOrder;
|
||||||
|
|
||||||
transient private LinkedHashMapEntry<K, V> head;
|
transient LinkedHashMapEntry<K, V> head;
|
||||||
transient private LinkedHashMapEntry<K, V> tail;
|
transient LinkedHashMapEntry<K, V> tail;
|
||||||
|
|
||||||
public TLinkedHashMap() {
|
public TLinkedHashMap() {
|
||||||
accessOrder = false;
|
accessOrder = false;
|
||||||
|
@ -73,127 +56,15 @@ public class TLinkedHashMap<K, V> extends THashMap<K, V> implements TMap<K, V> {
|
||||||
putAll(m);
|
putAll(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class AbstractMapIterator<K, V> {
|
@Override
|
||||||
int expectedModCount;
|
void putAllImpl(TMap<? extends K, ? extends V> map) {
|
||||||
LinkedHashMapEntry<K, V> futureEntry;
|
int capacity = elementCount + map.size();
|
||||||
LinkedHashMapEntry<K, V> currentEntry;
|
if (capacity > threshold) {
|
||||||
final TLinkedHashMap<K, V> associatedMap;
|
rehash(capacity);
|
||||||
|
|
||||||
AbstractMapIterator(TLinkedHashMap<K, V> map) {
|
|
||||||
expectedModCount = map.modCount;
|
|
||||||
futureEntry = map.head;
|
|
||||||
associatedMap = map;
|
|
||||||
}
|
}
|
||||||
|
for (var it = map.entrySet().iterator(); it.hasNext();) {
|
||||||
public boolean hasNext() {
|
TMap.Entry<? extends K, ? extends V> entry = it.next();
|
||||||
return futureEntry != null;
|
putImpl(entry.getKey(), entry.getValue(), false);
|
||||||
}
|
|
||||||
|
|
||||||
final void checkConcurrentMod() throws TConcurrentModificationException {
|
|
||||||
if (expectedModCount != associatedMap.modCount) {
|
|
||||||
throw new TConcurrentModificationException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final void makeNext() {
|
|
||||||
checkConcurrentMod();
|
|
||||||
if (!hasNext()) {
|
|
||||||
throw new TNoSuchElementException();
|
|
||||||
}
|
|
||||||
currentEntry = futureEntry;
|
|
||||||
futureEntry = futureEntry.chainForward;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void remove() {
|
|
||||||
checkConcurrentMod();
|
|
||||||
if (currentEntry == null) {
|
|
||||||
throw new TIllegalStateException();
|
|
||||||
}
|
|
||||||
associatedMap.removeEntry(currentEntry);
|
|
||||||
LinkedHashMapEntry<K, V> lhme = currentEntry;
|
|
||||||
LinkedHashMapEntry<K, V> p = lhme.chainBackward;
|
|
||||||
LinkedHashMapEntry<K, V> n = lhme.chainForward;
|
|
||||||
TLinkedHashMap<K, V> lhm = associatedMap;
|
|
||||||
if (p != null) {
|
|
||||||
p.chainForward = n;
|
|
||||||
if (n != null) {
|
|
||||||
n.chainBackward = p;
|
|
||||||
} else {
|
|
||||||
lhm.tail = p;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
lhm.head = n;
|
|
||||||
if (n != null) {
|
|
||||||
n.chainBackward = null;
|
|
||||||
} else {
|
|
||||||
lhm.tail = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
currentEntry = null;
|
|
||||||
expectedModCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class EntryIterator<K, V> extends AbstractMapIterator<K, V> implements TIterator<Entry<K, V>> {
|
|
||||||
EntryIterator(TLinkedHashMap<K, V> map) {
|
|
||||||
super(map);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Entry<K, V> next() {
|
|
||||||
makeNext();
|
|
||||||
return currentEntry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class KeyIterator<K, V> extends AbstractMapIterator<K, V> implements TIterator<K> {
|
|
||||||
KeyIterator(TLinkedHashMap<K, V> map) {
|
|
||||||
super(map);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public K next() {
|
|
||||||
makeNext();
|
|
||||||
return currentEntry.key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ValueIterator<K, V> extends AbstractMapIterator<K, V> implements TIterator<V> {
|
|
||||||
ValueIterator(TLinkedHashMap<K, V> map) {
|
|
||||||
super(map);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public V next() {
|
|
||||||
makeNext();
|
|
||||||
return currentEntry.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static final class LinkedHashMapEntrySet<K, V> extends HashMapEntrySet<K, V> {
|
|
||||||
public LinkedHashMapEntrySet(TLinkedHashMap<K, V> lhm) {
|
|
||||||
super(lhm);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TIterator<Entry<K, V>> iterator() {
|
|
||||||
return new EntryIterator<>((TLinkedHashMap<K, V>) hashMap());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void forEach(Consumer<? super Entry<K, V>> action) {
|
|
||||||
TLinkedHashMap<K, V> map = (TLinkedHashMap<K, V>) hashMap();
|
|
||||||
if (map.elementCount > 0) {
|
|
||||||
int prevModCount = map.modCount;
|
|
||||||
LinkedHashMapEntry<K, V> entry = map.head;
|
|
||||||
do {
|
|
||||||
action.accept(entry);
|
|
||||||
entry = entry.chainForward;
|
|
||||||
if (map.modCount != prevModCount) {
|
|
||||||
throw new TConcurrentModificationException();
|
|
||||||
}
|
|
||||||
} while (entry != null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,7 +126,7 @@ public class TLinkedHashMap<K, V> extends THashMap<K, V> implements TMap<K, V> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public V get(Object key) {
|
public V getOrDefault(Object key, V defaultValue) {
|
||||||
LinkedHashMapEntry<K, V> m;
|
LinkedHashMapEntry<K, V> m;
|
||||||
if (key == null) {
|
if (key == null) {
|
||||||
m = (LinkedHashMapEntry<K, V>) findNullKeyEntry();
|
m = (LinkedHashMapEntry<K, V>) findNullKeyEntry();
|
||||||
|
@ -265,7 +136,7 @@ public class TLinkedHashMap<K, V> extends THashMap<K, V> implements TMap<K, V> {
|
||||||
m = (LinkedHashMapEntry<K, V>) findNonNullKeyEntry(key, index, hash);
|
m = (LinkedHashMapEntry<K, V>) findNonNullKeyEntry(key, index, hash);
|
||||||
}
|
}
|
||||||
if (m == null) {
|
if (m == null) {
|
||||||
return null;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
if (accessOrder && tail != m) {
|
if (accessOrder && tail != m) {
|
||||||
LinkedHashMapEntry<K, V> p = m.chainBackward;
|
LinkedHashMapEntry<K, V> p = m.chainBackward;
|
||||||
|
@ -285,27 +156,24 @@ public class TLinkedHashMap<K, V> extends THashMap<K, V> implements TMap<K, V> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
HashEntry<K, V> createHashedEntry(K key, int index, int hash) {
|
public V get(Object key) {
|
||||||
|
return getOrDefault(key, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private HashEntry<K, V> createHashedEntry(K key, int index, int hash, boolean first) {
|
||||||
LinkedHashMapEntry<K, V> m = new LinkedHashMapEntry<>(key, hash);
|
LinkedHashMapEntry<K, V> m = new LinkedHashMapEntry<>(key, hash);
|
||||||
m.next = elementData[index];
|
m.next = elementData[index];
|
||||||
elementData[index] = m;
|
elementData[index] = m;
|
||||||
linkEntry(m);
|
linkEntry(m, first);
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public V put(K key, V value) {
|
public V put(K key, V value) {
|
||||||
V result = putImpl(key, value);
|
return putImpl(key, value, false);
|
||||||
|
|
||||||
if (removeEldestEntry(head)) {
|
|
||||||
remove(head.key);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
V putImpl(K key, V value, boolean first) {
|
||||||
V putImpl(K key, V value) {
|
|
||||||
LinkedHashMapEntry<K, V> m;
|
LinkedHashMapEntry<K, V> m;
|
||||||
if (elementCount == 0) {
|
if (elementCount == 0) {
|
||||||
head = null;
|
head = null;
|
||||||
|
@ -321,36 +189,37 @@ public class TLinkedHashMap<K, V> extends THashMap<K, V> implements TMap<K, V> {
|
||||||
if (++elementCount > threshold) {
|
if (++elementCount > threshold) {
|
||||||
rehash();
|
rehash();
|
||||||
}
|
}
|
||||||
m = (LinkedHashMapEntry<K, V>) createHashedEntry(null, 0, 0);
|
m = (LinkedHashMapEntry<K, V>) createHashedEntry(null, 0, 0, first);
|
||||||
} else {
|
} else {
|
||||||
linkEntry(m);
|
linkEntry(m, first);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
int hash = key.hashCode();
|
int hash = key.hashCode();
|
||||||
int index = (hash & 0x7FFFFFFF) % elementData.length;
|
int index = (hash & Integer.MAX_VALUE) % elementData.length;
|
||||||
m = (LinkedHashMapEntry<K, V>) findNonNullKeyEntry(key, index, hash);
|
m = (LinkedHashMapEntry<K, V>) findNonNullKeyEntry(key, index, hash);
|
||||||
if (m == null) {
|
if (m == null) {
|
||||||
modCount++;
|
modCount++;
|
||||||
if (++elementCount > threshold) {
|
if (++elementCount > threshold) {
|
||||||
rehash();
|
rehash();
|
||||||
index = (hash & 0x7FFFFFFF) % elementData.length;
|
index = (hash & Integer.MAX_VALUE) % elementData.length;
|
||||||
}
|
}
|
||||||
m = (LinkedHashMapEntry<K, V>) createHashedEntry(key, index, hash);
|
m = (LinkedHashMapEntry<K, V>) createHashedEntry(key, index, hash, first);
|
||||||
} else {
|
} else {
|
||||||
linkEntry(m);
|
linkEntry(m, first);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
V result = m.value;
|
V result = m.value;
|
||||||
m.value = value;
|
m.value = value;
|
||||||
|
|
||||||
|
if (removeEldestEntry(head)) {
|
||||||
|
remove(head.key);
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void linkEntry(LinkedHashMapEntry<K, V> m) {
|
void linkEntry(LinkedHashMapEntry<K, V> m, boolean first) {
|
||||||
if (tail == m) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (head == null) {
|
if (head == null) {
|
||||||
// Check if the map is empty
|
// Check if the map is empty
|
||||||
head = m;
|
head = m;
|
||||||
|
@ -364,8 +233,8 @@ public class TLinkedHashMap<K, V> extends THashMap<K, V> implements TMap<K, V> {
|
||||||
LinkedHashMapEntry<K, V> n = m.chainForward;
|
LinkedHashMapEntry<K, V> n = m.chainForward;
|
||||||
if (p == null) {
|
if (p == null) {
|
||||||
if (n != null) {
|
if (n != null) {
|
||||||
// The entry must be the head but not the tail
|
// Existing entry must be the head but not the tail
|
||||||
if (accessOrder) {
|
if (!first && accessOrder) {
|
||||||
head = n;
|
head = n;
|
||||||
n.chainBackward = null;
|
n.chainBackward = null;
|
||||||
m.chainBackward = tail;
|
m.chainBackward = tail;
|
||||||
|
@ -375,133 +244,87 @@ public class TLinkedHashMap<K, V> extends THashMap<K, V> implements TMap<K, V> {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// This is a new entry
|
// This is a new entry
|
||||||
m.chainBackward = tail;
|
m.chainBackward = first ? null : tail;
|
||||||
m.chainForward = null;
|
m.chainForward = first ? head : null;
|
||||||
tail.chainForward = m;
|
if (first) {
|
||||||
tail = m;
|
head.chainBackward = m;
|
||||||
|
head = m;
|
||||||
|
} else {
|
||||||
|
tail.chainForward = m;
|
||||||
|
tail = m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (n == null) {
|
||||||
|
// Existing entry must be the tail but not the head
|
||||||
|
if (first && accessOrder) {
|
||||||
|
tail = p;
|
||||||
|
p.chainForward = null;
|
||||||
|
m.chainBackward = null;
|
||||||
|
m.chainForward = head;
|
||||||
|
head.chainBackward = m;
|
||||||
|
head = m;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (elementCount > 1 && accessOrder) {
|
||||||
|
// Existing entry is neither the head nor tail
|
||||||
|
p.chainForward = n;
|
||||||
|
n.chainBackward = p;
|
||||||
|
if (first) {
|
||||||
|
m.chainForward = head;
|
||||||
|
m.chainBackward = null;
|
||||||
|
head.chainBackward = m;
|
||||||
|
head = m;
|
||||||
|
} else {
|
||||||
|
m.chainForward = null;
|
||||||
|
m.chainBackward = tail;
|
||||||
|
tail.chainForward = m;
|
||||||
|
tail = m;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n == null) {
|
|
||||||
// The entry must be the tail so we can't get here
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The entry is neither the head nor tail
|
|
||||||
if (accessOrder) {
|
|
||||||
p.chainForward = n;
|
|
||||||
n.chainBackward = p;
|
|
||||||
m.chainForward = null;
|
|
||||||
m.chainBackward = tail;
|
|
||||||
tail.chainForward = m;
|
|
||||||
tail = m;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TSet<Entry<K, V>> entrySet() {
|
public TSet<Entry<K, V>> entrySet() {
|
||||||
return new LinkedHashMapEntrySet<>(this);
|
return new TLinkedHashMapEntrySet<>(this, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TSet<K> keySet() {
|
public TSet<K> keySet() {
|
||||||
|
return sequencedKeySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TSequencedSet<K> sequencedKeySet() {
|
||||||
if (cachedKeySet == null) {
|
if (cachedKeySet == null) {
|
||||||
cachedKeySet = new TAbstractSet<K>() {
|
cachedKeySet = new TLinkedHashMapKeySet<>(this, false);
|
||||||
@Override
|
|
||||||
public boolean contains(Object object) {
|
|
||||||
return containsKey(object);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int size() {
|
|
||||||
return TLinkedHashMap.this.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clear() {
|
|
||||||
TLinkedHashMap.this.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean remove(Object key) {
|
|
||||||
if (containsKey(key)) {
|
|
||||||
TLinkedHashMap.this.remove(key);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TIterator<K> iterator() {
|
|
||||||
return new KeyIterator<>(TLinkedHashMap.this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void forEach(Consumer<? super K> action) {
|
|
||||||
if (elementCount > 0) {
|
|
||||||
int prevModCount = modCount;
|
|
||||||
LinkedHashMapEntry<K, V> entry = head;
|
|
||||||
do {
|
|
||||||
action.accept(entry.key);
|
|
||||||
entry = entry.chainForward;
|
|
||||||
if (modCount != prevModCount) {
|
|
||||||
throw new TConcurrentModificationException();
|
|
||||||
}
|
|
||||||
} while (entry != null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
return cachedKeySet;
|
return (TSequencedSet<K>) cachedKeySet;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TCollection<V> values() {
|
public TCollection<V> values() {
|
||||||
|
return sequencedValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TSequencedCollection<V> sequencedValues() {
|
||||||
if (cachedValues == null) {
|
if (cachedValues == null) {
|
||||||
cachedValues = new TAbstractCollection<V>() {
|
cachedValues = new TLinkedHashMapValues<>(this, false);
|
||||||
@Override
|
|
||||||
public boolean contains(Object object) {
|
|
||||||
return containsValue(object);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int size() {
|
|
||||||
return TLinkedHashMap.this.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clear() {
|
|
||||||
TLinkedHashMap.this.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TIterator<V> iterator() {
|
|
||||||
return new ValueIterator<>(TLinkedHashMap.this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void forEach(Consumer<? super V> action) {
|
|
||||||
if (elementCount > 0) {
|
|
||||||
int prevModCount = modCount;
|
|
||||||
LinkedHashMapEntry<K, V> entry = head;
|
|
||||||
do {
|
|
||||||
action.accept(entry.value);
|
|
||||||
entry = entry.chainForward;
|
|
||||||
if (modCount != prevModCount) {
|
|
||||||
throw new TConcurrentModificationException();
|
|
||||||
}
|
|
||||||
} while (entry != null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
return cachedValues;
|
return (TSequencedCollection<V>) cachedValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TSequencedSet<Entry<K, V>> sequencedEntrySet() {
|
||||||
|
return new TLinkedHashMapEntrySet<>(this, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public V remove(Object key) {
|
public V remove(Object key) {
|
||||||
LinkedHashMapEntry<K, V> m = (LinkedHashMapEntry<K, V>) removeEntry(key);
|
LinkedHashMapEntry<K, V> m = (LinkedHashMapEntry<K, V>) removeByKey(key);
|
||||||
if (m == null) {
|
if (m == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -509,13 +332,18 @@ public class TLinkedHashMap<K, V> extends THashMap<K, V> implements TMap<K, V> {
|
||||||
LinkedHashMapEntry<K, V> n = m.chainForward;
|
LinkedHashMapEntry<K, V> n = m.chainForward;
|
||||||
if (p != null) {
|
if (p != null) {
|
||||||
p.chainForward = n;
|
p.chainForward = n;
|
||||||
|
if (n != null) {
|
||||||
|
n.chainBackward = p;
|
||||||
|
} else {
|
||||||
|
tail = p;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
head = n;
|
head = n;
|
||||||
}
|
if (n != null) {
|
||||||
if (n != null) {
|
n.chainBackward = null;
|
||||||
n.chainBackward = p;
|
} else {
|
||||||
} else {
|
tail = null;
|
||||||
tail = p;
|
}
|
||||||
}
|
}
|
||||||
return m.value;
|
return m.value;
|
||||||
}
|
}
|
||||||
|
@ -545,4 +373,41 @@ public class TLinkedHashMap<K, V> extends THashMap<K, V> implements TMap<K, V> {
|
||||||
head = null;
|
head = null;
|
||||||
tail = null;
|
tail = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
|
||||||
|
if (elementCount > 0) {
|
||||||
|
int prevModCount = modCount;
|
||||||
|
LinkedHashMapEntry<K, V> entry = head;
|
||||||
|
do {
|
||||||
|
entry.value = function.apply(entry.key, entry.value);
|
||||||
|
entry = entry.chainForward;
|
||||||
|
if (modCount != prevModCount) {
|
||||||
|
throw new TConcurrentModificationException();
|
||||||
|
}
|
||||||
|
} while (entry != null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V putFirst(K k, V v) {
|
||||||
|
return putImpl(k, v, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V putLast(K k, V v) {
|
||||||
|
return putImpl(k, v, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TSequencedMap<K, V> reversed() {
|
||||||
|
return new TReversedLinkedHashMap<>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static <T> T checkNotNull(T node) {
|
||||||
|
if (node == null) {
|
||||||
|
throw new TNoSuchElementException();
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,121 @@
|
||||||
|
/*
|
||||||
|
* 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 java.util.function.Consumer;
|
||||||
|
|
||||||
|
class TLinkedHashMapEntrySet<K, V> extends TAbstractSet<TMap.Entry<K, V>>
|
||||||
|
implements TSequencedSet<TMap.Entry<K, V>> {
|
||||||
|
private final TLinkedHashMap<K, V> base;
|
||||||
|
private final boolean reversed;
|
||||||
|
|
||||||
|
TLinkedHashMapEntrySet(TLinkedHashMap<K, V> base, boolean reversed) {
|
||||||
|
this.base = base;
|
||||||
|
this.reversed = reversed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final int size() {
|
||||||
|
return base.elementCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void clear() {
|
||||||
|
base.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final TIterator<TMap.Entry<K, V>> iterator() {
|
||||||
|
return new TLinkedHashMapIterator.EntryIterator<>(base, reversed);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean contains(Object o) {
|
||||||
|
if (o instanceof TMap.Entry) {
|
||||||
|
TMap.Entry<?, ?> oEntry = (TMap.Entry<?, ?>) o;
|
||||||
|
TMap.Entry<K, V> entry = base.entryByKey(oEntry.getKey());
|
||||||
|
return entry != null && TObjects.equals(entry.getValue(), oEntry.getValue());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(Object object) {
|
||||||
|
if (object instanceof TMap.Entry) {
|
||||||
|
TMap.Entry<?, ?> oEntry = (TMap.Entry<?, ?>) object;
|
||||||
|
TMap.Entry<K, V> entry = base.entryByKey(oEntry.getKey());
|
||||||
|
if (entry != null && TObjects.equals(entry.getValue(), oEntry.getValue())) {
|
||||||
|
base.remove(entry.getKey());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void forEach(Consumer<? super TMap.Entry<K, V>> action) {
|
||||||
|
if (base.elementCount > 0) {
|
||||||
|
int prevModCount = base.modCount;
|
||||||
|
TLinkedHashMap.LinkedHashMapEntry<K, V> entry = reversed ? base.tail : base.head;
|
||||||
|
do {
|
||||||
|
action.accept(entry);
|
||||||
|
entry = reversed ? entry.chainBackward : entry.chainForward;
|
||||||
|
if (base.modCount != prevModCount) {
|
||||||
|
throw new TConcurrentModificationException();
|
||||||
|
}
|
||||||
|
} while (entry != null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void addFirst(TMap.Entry<K, V> e) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void addLast(TMap.Entry<K, V> e) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final TMap.Entry<K, V> getFirst() {
|
||||||
|
return TLinkedHashMap.checkNotNull(reversed ? base.tail : base.head);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final TMap.Entry<K, V> getLast() {
|
||||||
|
return TLinkedHashMap.checkNotNull(reversed ? base.head : base.tail);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final TMap.Entry<K, V> removeFirst() {
|
||||||
|
var e = TLinkedHashMap.checkNotNull(reversed ? base.tail : base.head);
|
||||||
|
base.remove(e.key);
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final TMap.Entry<K, V> removeLast() {
|
||||||
|
var e = TLinkedHashMap.checkNotNull(reversed ? base.head : base.tail);
|
||||||
|
base.remove(e.key);
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TSequencedSet<TMap.Entry<K, V>> reversed() {
|
||||||
|
return new TLinkedHashMapEntrySet<>(base, !reversed);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
/*
|
||||||
|
* 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 TLinkedHashMapIterator<K, V> {
|
||||||
|
private final TLinkedHashMap<K, V> base;
|
||||||
|
private final boolean reversed;
|
||||||
|
private int expectedModCount;
|
||||||
|
private TLinkedHashMap.LinkedHashMapEntry<K, V> futureEntry;
|
||||||
|
TLinkedHashMap.LinkedHashMapEntry<K, V> currentEntry;
|
||||||
|
|
||||||
|
TLinkedHashMapIterator(TLinkedHashMap<K, V> base, boolean reversed) {
|
||||||
|
this.base = base;
|
||||||
|
this.reversed = reversed;
|
||||||
|
expectedModCount = base.modCount;
|
||||||
|
futureEntry = reversed ? base.tail : base.head;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasNext() {
|
||||||
|
return futureEntry != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final void checkConcurrentMod() throws TConcurrentModificationException {
|
||||||
|
if (expectedModCount != base.modCount) {
|
||||||
|
throw new TConcurrentModificationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final void makeNext() {
|
||||||
|
checkConcurrentMod();
|
||||||
|
if (!hasNext()) {
|
||||||
|
throw new TNoSuchElementException();
|
||||||
|
}
|
||||||
|
currentEntry = futureEntry;
|
||||||
|
futureEntry = reversed ? futureEntry.chainBackward : futureEntry.chainForward;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove() {
|
||||||
|
if (currentEntry == null) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
checkConcurrentMod();
|
||||||
|
base.remove(currentEntry.key);
|
||||||
|
currentEntry = null;
|
||||||
|
expectedModCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class EntryIterator<K, V> extends TLinkedHashMapIterator<K, V> implements TIterator<TMap.Entry<K, V>> {
|
||||||
|
EntryIterator(TLinkedHashMap<K, V> map, boolean reversed) {
|
||||||
|
super(map, reversed);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TMap.Entry<K, V> next() {
|
||||||
|
makeNext();
|
||||||
|
return currentEntry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class KeyIterator<K, V> extends TLinkedHashMapIterator<K, V> implements TIterator<K> {
|
||||||
|
KeyIterator(TLinkedHashMap<K, V> map, boolean reversed) {
|
||||||
|
super(map, reversed);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public K next() {
|
||||||
|
makeNext();
|
||||||
|
return currentEntry.key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ValueIterator<K, V> extends TLinkedHashMapIterator<K, V> implements TIterator<V> {
|
||||||
|
ValueIterator(TLinkedHashMap<K, V> map, boolean reversed) {
|
||||||
|
super(map, reversed);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V next() {
|
||||||
|
makeNext();
|
||||||
|
return currentEntry.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
/*
|
||||||
|
* 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 java.util.function.Consumer;
|
||||||
|
|
||||||
|
class TLinkedHashMapKeySet<K> extends TAbstractSet<K> implements TSequencedSet<K> {
|
||||||
|
private final TLinkedHashMap<K, ?> base;
|
||||||
|
private final boolean reversed;
|
||||||
|
|
||||||
|
TLinkedHashMapKeySet(TLinkedHashMap<K, ?> base, boolean reversed) {
|
||||||
|
this.base = base;
|
||||||
|
this.reversed = reversed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final int size() {
|
||||||
|
return base.elementCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void clear() {
|
||||||
|
base.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final TIterator<K> iterator() {
|
||||||
|
return new TLinkedHashMapIterator.KeyIterator<>(base, reversed);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean contains(Object o) {
|
||||||
|
return base.containsKey(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean remove(Object key) {
|
||||||
|
int befCount = base.elementCount;
|
||||||
|
base.remove(key);
|
||||||
|
return base.elementCount != befCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void forEach(Consumer<? super K> action) {
|
||||||
|
if (base.elementCount > 0) {
|
||||||
|
int prevModCount = base.modCount;
|
||||||
|
TLinkedHashMap.LinkedHashMapEntry<K, ?> entry = reversed ? base.tail : base.head;
|
||||||
|
do {
|
||||||
|
action.accept(entry.key);
|
||||||
|
entry = reversed ? entry.chainBackward : entry.chainForward;
|
||||||
|
if (base.modCount != prevModCount) {
|
||||||
|
throw new TConcurrentModificationException();
|
||||||
|
}
|
||||||
|
} while (entry != null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final K getFirst() {
|
||||||
|
return TLinkedHashMap.checkNotNull(reversed ? base.tail : base.head).key;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final K getLast() {
|
||||||
|
return TLinkedHashMap.checkNotNull(reversed ? base.head : base.tail).key;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final K removeFirst() {
|
||||||
|
var e = TLinkedHashMap.checkNotNull(reversed ? base.tail : base.head);
|
||||||
|
base.remove(e.key);
|
||||||
|
return e.key;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final K removeLast() {
|
||||||
|
var e = TLinkedHashMap.checkNotNull(reversed ? base.head : base.tail);
|
||||||
|
base.remove(e.key);
|
||||||
|
return e.key;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TSequencedSet<K> reversed() {
|
||||||
|
if (reversed) {
|
||||||
|
return base.sequencedKeySet();
|
||||||
|
} else {
|
||||||
|
return new TLinkedHashMapKeySet<>(base, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
/*
|
||||||
|
* 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 java.util.function.Consumer;
|
||||||
|
|
||||||
|
class TLinkedHashMapValues<V> extends TAbstractCollection<V> implements TSequencedCollection<V> {
|
||||||
|
private final TLinkedHashMap<?, V> base;
|
||||||
|
private final boolean reversed;
|
||||||
|
|
||||||
|
TLinkedHashMapValues(TLinkedHashMap<?, V> base, boolean reversed) {
|
||||||
|
this.base = base;
|
||||||
|
this.reversed = reversed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final int size() {
|
||||||
|
return base.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void clear() {
|
||||||
|
base.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final TIterator<V> iterator() {
|
||||||
|
return new TLinkedHashMapIterator.ValueIterator<>(base, reversed);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean contains(Object o) {
|
||||||
|
return base.containsValue(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void forEach(Consumer<? super V> action) {
|
||||||
|
if (base.elementCount > 0) {
|
||||||
|
int prevModCount = base.modCount;
|
||||||
|
TLinkedHashMap.LinkedHashMapEntry<?, V> entry = reversed ? base.tail : base.head;
|
||||||
|
do {
|
||||||
|
action.accept(entry.value);
|
||||||
|
entry = reversed ? entry.chainBackward : entry.chainForward;
|
||||||
|
if (base.modCount != prevModCount) {
|
||||||
|
throw new TConcurrentModificationException();
|
||||||
|
}
|
||||||
|
} while (entry != null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final V getFirst() {
|
||||||
|
return TLinkedHashMap.checkNotNull(reversed ? base.tail : base.head).value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final V getLast() {
|
||||||
|
return TLinkedHashMap.checkNotNull(reversed ? base.head : base.tail).value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final V removeFirst() {
|
||||||
|
THashMap.HashEntry<?, V> e = TLinkedHashMap.checkNotNull(reversed ? base.tail : base.head);
|
||||||
|
return base.remove(e.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final V removeLast() {
|
||||||
|
THashMap.HashEntry<?, V> e = TLinkedHashMap.checkNotNull(reversed ? base.head : base.tail);
|
||||||
|
return base.remove(e.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TSequencedCollection<V> reversed() {
|
||||||
|
if (reversed) {
|
||||||
|
return base.sequencedValues();
|
||||||
|
} else {
|
||||||
|
return new TLinkedHashMapValues<>(base, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,42 +13,26 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
/*
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file distributed with
|
|
||||||
* this work for additional information regarding copyright ownership.
|
|
||||||
* The ASF licenses this file to You 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;
|
package org.teavm.classlib.java.util;
|
||||||
|
|
||||||
import org.teavm.classlib.java.io.TSerializable;
|
import org.teavm.classlib.java.io.TSerializable;
|
||||||
import org.teavm.classlib.java.lang.TCloneable;
|
import org.teavm.classlib.java.lang.TCloneable;
|
||||||
|
|
||||||
public class TLinkedHashSet<E> extends THashSet<E> implements TSet<E>, TCloneable, TSerializable {
|
public class TLinkedHashSet<E> extends THashSet<E> implements TSequencedSet<E>, TCloneable, TSerializable {
|
||||||
public TLinkedHashSet() {
|
public TLinkedHashSet() {
|
||||||
super(new TLinkedHashMap<E, THashSet<E>>());
|
super(new TLinkedHashMap<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
public TLinkedHashSet(int capacity) {
|
public TLinkedHashSet(int capacity) {
|
||||||
super(new TLinkedHashMap<E, THashSet<E>>(capacity));
|
super(new TLinkedHashMap<>(capacity));
|
||||||
}
|
}
|
||||||
|
|
||||||
public TLinkedHashSet(int capacity, float loadFactor) {
|
public TLinkedHashSet(int capacity, float loadFactor) {
|
||||||
super(new TLinkedHashMap<E, THashSet<E>>(capacity, loadFactor));
|
super(new TLinkedHashMap<>(capacity, loadFactor));
|
||||||
}
|
}
|
||||||
|
|
||||||
public TLinkedHashSet(TCollection<? extends E> collection) {
|
public TLinkedHashSet(TCollection<? extends E> collection) {
|
||||||
super(new TLinkedHashMap<E, THashSet<E>>(collection.size() < 6 ? 11 : collection.size() * 2));
|
super(new TLinkedHashMap<>(collection.size() < 6 ? 11 : collection.size() * 2));
|
||||||
for (TIterator<? extends E> iter = collection.iterator(); iter.hasNext();) {
|
for (TIterator<? extends E> iter = collection.iterator(); iter.hasNext();) {
|
||||||
add(iter.next());
|
add(iter.next());
|
||||||
}
|
}
|
||||||
|
@ -59,4 +43,101 @@ public class TLinkedHashSet<E> extends THashSet<E> implements TSet<E>, TCloneabl
|
||||||
THashMap<E, THashSet<E>> createBackingMap(int capacity, float loadFactor) {
|
THashMap<E, THashSet<E>> createBackingMap(int capacity, float loadFactor) {
|
||||||
return new TLinkedHashMap<>(capacity, loadFactor);
|
return new TLinkedHashMap<>(capacity, loadFactor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private TLinkedHashMap<E, THashSet<E>> map() {
|
||||||
|
return (TLinkedHashMap<E, THashSet<E>>) backingMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addFirst(E e) {
|
||||||
|
map().putFirst(e, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addLast(E e) {
|
||||||
|
map().putLast(e, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E getFirst() {
|
||||||
|
return map().sequencedKeySet().getFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E getLast() {
|
||||||
|
return map().sequencedKeySet().getLast();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E removeFirst() {
|
||||||
|
return map().sequencedKeySet().removeFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E removeLast() {
|
||||||
|
return map().sequencedKeySet().removeLast();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TSequencedSet<E> reversed() {
|
||||||
|
return new ReversedLinkedHashSet<>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ReversedLinkedHashSet<E> extends TAbstractSet<E> implements TSequencedSet<E> {
|
||||||
|
private final TLinkedHashSet<E> base;
|
||||||
|
|
||||||
|
ReversedLinkedHashSet(TLinkedHashSet<E> base) {
|
||||||
|
this.base = base;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return base.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TIterator<E> iterator() {
|
||||||
|
return base.map().sequencedKeySet().reversed().iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean add(E e) {
|
||||||
|
return base.add(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addFirst(E e) {
|
||||||
|
base.addLast(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addLast(E e) {
|
||||||
|
base.addFirst(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E getFirst() {
|
||||||
|
return base.getLast();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E getLast() {
|
||||||
|
return base.getFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E removeFirst() {
|
||||||
|
return base.removeLast();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E removeLast() {
|
||||||
|
return base.removeFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TSequencedSet<E> reversed() {
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,9 +68,7 @@ class TMapEntry<K, V> implements TMap.Entry<K, V>, Cloneable {
|
||||||
}
|
}
|
||||||
if (object instanceof TMap.Entry) {
|
if (object instanceof TMap.Entry) {
|
||||||
TMap.Entry<?, ?> entry = (TMap.Entry<?, ?>) object;
|
TMap.Entry<?, ?> entry = (TMap.Entry<?, ?>) object;
|
||||||
return (key == null ? entry.getKey() == null : key.equals(entry.getKey()))
|
return TObjects.equals(key, entry.getKey()) && TObjects.equals(value, entry.getValue());
|
||||||
&& (value == null ? entry.getValue() == null : value
|
|
||||||
.equals(entry.getValue()));
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -87,8 +85,7 @@ class TMapEntry<K, V> implements TMap.Entry<K, V>, Cloneable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return (key == null ? 0 : key.hashCode())
|
return TObjects.hashCode(key) ^ TObjects.hashCode(value);
|
||||||
^ (value == null ? 0 : value.hashCode());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,223 @@
|
||||||
|
/*
|
||||||
|
* 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 java.util.function.BiConsumer;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
class TReversedLinkedHashMap<K, V> extends TAbstractMap<K, V> implements TSequencedMap<K, V> {
|
||||||
|
private final TLinkedHashMap<K, V> base;
|
||||||
|
|
||||||
|
TReversedLinkedHashMap(TLinkedHashMap<K, V> base) {
|
||||||
|
this.base = base;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
return base.equals(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return base.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return base.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return base.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsKey(Object key) {
|
||||||
|
return base.containsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsValue(Object value) {
|
||||||
|
return base.containsValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V get(Object key) {
|
||||||
|
return base.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V put(K key, V value) {
|
||||||
|
return base.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V remove(Object key) {
|
||||||
|
return base.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putAll(TMap<? extends K, ? extends V> m) {
|
||||||
|
base.putAll(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
base.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TSet<K> keySet() {
|
||||||
|
return base.sequencedKeySet().reversed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TCollection<V> values() {
|
||||||
|
return base.sequencedValues().reversed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TSet<TMap.Entry<K, V>> entrySet() {
|
||||||
|
return base.sequencedEntrySet().reversed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V getOrDefault(Object key, V defaultValue) {
|
||||||
|
return base.getOrDefault(key, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void forEach(BiConsumer<? super K, ? super V> action) {
|
||||||
|
if (base.elementCount > 0) {
|
||||||
|
int prevModCount = base.modCount;
|
||||||
|
TLinkedHashMap.LinkedHashMapEntry<K, V> entry = base.tail;
|
||||||
|
do {
|
||||||
|
action.accept(entry.key, entry.value);
|
||||||
|
entry = entry.chainBackward;
|
||||||
|
if (base.modCount != prevModCount) {
|
||||||
|
throw new TConcurrentModificationException();
|
||||||
|
}
|
||||||
|
} while (entry != null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
|
||||||
|
if (base.elementCount > 0) {
|
||||||
|
int prevModCount = base.modCount;
|
||||||
|
TLinkedHashMap.LinkedHashMapEntry<K, V> entry = base.tail;
|
||||||
|
do {
|
||||||
|
entry.value = function.apply(entry.key, entry.value);
|
||||||
|
entry = entry.chainBackward;
|
||||||
|
if (base.modCount != prevModCount) {
|
||||||
|
throw new TConcurrentModificationException();
|
||||||
|
}
|
||||||
|
} while (entry != null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V putIfAbsent(K key, V value) {
|
||||||
|
return base.putIfAbsent(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(Object key, Object value) {
|
||||||
|
return base.remove(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean replace(K key, V oldValue, V newValue) {
|
||||||
|
return base.replace(key, oldValue, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V replace(K key, V value) {
|
||||||
|
return base.replace(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
|
||||||
|
return base.computeIfAbsent(key, mappingFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
|
||||||
|
return base.computeIfPresent(key, remappingFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
|
||||||
|
return base.compute(key, remappingFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
|
||||||
|
return base.merge(key, value, remappingFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TSequencedMap<K, V> reversed() {
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TMap.Entry<K, V> firstEntry() {
|
||||||
|
return base.lastEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TMap.Entry<K, V> lastEntry() {
|
||||||
|
return base.firstEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TMap.Entry<K, V> pollFirstEntry() {
|
||||||
|
return base.pollLastEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TMap.Entry<K, V> pollLastEntry() {
|
||||||
|
return base.pollFirstEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V putFirst(K k, V v) {
|
||||||
|
return base.putLast(k, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V putLast(K k, V v) {
|
||||||
|
return base.putFirst(k, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TSequencedSet<K> sequencedKeySet() {
|
||||||
|
return new TLinkedHashMapKeySet<>(base, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TSequencedCollection<V> sequencedValues() {
|
||||||
|
return new TLinkedHashMapValues<>(base, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TSequencedSet<Entry<K, V>> sequencedEntrySet() {
|
||||||
|
return new TLinkedHashMapEntrySet<>(base, true);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
public interface TSequencedMap<K, V> extends TMap<K, V> {
|
||||||
|
TSequencedMap<K, V> reversed();
|
||||||
|
|
||||||
|
private static <K, V> TMap.Entry<K, V> clone(TMap.Entry<K, V> entry) {
|
||||||
|
return TMap.entry(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
default TMap.Entry<K, V> firstEntry() {
|
||||||
|
var it = entrySet().iterator();
|
||||||
|
return it.hasNext() ? clone(it.next()) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
default TMap.Entry<K, V> lastEntry() {
|
||||||
|
var it = reversed().entrySet().iterator();
|
||||||
|
return it.hasNext() ? clone(it.next()) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
default TMap.Entry<K, V> pollFirstEntry() {
|
||||||
|
var it = entrySet().iterator();
|
||||||
|
if (it.hasNext()) {
|
||||||
|
var entry = clone(it.next());
|
||||||
|
it.remove();
|
||||||
|
return entry;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default TMap.Entry<K, V> pollLastEntry() {
|
||||||
|
var it = reversed().entrySet().iterator();
|
||||||
|
if (it.hasNext()) {
|
||||||
|
var entry = clone(it.next());
|
||||||
|
it.remove();
|
||||||
|
return entry;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default V putFirst(K k, V v) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
default V putLast(K k, V v) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
TSequencedSet<K> sequencedKeySet();
|
||||||
|
|
||||||
|
TSequencedCollection<V> sequencedValues();
|
||||||
|
|
||||||
|
TSequencedSet<TMap.Entry<K, V>> sequencedEntrySet();
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
public interface TSequencedSet<E> extends TSequencedCollection<E>, TSet<E> {
|
||||||
|
@Override
|
||||||
|
TSequencedSet<E> reversed();
|
||||||
|
}
|
|
@ -799,9 +799,7 @@ public final class TTemplateCollections {
|
||||||
}
|
}
|
||||||
if (object instanceof TMap.Entry) {
|
if (object instanceof TMap.Entry) {
|
||||||
TMap.Entry<?, ?> entry = (TMap.Entry<?, ?>) object;
|
TMap.Entry<?, ?> entry = (TMap.Entry<?, ?>) object;
|
||||||
return (key == null ? entry.getKey() == null : key.equals(entry.getKey()))
|
return TObjects.equals(key, entry.getKey()) && TObjects.equals(value, entry.getValue());
|
||||||
&& (value == null ? entry.getValue() == null : value
|
|
||||||
.equals(entry.getValue()));
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -818,8 +816,7 @@ public final class TTemplateCollections {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return (key == null ? 0 : key.hashCode())
|
return TObjects.hashCode(key) ^ TObjects.hashCode(value);
|
||||||
^ (value == null ? 0 : value.hashCode());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -70,7 +70,7 @@ class TMapEntry<K, V> implements TMap.Entry<K, V>, Cloneable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return (key == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode());
|
return Objects.hashCode(key) ^ Objects.hashCode(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -37,6 +37,7 @@ public interface TPredicate<T> {
|
||||||
return t -> TObjects.equals(t, targetRef);
|
return t -> TObjects.equals(t, targetRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
static <T> TPredicate<T> not(TPredicate<? super T> target) {
|
static <T> TPredicate<T> not(TPredicate<? super T> target) {
|
||||||
return (TPredicate<T>) target.negate();
|
return (TPredicate<T>) target.negate();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,984 @@
|
||||||
|
/*
|
||||||
|
* 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.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertSame;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.ConcurrentModificationException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.teavm.classlib.support.MapTest2Support;
|
||||||
|
import org.teavm.classlib.support.UnmodifiableCollectionTestSupport;
|
||||||
|
import org.teavm.junit.TeaVMTestRunner;
|
||||||
|
import org.teavm.junit.WholeClassCompilation;
|
||||||
|
|
||||||
|
@RunWith(TeaVMTestRunner.class)
|
||||||
|
@WholeClassCompilation
|
||||||
|
public class HashMapTest {
|
||||||
|
private HashMap<String, String> ht10;
|
||||||
|
|
||||||
|
private HashMap<String, Integer> ht100;
|
||||||
|
|
||||||
|
private HashMap<String, String> htfull;
|
||||||
|
|
||||||
|
private List<String> keyList;
|
||||||
|
|
||||||
|
private List<String> elmList;
|
||||||
|
|
||||||
|
public HashMapTest() {
|
||||||
|
ht10 = new HashMap<>(10);
|
||||||
|
ht100 = new HashMap<>(100);
|
||||||
|
htfull = new HashMap<>(10);
|
||||||
|
keyList = new ArrayList<>(10);
|
||||||
|
elmList = new ArrayList<>(10);
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
ht10.put("Key " + i, "Val " + i);
|
||||||
|
keyList.add("Key " + i);
|
||||||
|
elmList.add("Val " + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 7; i++) {
|
||||||
|
htfull.put("FKey " + i, "FVal " + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_Constructor() {
|
||||||
|
// Test for method java.util.HashMap()
|
||||||
|
new MapTest2Support(new HashMap<>()).runTest();
|
||||||
|
|
||||||
|
HashMap<String, String> h = new HashMap<>();
|
||||||
|
|
||||||
|
assertEquals("Created incorrect HashMap", 0, h.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_ConstructorI() {
|
||||||
|
// Test for method java.util.HashMap(int)
|
||||||
|
HashMap<String, String> h = new HashMap<>(9);
|
||||||
|
|
||||||
|
assertEquals("Created incorrect HashMap", 0, h.size());
|
||||||
|
|
||||||
|
HashMap<String, String> empty = new HashMap<>(0);
|
||||||
|
assertNull("Empty HashMap access", empty.get("nothing"));
|
||||||
|
empty.put("something", "here");
|
||||||
|
assertEquals("cannot get element", "here", empty.get("something"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_ConstructorIF() {
|
||||||
|
// Test for method java.util.HashMap(int, float)
|
||||||
|
HashMap<String, String> h = new HashMap<>(10, 0.5f);
|
||||||
|
assertEquals("Created incorrect HashMap", 0, h.size());
|
||||||
|
|
||||||
|
HashMap<String, String> empty = new HashMap<>(0, 0.75f);
|
||||||
|
assertNull("Empty HashMap access", empty.get("nothing"));
|
||||||
|
empty.put("something", "here");
|
||||||
|
assertEquals("cannot get element", "here", empty.get("something"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_ConstructorLjava_util_Map() {
|
||||||
|
// Test for method java.util.HashMap(java.util.Map)
|
||||||
|
Map<String, Object> map = new TreeMap<>();
|
||||||
|
Object firstVal = "Gabba";
|
||||||
|
Object secondVal = 5;
|
||||||
|
map.put("Gah", firstVal);
|
||||||
|
map.put("Ooga", secondVal);
|
||||||
|
HashMap<String, Object> ht = new HashMap<>(map);
|
||||||
|
assertSame("a) Incorrect HashMap constructed", firstVal, ht.get("Gah"));
|
||||||
|
assertSame("b) Incorrect HashMap constructed", secondVal, ht.get("Ooga"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void test_HashMap_Constructor() {
|
||||||
|
HashMap<HashMap<?, ?>, Set<HashMap<?, ?>>> hashMap = new HashMap<>();
|
||||||
|
hashMap.put(hashMap, hashMap.keySet());
|
||||||
|
new HashMap<>(hashMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_clear() {
|
||||||
|
// Test for method void java.util.HashMap.clear()
|
||||||
|
HashMap<String, String> h = hashMapClone(htfull);
|
||||||
|
h.clear();
|
||||||
|
assertEquals("HashMap was not cleared", 0, h.size());
|
||||||
|
Iterator<String> el = h.values().iterator();
|
||||||
|
Iterator<String> keys = h.keySet().iterator();
|
||||||
|
assertTrue("HashMap improperly cleared", !el.hasNext() && !keys.hasNext());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_clone() {
|
||||||
|
// Test for method java.lang.Object java.util.HashMap.clone()
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
HashMap<String, String> h = (HashMap<String, String>) htfull.clone();
|
||||||
|
assertEquals("Clone different size than original", h.size(), htfull.size());
|
||||||
|
|
||||||
|
Iterator<String> org = htfull.keySet().iterator();
|
||||||
|
Iterator<String> cpy = h.keySet().iterator();
|
||||||
|
|
||||||
|
String okey;
|
||||||
|
String ckey;
|
||||||
|
while (org.hasNext()) {
|
||||||
|
okey = org.next();
|
||||||
|
ckey = cpy.next();
|
||||||
|
assertEquals("Key comparison failed", okey, ckey);
|
||||||
|
assertEquals("Value comparison failed", htfull.get(okey), h.get(ckey));
|
||||||
|
}
|
||||||
|
assertFalse("Copy has more keys than original", cpy.hasNext());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_containsLjava_lang_Object() {
|
||||||
|
// Test for method boolean
|
||||||
|
// java.util.HashMap.contains(java.lang.Object)
|
||||||
|
assertTrue("Element not found", ht10.containsValue("Val 7"));
|
||||||
|
assertFalse("Invalid element found", ht10.containsValue("ZZZZZZZZZZZZZZZZ"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_containsKeyLjava_lang_Object() {
|
||||||
|
// Test for method boolean
|
||||||
|
// java.util.HashMap.containsKey(java.lang.Object)
|
||||||
|
|
||||||
|
assertTrue("Failed to find key", htfull.containsKey("FKey 4"));
|
||||||
|
assertFalse("Failed to find key", htfull.containsKey("FKey 99"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_containsValueLjava_lang_Object() {
|
||||||
|
// Test for method boolean
|
||||||
|
// java.util.HashMap.containsValue(java.lang.Object)
|
||||||
|
for (String s : elmList) {
|
||||||
|
assertTrue("Returned false for valid value", ht10.containsValue(s));
|
||||||
|
}
|
||||||
|
assertFalse("Returned true for invalid value", ht10.containsValue(new Object()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_elements() {
|
||||||
|
// Test for method java.util.Enumeration java.util.HashMap.elements()
|
||||||
|
Iterator<String> elms = ht10.values().iterator();
|
||||||
|
while (elms.hasNext()) {
|
||||||
|
String s = elms.next();
|
||||||
|
assertTrue("Missing key from enumeration", elmList.contains(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals("All keys not retrieved", 10, ht10.size());
|
||||||
|
|
||||||
|
assertFalse(elms.hasNext());
|
||||||
|
try {
|
||||||
|
elms.next();
|
||||||
|
fail("should throw NoSuchElementException");
|
||||||
|
} catch (NoSuchElementException e) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_elements_subtest0() {
|
||||||
|
// this is the reference implementation behavior
|
||||||
|
final HashMap<String, String> ht = new HashMap<>(7);
|
||||||
|
ht.put("1", "a");
|
||||||
|
// these three elements hash to the same bucket in a 7 element HashMap
|
||||||
|
ht.put("2", "b");
|
||||||
|
ht.put("9", "c");
|
||||||
|
ht.put("12", "d");
|
||||||
|
// HashMap looks like:
|
||||||
|
// 0: "1"
|
||||||
|
// 1: "12" -> "9" -> "2"
|
||||||
|
Iterator<String> en = ht.values().iterator();
|
||||||
|
// cache the first entry
|
||||||
|
en.hasNext();
|
||||||
|
ht.remove("12");
|
||||||
|
ht.remove("9");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_entrySet() {
|
||||||
|
// Test for method java.util.Set java.util.HashMap.entrySet()
|
||||||
|
Set<Map.Entry<String, String>> s = ht10.entrySet();
|
||||||
|
Set<String> s2 = new HashSet<>();
|
||||||
|
for (Map.Entry<String, String> entry : s) {
|
||||||
|
s2.add(entry.getValue());
|
||||||
|
}
|
||||||
|
for (String string : elmList) {
|
||||||
|
assertTrue("Returned incorrect entry set", s2.contains(string));
|
||||||
|
}
|
||||||
|
|
||||||
|
ht10.entrySet().iterator().next().setValue(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_equalsLjava_lang_Object() {
|
||||||
|
// Test for method boolean java.util.HashMap.equals(java.lang.Object)
|
||||||
|
HashMap<String, String> h = hashMapClone(ht10);
|
||||||
|
assertEquals("Returned false for equal tables", ht10, h);
|
||||||
|
assertFalse("Returned true for unequal tables", ht10.equals(htfull));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_getLjava_lang_Object() {
|
||||||
|
// Test for method java.lang.Object
|
||||||
|
// java.util.HashMap.get(java.lang.Object)
|
||||||
|
HashMap<String, String> h = hashMapClone(htfull);
|
||||||
|
assertEquals("Could not retrieve element", "FVal 2", h.get("FKey 2"));
|
||||||
|
|
||||||
|
// Regression for HARMONY-262
|
||||||
|
org.teavm.classlib.java.util.HashMapTest.ReusableKey
|
||||||
|
k = new org.teavm.classlib.java.util.HashMapTest.ReusableKey();
|
||||||
|
HashMap<org.teavm.classlib.java.util.HashMapTest.ReusableKey, String> h2 = new HashMap<>();
|
||||||
|
k.setKey(1);
|
||||||
|
h2.put(k, "value1");
|
||||||
|
|
||||||
|
k.setKey(13);
|
||||||
|
assertNull(h2.get(k));
|
||||||
|
|
||||||
|
k.setKey(12);
|
||||||
|
assertNull(h2.get(k));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_hashCode() {
|
||||||
|
// Test for method int java.util.HashMap.hashCode()
|
||||||
|
int expectedHash = 0;
|
||||||
|
for (Map.Entry<String, String> e : ht10.entrySet()) {
|
||||||
|
expectedHash += e.hashCode();
|
||||||
|
}
|
||||||
|
assertEquals("Incorrect hashCode returned. Wanted: " + expectedHash + " got: " + ht10.hashCode(),
|
||||||
|
expectedHash, ht10.hashCode());
|
||||||
|
assertEquals(ht10.hashCode(), ht10.entrySet().hashCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_isEmpty() {
|
||||||
|
// Test for method boolean java.util.HashMap.isEmpty()
|
||||||
|
|
||||||
|
assertFalse("isEmpty returned incorrect value", ht10.isEmpty());
|
||||||
|
assertTrue("isEmpty returned incorrect value", new HashMap<String, String>().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_keys() {
|
||||||
|
// Test for method java.util.Enumeration java.util.HashMap.keys()
|
||||||
|
|
||||||
|
Iterator<String> keys = ht10.keySet().iterator();
|
||||||
|
while (keys.hasNext()) {
|
||||||
|
String s = keys.next();
|
||||||
|
assertTrue("Missing key from enumeration", keyList.contains(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals("All keys not retrieved", 10, ht10.size());
|
||||||
|
|
||||||
|
assertFalse(keys.hasNext());
|
||||||
|
try {
|
||||||
|
keys.next();
|
||||||
|
fail("should throw NoSuchElementException");
|
||||||
|
} catch (NoSuchElementException e) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_keys_subtest0() {
|
||||||
|
// this is the reference implementation behavior
|
||||||
|
final HashMap<String, String> ht = new HashMap<>(3);
|
||||||
|
ht.put("initial", "");
|
||||||
|
Iterator<String> en = ht.keySet().iterator();
|
||||||
|
en.hasNext();
|
||||||
|
ht.remove("initial");
|
||||||
|
try {
|
||||||
|
en.next();
|
||||||
|
fail();
|
||||||
|
} catch (ConcurrentModificationException e) {
|
||||||
|
// ok
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_keySet() {
|
||||||
|
// Test for method java.util.Set java.util.HashMap.keySet()
|
||||||
|
Set<String> s = ht10.keySet();
|
||||||
|
for (String string : keyList) {
|
||||||
|
assertTrue("Returned incorrect key set", s.contains(string));
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<Integer, String> map = new HashMap<>(101);
|
||||||
|
map.put(1, "1");
|
||||||
|
map.put(102, "102");
|
||||||
|
map.put(203, "203");
|
||||||
|
Iterator<Integer> it = map.keySet().iterator();
|
||||||
|
Integer remove1 = it.next();
|
||||||
|
it.remove();
|
||||||
|
Integer remove2 = it.next();
|
||||||
|
it.remove();
|
||||||
|
ArrayList<Integer> list = new ArrayList<>(Arrays.asList(1, 102, 203));
|
||||||
|
list.remove(remove1);
|
||||||
|
list.remove(remove2);
|
||||||
|
assertEquals("Wrong result", it.next(), list.get(0));
|
||||||
|
assertEquals("Wrong size", 1, map.size());
|
||||||
|
assertEquals("Wrong contents", map.keySet().iterator().next(), list.get(0));
|
||||||
|
|
||||||
|
Map<Integer, String> map2 = new HashMap<>(101);
|
||||||
|
map2.put(1, "1");
|
||||||
|
map2.put(4, "4");
|
||||||
|
Iterator<Integer> it2 = map2.keySet().iterator();
|
||||||
|
Integer remove3 = it2.next();
|
||||||
|
Integer next;
|
||||||
|
if (remove3 == 1) {
|
||||||
|
next = 4;
|
||||||
|
} else {
|
||||||
|
next = 1;
|
||||||
|
}
|
||||||
|
it2.hasNext();
|
||||||
|
it2.remove();
|
||||||
|
assertEquals("Wrong result 2", it2.next(), next);
|
||||||
|
assertEquals("Wrong size 2", 1, map2.size());
|
||||||
|
assertEquals("Wrong contents 2", map2.keySet().iterator().next(), next);
|
||||||
|
|
||||||
|
Iterator<String> enumeration = s.iterator();
|
||||||
|
assertTrue(enumeration.hasNext());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_keySet_subtest0() {
|
||||||
|
Set<String> s1 = ht10.keySet();
|
||||||
|
assertTrue("should contain key", s1.remove("Key 0"));
|
||||||
|
assertFalse("should not contain key", s1.remove("Key 0"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_keySet_subtest1() {
|
||||||
|
// this is the reference implementation behavior
|
||||||
|
final HashMap<String, String> ht = new HashMap<>(7);
|
||||||
|
ht.put("1", "a");
|
||||||
|
// these three elements hash to the same bucket in a 7 element HashMap
|
||||||
|
ht.put("2", "b");
|
||||||
|
ht.put("9", "c");
|
||||||
|
ht.put("12", "d");
|
||||||
|
// HashMap looks like:
|
||||||
|
// 0: "1"
|
||||||
|
// 1: "12" -> "9" -> "2"
|
||||||
|
Iterator<String> it = ht.keySet().iterator();
|
||||||
|
// this is mostly a copy of the test in test_elements_subtest0()
|
||||||
|
// test removing with the iterator does not null the values
|
||||||
|
while (it.hasNext()) {
|
||||||
|
String key = it.next();
|
||||||
|
if ("1".equals(key)) {
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it = ht.values().iterator();
|
||||||
|
boolean exception = false;
|
||||||
|
try {
|
||||||
|
// cached "12"
|
||||||
|
Set<String> iteratorElements = new HashSet<>();
|
||||||
|
iteratorElements.add(it.next());
|
||||||
|
iteratorElements.add(it.next());
|
||||||
|
iteratorElements.add(it.next());
|
||||||
|
assertTrue(iteratorElements.contains("b"));
|
||||||
|
assertTrue(iteratorElements.contains("c"));
|
||||||
|
assertTrue(iteratorElements.contains("d"));
|
||||||
|
} catch (NoSuchElementException e) {
|
||||||
|
exception = true;
|
||||||
|
}
|
||||||
|
assertFalse("unexpected NoSuchElementException", exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_putLjava_lang_ObjectLjava_lang_Object() {
|
||||||
|
// Test for method java.lang.Object
|
||||||
|
// java.util.HashMap.put(java.lang.Object, java.lang.Object)
|
||||||
|
HashMap<String, Integer> h = hashMapClone(ht100);
|
||||||
|
Integer key = 100;
|
||||||
|
h.put("Value 100", key);
|
||||||
|
assertTrue("Key/Value not inserted", h.size() == 1 && h.containsValue(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_putAllLjava_util_Map() {
|
||||||
|
// Test for method void java.util.HashMap.putAll(java.util.Map)
|
||||||
|
HashMap<String, String> h = new HashMap<>();
|
||||||
|
h.putAll(ht10);
|
||||||
|
for (String x : keyList) {
|
||||||
|
assertEquals("Failed to put all elements", h.get(x), ht10.get(x));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_removeLjava_lang_Object() {
|
||||||
|
// Test for method java.lang.Object
|
||||||
|
// java.util.HashMap.remove(java.lang.Object)
|
||||||
|
HashMap<String, String> h = hashMapClone(htfull);
|
||||||
|
Object k = h.remove("FKey 0");
|
||||||
|
assertTrue("Remove failed", !h.containsKey("FKey 0") || k == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_HashMap_selfReference_scenario1() {
|
||||||
|
HashMap<HashMap<?, ?>, Set<HashMap<?, ?>>> hashMap = new HashMap<>();
|
||||||
|
Set<HashMap<?, ?>> keySet = hashMap.keySet();
|
||||||
|
hashMap.put(hashMap, keySet);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_HashMap_selfReference_scenario2() {
|
||||||
|
HashMap<HashMap<?, ?>, HashMap<?, ?>> hashMap = new HashMap<>();
|
||||||
|
hashMap.keySet();
|
||||||
|
hashMap.put(hashMap, hashMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_size() {
|
||||||
|
// Test for method int java.util.HashMap.size()
|
||||||
|
assertTrue("Returned invalid size", ht10.size() == 10 && ht100.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_toString() {
|
||||||
|
// Test for method java.lang.String java.util.HashMap.toString()
|
||||||
|
HashMap<Serializable, Serializable> h = new HashMap<>();
|
||||||
|
assertEquals("Incorrect toString for Empty table", "{}", h.toString());
|
||||||
|
|
||||||
|
h.put("one", "1");
|
||||||
|
h.put("two", h);
|
||||||
|
String result = h.toString();
|
||||||
|
assertTrue("should contain self ref", result.contains("(this"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_values() {
|
||||||
|
// Test for method java.util.Collection java.util.HashMap.values()
|
||||||
|
Collection<String> c = ht10.values();
|
||||||
|
for (String s : elmList) {
|
||||||
|
assertTrue("Returned incorrect values", c.contains(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
HashMap<Integer, Integer> myHashMap = new HashMap<>();
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
myHashMap.put(i, i);
|
||||||
|
}
|
||||||
|
Collection<Integer> values = myHashMap.values();
|
||||||
|
new UnmodifiableCollectionTestSupport(values).runTest();
|
||||||
|
values.remove(0);
|
||||||
|
assertFalse("Removing from the values collection should remove from the original map",
|
||||||
|
myHashMap.containsValue(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_entrySet_remove() {
|
||||||
|
HashMap<String, String> hashMap = new HashMap<>();
|
||||||
|
hashMap.put("my.nonexistent.prop", "AAA");
|
||||||
|
hashMap.put("parse.error", "BBB");
|
||||||
|
Iterator<Map.Entry<String, String>> iterator = hashMap.entrySet().iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
Map.Entry<String, String> entry = iterator.next();
|
||||||
|
final Object value = entry.getValue();
|
||||||
|
if (value.equals("AAA")) {
|
||||||
|
iterator.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertFalse(hashMap.containsKey("my.nonexistent.prop"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_keys_elements_keySet_Exceptions() {
|
||||||
|
HashMap<String, String> hashMap = new HashMap<>();
|
||||||
|
String key = "key";
|
||||||
|
String value = "value";
|
||||||
|
hashMap.put(key, value);
|
||||||
|
|
||||||
|
Iterator<String> iterator = hashMap.keySet().iterator();
|
||||||
|
assertTrue(iterator.hasNext());
|
||||||
|
try {
|
||||||
|
iterator.remove();
|
||||||
|
fail("should throw IllegalStateException");
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
iterator.next();
|
||||||
|
iterator.remove();
|
||||||
|
assertFalse(iterator.hasNext());
|
||||||
|
assertTrue(hashMap.isEmpty());
|
||||||
|
|
||||||
|
hashMap.put(key, value);
|
||||||
|
iterator = hashMap.values().iterator();
|
||||||
|
assertTrue(iterator.hasNext());
|
||||||
|
try {
|
||||||
|
iterator.remove();
|
||||||
|
fail("should throw IllegalStateException");
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
iterator.next();
|
||||||
|
iterator.remove();
|
||||||
|
assertFalse(iterator.hasNext());
|
||||||
|
assertTrue(hashMap.isEmpty());
|
||||||
|
|
||||||
|
hashMap.put(key, value);
|
||||||
|
iterator = hashMap.keySet().iterator();
|
||||||
|
assertTrue(iterator.hasNext());
|
||||||
|
try {
|
||||||
|
iterator.remove();
|
||||||
|
fail("should throw IllegalStateException");
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
iterator.next();
|
||||||
|
iterator.remove();
|
||||||
|
assertFalse(iterator.hasNext());
|
||||||
|
|
||||||
|
hashMap.clear();
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
hashMap.put(key + i, value + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator<String> enumeration = hashMap.keySet().iterator();
|
||||||
|
iterator = enumeration;
|
||||||
|
assertTrue(enumeration.hasNext());
|
||||||
|
assertTrue(iterator.hasNext());
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
if (i % 2 == 0) {
|
||||||
|
enumeration.next();
|
||||||
|
} else {
|
||||||
|
iterator.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertFalse(enumeration.hasNext());
|
||||||
|
assertFalse(iterator.hasNext());
|
||||||
|
try {
|
||||||
|
enumeration.next();
|
||||||
|
fail("should throw NoSuchElementException");
|
||||||
|
} catch (NoSuchElementException e) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
iterator.next();
|
||||||
|
fail("should throw NoSuchElementException");
|
||||||
|
} catch (NoSuchElementException e) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
|
||||||
|
// cast Enumeration to Iterator
|
||||||
|
enumeration = hashMap.values().iterator();
|
||||||
|
iterator = enumeration;
|
||||||
|
assertTrue(enumeration.hasNext());
|
||||||
|
assertTrue(iterator.hasNext());
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
if (i % 2 == 0) {
|
||||||
|
enumeration.next();
|
||||||
|
} else {
|
||||||
|
iterator.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertFalse(enumeration.hasNext());
|
||||||
|
assertFalse(iterator.hasNext());
|
||||||
|
try {
|
||||||
|
enumeration.next();
|
||||||
|
fail("should throw NoSuchElementException");
|
||||||
|
} catch (NoSuchElementException e) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
iterator.next();
|
||||||
|
fail("should throw NoSuchElementException");
|
||||||
|
} catch (NoSuchElementException e) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
|
||||||
|
// cast Iterator to Enumeration
|
||||||
|
enumeration = hashMap.keySet().iterator();
|
||||||
|
iterator = (Iterator<String>) enumeration;
|
||||||
|
assertTrue(enumeration.hasNext());
|
||||||
|
assertTrue(iterator.hasNext());
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
if (i % 2 == 0) {
|
||||||
|
enumeration.next();
|
||||||
|
} else {
|
||||||
|
iterator.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertFalse(enumeration.hasNext());
|
||||||
|
assertFalse(iterator.hasNext());
|
||||||
|
try {
|
||||||
|
enumeration.next();
|
||||||
|
fail("should throw NoSuchElementException");
|
||||||
|
} catch (NoSuchElementException e) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
iterator.next();
|
||||||
|
fail("should throw NoSuchElementException");
|
||||||
|
} catch (NoSuchElementException e) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_computeUpdatesValueIfPresent() {
|
||||||
|
HashMap<String, String> ht10 = new HashMap<>();
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
ht10.put("Key" + i, "Val" + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
String newVal = ht10.compute("Key5", (k, v) -> "changed");
|
||||||
|
assertEquals("changed", newVal);
|
||||||
|
assertEquals(10, ht10.size());
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
if (i == 5) {
|
||||||
|
assertEquals("Value was incorrectly changed", "changed", ht10.get("Key" + i));
|
||||||
|
} else {
|
||||||
|
assertEquals("Value was unexpectedly changed", "Val" + i, ht10.get("Key" + i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_computePutsNewEntryIfKeyIsAbsent() {
|
||||||
|
HashMap<String, String> ht10 = new HashMap<>();
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
ht10.put("Key" + i, "Val" + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
String newVal = ht10.compute("absent key", (k, v) -> "added");
|
||||||
|
assertEquals("added", newVal);
|
||||||
|
assertEquals(11, ht10.size());
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
assertEquals("Value was unexpectedly changed", "Val" + i, ht10.get("Key" + i));
|
||||||
|
}
|
||||||
|
assertEquals("New value expected", "added", ht10.get("absent key"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_computeRemovesEntryWhenNullProduced() {
|
||||||
|
HashMap<String, String> ht10 = new HashMap<>();
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
ht10.put("Key" + i, "Val" + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
String newVal = ht10.compute("Key5", (k, v) -> null);
|
||||||
|
assertNull(newVal);
|
||||||
|
assertEquals(9, ht10.size());
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
if (i == 5) {
|
||||||
|
assertNull("Value was unexpectedly present in map", ht10.get("Key" + i));
|
||||||
|
} else {
|
||||||
|
assertEquals("Value was unexpectedly changed", "Val" + i, ht10.get("Key" + i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_computeIfAbsentNominal() {
|
||||||
|
HashMap<String, String> ht10 = new HashMap<>();
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
ht10.put("Key" + i, "Val" + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
String newVal = ht10.computeIfAbsent("absent key", k -> "added");
|
||||||
|
assertEquals("added", newVal);
|
||||||
|
assertEquals(11, ht10.size());
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
assertEquals("Value was unexpectedly changed", "Val" + i, ht10.get("Key" + i));
|
||||||
|
}
|
||||||
|
assertEquals("New value expected", "added", ht10.get("absent key"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_computeIfAbsentIgnoresExistingEntry() {
|
||||||
|
HashMap<String, String> ht10 = new HashMap<>();
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
ht10.put("Key" + i, "Val" + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
String newVal = ht10.computeIfAbsent("Key5", v -> "changed");
|
||||||
|
assertEquals("Val5", newVal);
|
||||||
|
assertEquals(10, ht10.size());
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
assertEquals("Value was unexpectedly changed", "Val" + i, ht10.get("Key" + i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_computeIfAbsentDoesNothingIfNullProduced() {
|
||||||
|
HashMap<String, String> ht10 = new HashMap<>();
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
ht10.put("Key" + i, "Val" + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
String newVal = ht10.computeIfAbsent("absent key", v -> null);
|
||||||
|
assertNull(newVal);
|
||||||
|
assertEquals(10, ht10.size());
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
assertEquals("Value was unexpectedly changed", "Val" + i, ht10.get("Key" + i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_computeIfPresentNominal() {
|
||||||
|
HashMap<String, String> ht10 = new HashMap<>();
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
ht10.put("Key" + i, "Val" + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
String newVal = ht10.computeIfPresent("Key5", (k, v) -> "changed");
|
||||||
|
assertEquals("changed", newVal);
|
||||||
|
assertEquals(10, ht10.size());
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
if (i == 5) {
|
||||||
|
assertEquals("Value was incorrectly updated", "changed", ht10.get("Key" + i));
|
||||||
|
} else {
|
||||||
|
assertEquals("Value was unexpectedly changed", "Val" + i, ht10.get("Key" + i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_computeIfPresentIgnoresAbsentKeys() {
|
||||||
|
HashMap<String, String> ht10 = new HashMap<>();
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
ht10.put("Key" + i, "Val" + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
String newVal = ht10.computeIfPresent("absent key", (k, v) -> "added");
|
||||||
|
assertNull(newVal);
|
||||||
|
assertEquals(10, ht10.size());
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
assertEquals("Value was unexpectedly changed", "Val" + i, ht10.get("Key" + i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_computeIfPresentRemovesEntryWhenNullProduced() {
|
||||||
|
HashMap<String, String> ht10 = new HashMap<>();
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
ht10.put("Key" + i, "Val" + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
String newVal = ht10.computeIfPresent("Key5", (k, v) -> null);
|
||||||
|
assertNull(newVal);
|
||||||
|
assertEquals(9, ht10.size());
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
if (i == 5) {
|
||||||
|
assertNull("Value unexpectedly present", ht10.get("Key" + i));
|
||||||
|
} else {
|
||||||
|
assertEquals("Value was unexpectedly changed", "Val" + i, ht10.get("Key" + i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_mergeKeyAbsentCase() {
|
||||||
|
HashMap<String, String> ht10 = new HashMap<>();
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
ht10.put("Key" + i, "Val" + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
String newVal = ht10.merge("absent key", "changed", (k, v) -> "remapped");
|
||||||
|
assertEquals("changed", newVal);
|
||||||
|
assertEquals(11, ht10.size());
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
assertEquals("Value was unexpectedly changed", "Val" + i, ht10.get("Key" + i));
|
||||||
|
}
|
||||||
|
assertEquals("New value expected", "changed", ht10.get("absent key"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_mergeKeyPresentCase() {
|
||||||
|
HashMap<String, String> ht10 = new HashMap<>();
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
ht10.put("Key" + i, "Val" + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
String newVal = ht10.merge("Key5", "changed", (k, v) -> "remapped");
|
||||||
|
assertEquals("remapped", newVal);
|
||||||
|
assertEquals(10, ht10.size());
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
if (i == 5) {
|
||||||
|
assertEquals("Value was incorrectly updated", "remapped", ht10.get("Key" + i));
|
||||||
|
} else {
|
||||||
|
assertEquals("Value was unexpectedly changed", "Val" + i, ht10.get("Key" + i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_mergeKeyAbsentCase_remapToNull() {
|
||||||
|
HashMap<String, String> ht10 = new HashMap<>();
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
ht10.put("Key" + i, "Val" + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
String newVal = ht10.merge("absent key", "changed", (k, v) -> null);
|
||||||
|
assertEquals("changed", newVal);
|
||||||
|
assertEquals(11, ht10.size());
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
assertEquals("Value was unexpectedly changed", "Val" + i, ht10.get("Key" + i));
|
||||||
|
}
|
||||||
|
assertEquals("New value expected", "changed", ht10.get("absent key"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_mergeKeyPresentCase_remapToNull() {
|
||||||
|
HashMap<String, String> ht10 = new HashMap<>();
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
ht10.put("Key" + i, "Val" + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
String newVal = ht10.merge("Key5", "changed", (k, v) -> null);
|
||||||
|
assertNull(newVal);
|
||||||
|
assertEquals(9, ht10.size());
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
if (i == 5) {
|
||||||
|
assertNull("Null value expected", ht10.get("Key" + i));
|
||||||
|
} else {
|
||||||
|
assertEquals("Value was unexpectedly changed", "Val" + i, ht10.get("Key" + i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_replaceNominal() {
|
||||||
|
HashMap<String, String> ht10 = new HashMap<>();
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
ht10.put("Key" + i, "Val" + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
String newVal = ht10.replace("Key5", "changed");
|
||||||
|
assertEquals("Val5", newVal);
|
||||||
|
assertEquals(10, ht10.size());
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
if (i == 5) {
|
||||||
|
assertEquals("Value was incorrectly updated", "changed", ht10.get("Key" + i));
|
||||||
|
} else {
|
||||||
|
assertEquals("Value was unexpectedly changed", "Val" + i, ht10.get("Key" + i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_replaceAbsentKey() {
|
||||||
|
HashMap<String, String> ht10 = new HashMap<>();
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
ht10.put("Key" + i, "Val" + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
String newVal = ht10.replace("absent key", "changed");
|
||||||
|
assertNull(newVal);
|
||||||
|
assertEquals(10, ht10.size());
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
assertEquals("Value was unexpectedly changed", "Val" + i, ht10.get("Key" + i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_replace2Nominal() {
|
||||||
|
HashMap<String, String> ht10 = new HashMap<>();
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
ht10.put("Key" + i, "Val" + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean replaced = ht10.replace("Key5", "Val5", "changed");
|
||||||
|
assertTrue(replaced);
|
||||||
|
assertEquals(10, ht10.size());
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
if (i == 5) {
|
||||||
|
assertEquals("Value was incorrectly updated", "changed", ht10.get("Key" + i));
|
||||||
|
} else {
|
||||||
|
assertEquals("Value was unexpectedly changed", "Val" + i, ht10.get("Key" + i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_replace2WithIncorrectExpectation() {
|
||||||
|
HashMap<String, String> ht10 = new HashMap<>();
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
ht10.put("Key" + i, "Val" + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean replaced = ht10.replace("Key5", "incorrect value", "changed");
|
||||||
|
assertFalse(replaced);
|
||||||
|
assertEquals(10, ht10.size());
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
assertEquals("Value was unexpectedly changed", "Val" + i, ht10.get("Key" + i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected <K, V> HashMap<K, V> hashMapClone(HashMap<K, V> s) {
|
||||||
|
return (HashMap<K, V>) s.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ReusableKey {
|
||||||
|
private int key;
|
||||||
|
|
||||||
|
public void setKey(int key) {
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (o == this) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(o instanceof ReusableKey)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return key == ((ReusableKey) o).key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,6 +32,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.assertNotEquals;
|
import static org.junit.Assert.assertNotEquals;
|
||||||
|
@ -43,9 +44,16 @@ import static org.junit.Assert.fail;
|
||||||
import java.util.AbstractMap;
|
import java.util.AbstractMap;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
import java.util.SequencedMap;
|
||||||
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.classlib.support.MapTest2Support;
|
import org.teavm.classlib.support.MapTest2Support;
|
||||||
|
@ -529,6 +537,137 @@ public class LinkedHashMapTest {
|
||||||
lhm.put("B", "C");
|
lhm.put("B", "C");
|
||||||
|
|
||||||
assertEquals("{A=(this Map), B=C}", lhm.toString());
|
assertEquals("{A=(this Map), B=C}", lhm.toString());
|
||||||
|
assertEquals("{B=C, A={A=(this Map), B=C}}", lhm.reversed().toString());
|
||||||
assertEquals("{}", new LinkedHashMap<>().toString());
|
assertEquals("{}", new LinkedHashMap<>().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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, LinkedHashMap::new));
|
||||||
|
}
|
||||||
|
|
||||||
|
private SequencedMap<Integer, String> generateAccessOrderMap() {
|
||||||
|
return BASE_LIST.stream().collect(Collectors.toMap(Function.identity(), i -> Integer.toString(i),
|
||||||
|
(a, b) -> a, () -> new LinkedHashMap<>(16, 0.75f, true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSequencedMap() {
|
||||||
|
SequencedMap<Integer, String> map = generateMap();
|
||||||
|
assertEquals(Map.entry(1, "1"), map.pollFirstEntry());
|
||||||
|
assertArrayEquals(new Integer[] { 6, 2, 5, 3, 4 }, map.keySet().toArray(new Integer[0]));
|
||||||
|
assertEquals(Map.entry(4, "4"), map.pollLastEntry());
|
||||||
|
assertArrayEquals(new Integer[] { 6, 2, 5, 3 }, map.keySet().toArray(new Integer[0]));
|
||||||
|
assertEquals(Map.entry(3, "3"), map.pollLastEntry());
|
||||||
|
assertArrayEquals(new Integer[] { 6, 2, 5 }, map.keySet().toArray(new Integer[0]));
|
||||||
|
assertEquals(Map.entry(6, "6"), map.firstEntry());
|
||||||
|
assertEquals(Map.entry(5, "5"), map.lastEntry());
|
||||||
|
map.putFirst(1, "1");
|
||||||
|
map.put(7, "7");
|
||||||
|
map.putLast(3, "3");
|
||||||
|
assertArrayEquals(new Integer[] { 1, 6, 2, 5, 7, 3 }, map.keySet().toArray(new Integer[0]));
|
||||||
|
map = generateMap().reversed();
|
||||||
|
assertEquals(Map.entry(4, "4"), map.pollFirstEntry());
|
||||||
|
assertArrayEquals(new Integer[] { 3, 5, 2, 6, 1 }, map.keySet().toArray(new Integer[0]));
|
||||||
|
assertEquals(Map.entry(1, "1"), map.pollLastEntry());
|
||||||
|
assertArrayEquals(new Integer[] { 3, 5, 2, 6 }, map.keySet().toArray(new Integer[0]));
|
||||||
|
assertEquals(Map.entry(6, "6"), map.pollLastEntry());
|
||||||
|
assertArrayEquals(new Integer[] { 3, 5, 2 }, map.keySet().toArray(new Integer[0]));
|
||||||
|
assertEquals(Map.entry(3, "3"), map.firstEntry());
|
||||||
|
assertEquals(Map.entry(2, "2"), map.lastEntry());
|
||||||
|
map.putFirst(1, "1");
|
||||||
|
map.put(7, "7");
|
||||||
|
map.putLast(6, "6");
|
||||||
|
assertArrayEquals(new Integer[] { 7, 1, 3, 5, 2, 6 }, map.keySet().toArray(new Integer[0]));
|
||||||
|
map = generateAccessOrderMap();
|
||||||
|
map.putFirst(3, "3");
|
||||||
|
map.put(5, "5");
|
||||||
|
map.putLast(2, "2");
|
||||||
|
assertArrayEquals(new Integer[] { 3, 1, 6, 4, 5, 2 }, map.keySet().toArray(new Integer[0]));
|
||||||
|
assertArrayEquals(new Integer[] { 2, 5, 4, 6, 1, 3 }, map.reversed().keySet().toArray(new Integer[0]));
|
||||||
|
map = generateAccessOrderMap();
|
||||||
|
map.putFirst(1, "1");
|
||||||
|
assertArrayEquals(new Integer[] { 1, 6, 2, 5, 3, 4 }, map.keySet().toArray(new Integer[0]));
|
||||||
|
map.put(1, "1");
|
||||||
|
assertArrayEquals(new Integer[] { 6, 2, 5, 3, 4, 1 }, map.keySet().toArray(new Integer[0]));
|
||||||
|
map.putLast(6, "6");
|
||||||
|
assertArrayEquals(new Integer[] { 2, 5, 3, 4, 1, 6 }, map.keySet().toArray(new Integer[0]));
|
||||||
|
map.putFirst(6, "6");
|
||||||
|
assertArrayEquals(new Integer[] { 6, 2, 5, 3, 4, 1 }, map.keySet().toArray(new Integer[0]));
|
||||||
|
assertArrayEquals(new Integer[] { 1, 4, 3, 5, 2, 6 }, map.reversed().keySet().toArray(new Integer[0]));
|
||||||
|
map = generateAccessOrderMap().reversed();
|
||||||
|
assertArrayEquals(new Integer[] { 4, 3, 5, 2, 6, 1 }, map.keySet().toArray(new Integer[0]));
|
||||||
|
map.putFirst(1, "1");
|
||||||
|
assertArrayEquals(new Integer[] { 1, 4, 3, 5, 2, 6 }, map.keySet().toArray(new Integer[0]));
|
||||||
|
map.put(1, "1");
|
||||||
|
assertArrayEquals(new Integer[] { 1, 4, 3, 5, 2, 6 }, map.keySet().toArray(new Integer[0]));
|
||||||
|
map.putLast(1, "1");
|
||||||
|
assertArrayEquals(new Integer[] { 4, 3, 5, 2, 6, 1 }, map.keySet().toArray(new Integer[0]));
|
||||||
|
assertArrayEquals(new Integer[] { 1, 6, 2, 5, 3, 4 }, map.reversed().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(6, it.next().intValue());
|
||||||
|
it.remove();
|
||||||
|
assertArrayEquals(new Integer[] { 1, 2, 5, 3, 4 }, map.keySet().toArray(new Integer[0]));
|
||||||
|
map = map.reversed();
|
||||||
|
it = map.keySet().iterator();
|
||||||
|
assertTrue(it.hasNext());
|
||||||
|
assertEquals(4, it.next().intValue());
|
||||||
|
assertTrue(it.hasNext());
|
||||||
|
assertEquals(3, it.next().intValue());
|
||||||
|
it.remove();
|
||||||
|
assertArrayEquals(new Integer[] { 4, 5, 2, 1 }, map.keySet().toArray(new Integer[0]));
|
||||||
|
map = generateMap();
|
||||||
|
Iterator<String> sit = map.values().iterator();
|
||||||
|
assertTrue(sit.hasNext());
|
||||||
|
assertEquals("1", sit.next());
|
||||||
|
assertTrue(sit.hasNext());
|
||||||
|
assertEquals("6", sit.next());
|
||||||
|
sit.remove();
|
||||||
|
assertArrayEquals(new String[] { "1", "2", "5", "3", "4" }, map.values().toArray(new String[0]));
|
||||||
|
map = map.reversed();
|
||||||
|
sit = map.values().iterator();
|
||||||
|
assertTrue(sit.hasNext());
|
||||||
|
assertEquals("4", sit.next());
|
||||||
|
assertTrue(sit.hasNext());
|
||||||
|
assertEquals("3", sit.next());
|
||||||
|
sit.remove();
|
||||||
|
assertArrayEquals(new String[] { "4", "5", "2", "1" }, map.values().toArray(new String[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEmpty() {
|
||||||
|
var empty = new LinkedHashMap<>();
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user