/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch 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.elasticsearch.common.collect;
import com.carrotsearch.hppc.ObjectCollection;
import com.carrotsearch.hppc.ObjectContainer;
import com.carrotsearch.hppc.ObjectLookupContainer;
import com.carrotsearch.hppc.ObjectObjectAssociativeContainer;
import com.carrotsearch.hppc.ObjectObjectHashMap;
import com.carrotsearch.hppc.ObjectObjectMap;
import com.carrotsearch.hppc.cursors.ObjectCursor;
import com.carrotsearch.hppc.cursors.ObjectObjectCursor;
import com.carrotsearch.hppc.predicates.ObjectObjectPredicate;
import com.carrotsearch.hppc.predicates.ObjectPredicate;
import com.carrotsearch.hppc.procedures.ObjectObjectProcedure;
import java.util.Iterator;
import java.util.Map;
/**
* An immutable map implementation based on open hash map.
* <p>
* Can be constructed using a {@link #builder()}, or using {@link #builder(ImmutableOpenMap)} (which is an optimized
* option to copy over existing content and modify it).
*/
public final class ImmutableOpenMap<KType, VType> implements Iterable<ObjectObjectCursor<KType, VType>> {
private final ObjectObjectHashMap<KType, VType> map;
private ImmutableOpenMap(ObjectObjectHashMap<KType, VType> map) {
this.map = map;
}
/**
* @return Returns the value associated with the given key or the default value
* for the key type, if the key is not associated with any value.
* <p>
* <b>Important note:</b> For primitive type values, the value returned for a non-existing
* key may not be the default value of the primitive type (it may be any value previously
* assigned to that slot).
*/
public VType get(KType key) {
return map.get(key);
}
/**
* @return Returns the value associated with the given key or the provided default value if the
* key is not associated with any value.
*/
public VType getOrDefault(KType key, VType defaultValue) {
return map.getOrDefault(key, defaultValue);
}
/**
* Returns <code>true</code> if this container has an association to a value for
* the given key.
*/
public boolean containsKey(KType key) {
return map.containsKey(key);
}
/**
* @return Returns the current size (number of assigned keys) in the container.
*/
public int size() {
return map.size();
}
/**
* @return Return <code>true</code> if this hash map contains no assigned keys.
*/
public boolean isEmpty() {
return map.isEmpty();
}
/**
* Returns a cursor over the entries (key-value pairs) in this map. The iterator is
* implemented as a cursor and it returns <b>the same cursor instance</b> on every
* call to {@link Iterator#next()}. To read the current key and value use the cursor's
* public fields. An example is shown below.
* <pre>
* for (IntShortCursor c : intShortMap)
* {
* System.out.println("index=" + c.index
* + " key=" + c.key
* + " value=" + c.value);
* }
* </pre>
* <p>
* The <code>index</code> field inside the cursor gives the internal index inside
* the container's implementation. The interpretation of this index depends on
* to the container.
*/
@Override
public Iterator<ObjectObjectCursor<KType, VType>> iterator() {
return map.iterator();
}
/**
* Returns a specialized view of the keys of this associated container.
* The view additionally implements {@link ObjectLookupContainer}.
*/
public ObjectLookupContainer<KType> keys() {
return map.keys();
}
/**
* Returns a direct iterator over the keys.
*/
public Iterator<KType> keysIt() {
final Iterator<ObjectCursor<KType>> iterator = map.keys().iterator();
return new Iterator<KType>() {
@Override
public boolean hasNext() { return iterator.hasNext(); }
@Override
public KType next() {
return iterator.next().value;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
/**
* @return Returns a container with all values stored in this map.
*/
public ObjectContainer<VType> values() {
return map.values();
}
/**
* Returns a direct iterator over the keys.
*/
public Iterator<VType> valuesIt() {
final Iterator<ObjectCursor<VType>> iterator = map.values().iterator();
return new Iterator<VType>() {
@Override
public boolean hasNext() { return iterator.hasNext(); }
@Override
public VType next() {
return iterator.next().value;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@Override
public String toString() {
return map.toString();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ImmutableOpenMap that = (ImmutableOpenMap) o;
if (!map.equals(that.map)) return false;
return true;
}
@Override
public int hashCode() {
return map.hashCode();
}
@SuppressWarnings("unchecked")
private static final ImmutableOpenMap EMPTY = new ImmutableOpenMap(new ObjectObjectHashMap());
@SuppressWarnings("unchecked")
public static <KType, VType> ImmutableOpenMap<KType, VType> of() {
return EMPTY;
}
/**
* @return An immutable copy of the given map
*/
public static <KType, VType> ImmutableOpenMap<KType, VType> copyOf(ObjectObjectMap<KType, VType> map) {
Builder<KType, VType> builder = builder();
builder.putAll(map);
return builder.build();
}
public static <KType, VType> Builder<KType, VType> builder() {
return new Builder<>();
}
public static <KType, VType> Builder<KType, VType> builder(int size) {
return new Builder<>(size);
}
public static <KType, VType> Builder<KType, VType> builder(ImmutableOpenMap<KType, VType> map) {
return new Builder<>(map);
}
public static class Builder<KType, VType> implements ObjectObjectMap<KType, VType> {
private ObjectObjectHashMap<KType, VType> map;
public Builder() {
//noinspection unchecked
this(EMPTY);
}
public Builder(int size) {
this.map = new ObjectObjectHashMap<>(size);
}
public Builder(ImmutableOpenMap<KType, VType> map) {
this.map = map.map.clone();
}
/**
* Builds a new instance of the
*/
public ImmutableOpenMap<KType, VType> build() {
ObjectObjectHashMap<KType, VType> map = this.map;
this.map = null; // nullify the map, so any operation post build will fail! (hackish, but safest)
return new ImmutableOpenMap<>(map);
}
/**
* Puts all the entries in the map to the builder.
*/
public Builder<KType, VType> putAll(Map<KType, VType> map) {
for (Map.Entry<KType, VType> entry : map.entrySet()) {
this.map.put(entry.getKey(), entry.getValue());
}
return this;
}
/**
* A put operation that can be used in the fluent pattern.
*/
public Builder<KType, VType> fPut(KType key, VType value) {
map.put(key, value);
return this;
}
@Override
public VType put(KType key, VType value) {
return map.put(key, value);
}
@Override
public VType get(KType key) {
return map.get(key);
}
@Override
public VType getOrDefault(KType kType, VType vType) {
return map.getOrDefault(kType, vType);
}
@Override
public int putAll(ObjectObjectAssociativeContainer<? extends KType, ? extends VType> container) {
return map.putAll(container);
}
@Override
public int putAll(Iterable<? extends ObjectObjectCursor<? extends KType, ? extends VType>> iterable) {
return map.putAll(iterable);
}
/**
* Remove that can be used in the fluent pattern.
*/
public Builder<KType, VType> fRemove(KType key) {
map.remove(key);
return this;
}
@Override
public VType remove(KType key) {
return map.remove(key);
}
@Override
public Iterator<ObjectObjectCursor<KType, VType>> iterator() {
return map.iterator();
}
@Override
public boolean containsKey(KType key) {
return map.containsKey(key);
}
@Override
public int size() {
return map.size();
}
@Override
public boolean isEmpty() {
return map.isEmpty();
}
@Override
public int removeAll(ObjectContainer<? super KType> container) {
return map.removeAll(container);
}
@Override
public int removeAll(ObjectPredicate<? super KType> predicate) {
return map.removeAll(predicate);
}
@Override
public <T extends ObjectObjectProcedure<? super KType, ? super VType>> T forEach(T procedure) {
return map.forEach(procedure);
}
@Override
public void clear() {
map.clear();
}
@Override
public ObjectCollection<KType> keys() {
return map.keys();
}
@Override
public ObjectContainer<VType> values() {
return map.values();
}
@SuppressWarnings("unchecked")
public <K, V> Builder<K, V> cast() {
return (Builder) this;
}
@Override
public int removeAll(ObjectObjectPredicate<? super KType, ? super VType> predicate) {
return map.removeAll(predicate);
}
@Override
public <T extends ObjectObjectPredicate<? super KType, ? super VType>> T forEach(T predicate) {
return map.forEach(predicate);
}
@Override
public int indexOf(KType key) {
return map.indexOf(key);
}
@Override
public boolean indexExists(int index) {
return map.indexExists(index);
}
@Override
public VType indexGet(int index) {
return map.indexGet(index);
}
@Override
public VType indexReplace(int index, VType newValue) {
return map.indexReplace(index, newValue);
}
@Override
public void indexInsert(int index, KType key, VType value) {
map.indexInsert(index, key, value);
}
@Override
public void release() {
map.release();
}
@Override
public String visualizeKeyDistribution(int characters) {
return map.visualizeKeyDistribution(characters);
}
}
}