Adds java.util.HashMap

This commit is contained in:
konsoletyper 2014-02-23 18:53:43 +04:00
parent 03d5554e76
commit 45dc8aa0ec
4 changed files with 749 additions and 2 deletions

View File

@ -128,8 +128,8 @@ public abstract class TAbstractMap<K, V> extends TObject implements TMap<K, V> {
}
}
private KeySet cachedKeySet;
private Values cachedValues;
TSet<K> cachedKeySet;
TCollection<V> cachedValues;
protected TAbstractMap() {
}

View File

@ -93,6 +93,46 @@ public class TCollections extends TObject {
};
}
public static <T> TSet<T> singleton(final T o) {
return new TAbstractSet<T>() {
@Override public int size() {
return 1;
}
@Override public TIterator<T> iterator() {
return new TIterator<T>() {
private boolean read;
@Override public boolean hasNext() {
return !read;
}
@Override public T next() {
if (read) {
throw new TNoSuchElementException();
}
read = true;
return o;
}
@Override public void remove() {
throw new TUnsupportedOperationException();
}
};
}
@Override public boolean contains(Object o2) {
return TObjects.equals(o, o2);
}
};
}
public static <T> TList<T> unmodifiableList(final TList<? extends T> list) {
return new TAbstractList<T>() {
@Override public T get(int index) {
return list.get(index);
}
@Override public int size() {
return list.size();
}
};
}
public static <T> TList<T> nCopies(final int n, final T o) {
return new TAbstractList<T>() {
@Override public T get(int index) {

View File

@ -0,0 +1,602 @@
/*
* 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;
import java.util.*;
import org.teavm.classlib.java.io.TSerializable;
import org.teavm.classlib.java.lang.TCloneNotSupportedException;
import org.teavm.classlib.java.lang.TIllegalArgumentException;
import org.teavm.classlib.java.lang.TIllegalStateException;
import org.teavm.classlib.java.lang.TObject;
public class THashMap<K, V> extends TAbstractMap<K, V> implements TSerializable {
transient int elementCount;
transient Entry<K, V>[] elementData;
transient int modCount = 0;
private static final int DEFAULT_SIZE = 16;
final float loadFactor;
int threshold;
static class Entry<K, V> extends TMapEntry<K, V> {
final int origKeyHash;
Entry<K, V> next;
Entry(K theKey, int hash) {
super(theKey, null);
this.origKeyHash = hash;
}
Entry(K theKey, V theValue) {
super(theKey, theValue);
origKeyHash = (theKey == null ? 0 : computeHashCode(theKey));
}
@Override
@SuppressWarnings("unchecked")
public Object clone() {
Entry<K, V> entry = (Entry<K, V>) super.clone();
if (next != null) {
entry.next = (Entry<K, V>) next.clone();
}
return entry;
}
}
private static class AbstractMapIterator<K, V> {
private int position = 0;
int expectedModCount;
Entry<K, V> futureEntry;
Entry<K, V> currentEntry;
Entry<K, V> prevEntry;
final THashMap<K, V> associatedMap;
AbstractMapIterator(THashMap<K, V> hm) {
associatedMap = hm;
expectedModCount = hm.modCount;
futureEntry = null;
}
public boolean hasNext() {
if (futureEntry != null) {
return true;
}
while (position < associatedMap.elementData.length) {
if (associatedMap.elementData[position] == null) {
position++;
} else {
return true;
}
}
return false;
}
final void checkConcurrentMod() throws ConcurrentModificationException {
if (expectedModCount != associatedMap.modCount) {
throw new TConcurrentModificationException();
}
}
final void makeNext() {
checkConcurrentMod();
if (!hasNext()) {
throw new TNoSuchElementException();
}
if (futureEntry == null) {
currentEntry = associatedMap.elementData[position++];
futureEntry = currentEntry.next;
prevEntry = null;
} else {
if(currentEntry!=null){
prevEntry = currentEntry;
}
currentEntry = futureEntry;
futureEntry = futureEntry.next;
}
}
public final void remove() {
checkConcurrentMod();
if (currentEntry == null) {
throw new TIllegalStateException();
}
if(prevEntry == null){
int index = currentEntry.origKeyHash & (associatedMap.elementData.length - 1);
associatedMap.elementData[index] = associatedMap.elementData[index].next;
} else {
prevEntry.next = currentEntry.next;
}
currentEntry = null;
expectedModCount++;
associatedMap.modCount++;
associatedMap.elementCount--;
}
}
private static class EntryIterator <K, V> extends AbstractMapIterator<K, V>
implements TIterator<TMap.Entry<K, V>> {
EntryIterator (THashMap<K, V> map) {
super(map);
}
@Override
public TMap.Entry<K, V> next() {
makeNext();
return currentEntry;
}
}
private static class KeyIterator <K, V> extends AbstractMapIterator<K, V> implements TIterator<K> {
KeyIterator (THashMap<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 (THashMap<K, V> map) {
super(map);
}
@Override
public V next() {
makeNext();
return currentEntry.value;
}
}
static class HashMapEntrySet<KT, VT> extends TAbstractSet<TMap.Entry<KT, VT>> {
private final THashMap<KT, VT> associatedMap;
public HashMapEntrySet(THashMap<KT, VT> hm) {
associatedMap = hm;
}
THashMap<KT, VT> hashMap() {
return associatedMap;
}
@Override
public int size() {
return associatedMap.elementCount;
}
@Override
public void clear() {
associatedMap.clear();
}
@Override
public boolean remove(Object object) {
if (object instanceof TMap.Entry) {
TMap.Entry<?, ?> oEntry = (TMap.Entry<?, ?>) object;
TMap.Entry<KT,VT> entry = associatedMap.getEntry(oEntry.getKey());
if(valuesEq(entry, oEntry)) {
associatedMap.removeEntry(entry);
return true;
}
}
return false;
}
@Override
public boolean contains(Object object) {
if (object instanceof TMap.Entry) {
TMap.Entry<?, ?> oEntry = (TMap.Entry<?, ?>) object;
TMap.Entry<KT, VT> entry = associatedMap.getEntry(oEntry.getKey());
return valuesEq(entry, oEntry);
}
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
public TIterator<TMap.Entry<KT, VT>> iterator() {
return new EntryIterator<>(associatedMap);
}
}
@SuppressWarnings("unchecked")
Entry<K, V>[] newElementArray(int s) {
return new Entry[s];
}
public THashMap() {
this(DEFAULT_SIZE);
}
public THashMap(int capacity) {
this(capacity, 0.75f); // default load factor of 0.75
}
private static final int calculateCapacity(int x) {
if(x >= 1 << 30){
return 1 << 30;
}
if(x == 0){
return 16;
}
x = x -1;
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
x |= x >> 8;
x |= x >> 16;
return x + 1;
}
/**
* Constructs a new {@code HashMap} instance with the specified capacity and
* load factor.
*
* @param capacity
* the initial capacity of this hash map.
* @param loadFactor
* the initial load factor.
* @throws IllegalArgumentException
* when the capacity is less than zero or the load factor is
* less or equal to zero.
*/
public THashMap(int capacity, float loadFactor) {
if (capacity >= 0 && loadFactor > 0) {
capacity = calculateCapacity(capacity);
elementCount = 0;
elementData = newElementArray(capacity);
this.loadFactor = loadFactor;
computeThreshold();
} else {
throw new TIllegalArgumentException();
}
}
public THashMap(TMap<? extends K, ? extends V> map) {
this(calculateCapacity(map.size()));
putAllImpl(map);
}
@Override
public void clear() {
if (elementCount > 0) {
elementCount = 0;
Arrays.fill(elementData, null);
modCount++;
}
}
@Override
@SuppressWarnings("unchecked")
public TObject clone() {
try {
THashMap<K, V> map = (THashMap<K, V>) super.clone();
map.elementCount = 0;
map.elementData = newElementArray(elementData.length);
map.putAll(this);
return map;
} catch (TCloneNotSupportedException e) {
return null;
}
}
private void computeThreshold() {
threshold = (int) (elementData.length * loadFactor);
}
@Override
public boolean containsKey(Object key) {
Entry<K, V> m = getEntry(key);
return m != null;
}
@Override
public boolean containsValue(Object value) {
if (value != null) {
for (int i = 0; i < elementData.length; i++) {
Entry<K, V> entry = elementData[i];
while (entry != null) {
if (areEqualValues(value, entry.value)) {
return true;
}
entry = entry.next;
}
}
} else {
for (int i = 0; i < elementData.length; i++) {
Entry<K, V> entry = elementData[i];
while (entry != null) {
if (entry.value == null) {
return true;
}
entry = entry.next;
}
}
}
return false;
}
@Override
public TSet<TMap.Entry<K, V>> entrySet() {
return new HashMapEntrySet<>(this);
}
@Override
public V get(Object key) {
Entry<K, V> m = getEntry(key);
if (m != null) {
return m.value;
}
return null;
}
final Entry<K, V> getEntry(Object key) {
Entry<K, V> m;
if (key == null) {
m = findNullKeyEntry();
} else {
int hash = computeHashCode(key);
int index = hash & (elementData.length - 1);
m = findNonNullKeyEntry(key, index, hash);
}
return m;
}
final Entry<K,V> findNonNullKeyEntry(Object key, int index, int keyHash) {
Entry<K,V> m = elementData[index];
while (m != null
&& (m.origKeyHash != keyHash || !areEqualKeys(key, m.key))) {
m = m.next;
}
return m;
}
final Entry<K,V> findNullKeyEntry() {
Entry<K,V> m = elementData[0];
while (m != null && m.key != null)
m = m.next;
return m;
}
@Override
public boolean isEmpty() {
return elementCount == 0;
}
@Override
public TSet<K> keySet() {
if (cachedKeySet == null) {
cachedKeySet = new TAbstractSet<K>() {
@Override public boolean contains(Object object) {
return containsKey(object);
}
@Override public int size() {
return THashMap.this.size();
}
@Override public void clear() {
THashMap.this.clear();
}
@Override public boolean remove(Object key) {
Entry<K, V> entry = THashMap.this.removeEntry(key);
return entry != null;
}
@Override public TIterator<K> iterator() {
return new KeyIterator<>(THashMap.this);
}
};
}
return cachedKeySet;
}
@Override
public V put(K key, V value) {
return putImpl(key, value);
}
V putImpl(K key, V value) {
Entry<K,V> entry;
if(key == null) {
entry = findNullKeyEntry();
if (entry == null) {
modCount++;
entry = createHashedEntry(null, 0, 0);
if (++elementCount > threshold) {
rehash();
}
}
} else {
int hash = computeHashCode(key);
int index = hash & (elementData.length - 1);
entry = findNonNullKeyEntry(key, index, hash);
if (entry == null) {
modCount++;
entry = createHashedEntry(key, index, hash);
if (++elementCount > threshold) {
rehash();
}
}
}
V result = entry.value;
entry.value = value;
return result;
}
Entry<K, V> createEntry(K key, int index, V value) {
Entry<K, V> entry = new Entry<>(key, value);
entry.next = elementData[index];
elementData[index] = entry;
return entry;
}
Entry<K,V> createHashedEntry(K key, int index, int hash) {
Entry<K,V> entry = new Entry<>(key,hash);
entry.next = elementData[index];
elementData[index] = entry;
return entry;
}
@Override
public void putAll(TMap<? extends K, ? extends V> map) {
if (!map.isEmpty()) {
putAllImpl(map);
}
}
private void putAllImpl(TMap<? extends K, ? extends V> map) {
int capacity = elementCount + map.size();
if (capacity > threshold) {
rehash(capacity);
}
for (TIterator<? extends TMap.Entry<? extends K, ? extends V>> iter = map.entrySet().iterator();
iter.hasNext();) {
TMap.Entry<? extends K, ? extends V> entry = iter.next();
putImpl(entry.getKey(), entry.getValue());
}
}
void rehash(int capacity) {
int length = calculateCapacity((capacity == 0 ? 1 : capacity << 1));
Entry<K, V>[] newData = newElementArray(length);
for (int i = 0; i < elementData.length; i++) {
Entry<K, V> entry = elementData[i];
elementData[i] = null;
while (entry != null) {
int index = entry.origKeyHash & (length - 1);
Entry<K, V> next = entry.next;
entry.next = newData[index];
newData[index] = entry;
entry = next;
}
}
elementData = newData;
computeThreshold();
}
void rehash() {
rehash(elementData.length);
}
@Override
public V remove(Object key) {
Entry<K, V> entry = removeEntry(key);
if (entry != null) {
return entry.value;
}
return null;
}
final void removeEntry(Entry<K, V> entry) {
int index = entry.origKeyHash & (elementData.length - 1);
Entry<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 Entry<K, V> removeEntry(Object key) {
int index = 0;
Entry<K, V> entry;
Entry<K, V> last = null;
if (key != null) {
int hash = computeHashCode(key);
index = hash & (elementData.length - 1);
entry = elementData[index];
while (entry != null && !(entry.origKeyHash == hash && areEqualKeys(key, entry.key))) {
last = entry;
entry = entry.next;
}
} else {
entry = elementData[0];
while (entry != null && entry.key != null) {
last = entry;
entry = entry.next;
}
}
if (entry == null) {
return null;
}
if (last == null) {
elementData[index] = entry.next;
} else {
last.next = entry.next;
}
modCount++;
elementCount--;
return entry;
}
@Override
public int size() {
return elementCount;
}
@Override
public TCollection<V> values() {
if (cachedValues == null) {
cachedValues = new TAbstractCollection<V>() {
@Override public boolean contains(Object object) {
return containsValue(object);
}
@Override public int size() {
return THashMap.this.size();
}
@Override public void clear() {
THashMap.this.clear();
}
@Override public TIterator<V> iterator() {
return new ValueIterator<>(THashMap.this);
}
};
}
return cachedValues;
}
static int computeHashCode(Object key) {
return key.hashCode();
}
static boolean areEqualKeys(Object key1, Object key2) {
return (key1 == key2) || key1.equals(key2);
}
static boolean areEqualValues(Object value1, Object value2) {
return (value1 == value2) || value1.equals(value2);
}
}

View File

@ -0,0 +1,105 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* 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;
/**
* MapEntry is an internal class which provides an implementation of Map.Entry.
*/
class TMapEntry<K, V> implements TMap.Entry<K, V>, Cloneable {
K key;
V value;
interface Type<RT, KT, VT> {
RT get(TMapEntry<KT, VT> entry);
}
TMapEntry(K theKey) {
key = theKey;
}
TMapEntry(K theKey, V theValue) {
key = theKey;
value = theValue;
}
@Override
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
return null;
}
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object instanceof TMap.Entry) {
TMap.Entry<?, ?> entry = (TMap.Entry<?, ?>) object;
return (key == null ? entry.getKey() == null : key.equals(entry.getKey()))
&& (value == null ? entry.getValue() == null : value
.equals(entry.getValue()));
}
return false;
}
@Override
public K getKey() {
return key;
}
@Override
public V getValue() {
return value;
}
@Override
public int hashCode() {
return (key == null ? 0 : key.hashCode())
^ (value == null ? 0 : value.hashCode());
}
@Override
public V setValue(V object) {
V result = value;
value = object;
return result;
}
@Override
public String toString() {
return key + "=" + value;
}
}