java.util.AbstractHashMap Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2008 Google Inc.
*
* 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 java.util;
import static javaemul.internal.InternalPreconditions.checkArgument;
import static javaemul.internal.InternalPreconditions.checkConcurrentModification;
import static javaemul.internal.InternalPreconditions.checkElement;
import static javaemul.internal.InternalPreconditions.checkState;
import static javaemul.internal.InternalPreconditions.isApiChecked;
import javaemul.internal.JsUtils;
/**
* Implementation of Map interface based on a hash table. [Sun
* docs]
*
* @param key type
* @param value type
*/
abstract class AbstractHashMap extends AbstractMap {
private final class EntrySet extends AbstractSet> {
@Override
public void clear() {
AbstractHashMap.this.clear();
}
@Override
public boolean contains(Object o) {
if (o instanceof Map.Entry) {
return containsEntry((Map.Entry, ?>) o);
}
return false;
}
@Override
public Iterator> iterator() {
return new EntrySetIterator();
}
@Override
public boolean remove(Object entry) {
if (contains(entry)) {
Object key = ((Map.Entry, ?>) entry).getKey();
AbstractHashMap.this.remove(key);
return true;
}
return false;
}
@Override
public int size() {
return AbstractHashMap.this.size();
}
}
/**
* Iterator for EntrySet
.
*/
private final class EntrySetIterator implements Iterator> {
private Iterator> stringMapEntries = stringMap.iterator();
private Iterator> current = stringMapEntries;
private Iterator> last;
private boolean hasNext = computeHasNext();
private int lastModCount = modCount;
@Override
public boolean hasNext() {
return hasNext;
}
private boolean computeHasNext() {
if (current.hasNext()) {
return true;
}
if (current != stringMapEntries) {
return false;
}
current = hashCodeMap.iterator();
return current.hasNext();
}
@Override
public Entry next() {
checkConcurrentModification(modCount, lastModCount);
checkElement(hasNext());
last = current;
Entry rv = current.next();
hasNext = computeHasNext();
return rv;
}
@Override
public void remove() {
checkState(last != null);
checkConcurrentModification(modCount, lastModCount);
last.remove();
last = null;
hasNext = computeHasNext();
lastModCount = modCount;
}
}
/** A map of integral hashCodes onto entries. */
private InternalHashCodeMap hashCodeMap;
/** A map of Strings onto values. */
private InternalStringMap stringMap;
int modCount;
public AbstractHashMap() {
reset();
}
public AbstractHashMap(int ignored) {
// This implementation of HashMap has no need of initial capacities.
this(ignored, 0);
}
public AbstractHashMap(int ignored, float alsoIgnored) {
// This implementation of HashMap has no need of load factors or capacities.
checkArgument(ignored >= 0, "Negative initial capacity");
checkArgument(alsoIgnored >= 0, "Non-positive load factor");
reset();
}
public AbstractHashMap(Map extends K, ? extends V> toBeCopied) {
reset();
this.putAll(toBeCopied);
}
@Override
public void clear() {
reset();
}
private void reset() {
hashCodeMap = new InternalHashCodeMap(this);
stringMap = new InternalStringMap(this);
structureChanged();
}
void structureChanged() {
if (!isApiChecked()) {
// Shouldn't be necessary but JsCompiler chokes on removing modCount so make sure we don't pay
// cost for updating the field.
return;
}
this.modCount++;
}
@Override
public boolean containsKey(Object key) {
return key instanceof String
? stringMap.contains(JsUtils.uncheckedCast(key))
: hashCodeMap.getEntry(key) != null;
}
@Override
public boolean containsValue(Object value) {
return containsValue(value, stringMap) || containsValue(value, hashCodeMap);
}
private boolean containsValue(Object value, Iterable> entries) {
for (Entry entry : entries) {
if (equals(value, entry.getValue())) {
return true;
}
}
return false;
}
@Override
public Set> entrySet() {
return new EntrySet();
}
@Override
public V get(Object key) {
return key instanceof String
? stringMap.get(JsUtils.uncheckedCast(key))
: getEntryValueOrNull(hashCodeMap.getEntry(key));
}
@Override
public V put(K key, V value) {
return key instanceof String
? stringMap.put(JsUtils.uncheckedCast(key), value)
: hashCodeMap.put(key, value);
}
@Override
public V remove(Object key) {
return key instanceof String
? stringMap.remove(JsUtils.uncheckedCast(key))
: hashCodeMap.remove(key);
}
@Override
public int size() {
return hashCodeMap.size() + stringMap.size();
}
/**
* Subclasses must override to return a whether or not two keys or values are
* equal.
*/
abstract boolean equals(Object value1, Object value2);
/**
* Subclasses must override to return a hash code for a given key. The key is
* guaranteed to be non-null and not a String.
*/
abstract int getHashCode(Object key);
}