All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.elasticsearch.common.collect.ImmutableOpenMap Maven / Gradle / Ivy

There is a newer version: 8.14.0
Show newest version
/*
 * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
 * or more contributor license agreements. Licensed under the Elastic License
 * 2.0 and the Server Side Public License, v 1; you may not use this file except
 * in compliance with, at your election, the Elastic License 2.0 or the Server
 * Side Public License, v 1.
 */

package org.elasticsearch.common.collect;

import com.carrotsearch.hppc.ObjectCollection;
import com.carrotsearch.hppc.ObjectObjectHashMap;
import com.carrotsearch.hppc.cursors.ObjectCursor;
import com.carrotsearch.hppc.cursors.ObjectObjectCursor;

import java.util.AbstractCollection;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Predicate;

/**
 * An immutable map implementation based on open hash map.
 * 

* Can be constructed using a {@link #builder()}, or using {@link #builder(Map)} (which is an optimized * option to copy over existing content and modify it). */ public final class ImmutableOpenMap extends AbstractMap { private final ObjectObjectHashMap map; /** * Holds cached entrySet(). */ private Set> entrySet; private ImmutableOpenMap(ObjectObjectHashMap 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. *

* Important note: 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). */ @Override @SuppressWarnings("unchecked") public VType get(Object key) { return map.get((KType) key); } /** * @return Returns the value associated with the given key or the provided default value if the * key is not associated with any value. */ @Override @SuppressWarnings("unchecked") public VType getOrDefault(Object key, VType defaultValue) { return map.getOrDefault((KType) key, defaultValue); } /** * Returns true if this container has an association to a value for * the given key. */ @Override @SuppressWarnings("unchecked") public boolean containsKey(Object key) { return map.containsKey((KType) key); } @Override public boolean containsValue(Object value) { for (ObjectCursor cursor : map.values()) { if (Objects.equals(cursor.value, value)) { return true; } } return false; } @Override public VType put(KType key, VType value) { throw new UnsupportedOperationException("modification is not supported"); } @Override public VType remove(Object key) { throw new UnsupportedOperationException("modification is not supported"); } @Override public void putAll(Map m) { throw new UnsupportedOperationException("modification is not supported"); } @Override public void clear() { throw new UnsupportedOperationException("modification is not supported"); } @Override public int size() { return map.size(); } @Override public boolean isEmpty() { return map.isEmpty(); } @Override public Set> entrySet() { Set> es; return (es = entrySet) == null ? (entrySet = new EntrySet<>(map)) : es; } private static final class ImmutableEntry implements Map.Entry { private final KType key; private final VType value; ImmutableEntry(KType key, VType value) { this.key = key; this.value = value; } @Override public KType getKey() { return key; } @Override public VType getValue() { return value; } @Override public VType setValue(VType value) { throw new UnsupportedOperationException("collection is immutable"); } @Override @SuppressWarnings("rawtypes") public boolean equals(Object o) { if (this == o) return true; if ((o instanceof Map.Entry) == false) return false; Map.Entry that = (Map.Entry) o; return Objects.equals(key, that.getKey()) && Objects.equals(value, that.getValue()); } @Override public int hashCode() { return Objects.hashCode(key) ^ Objects.hashCode(value); } } private static final class ConversionIterator implements Iterator> { private final Iterator> original; ConversionIterator(Iterator> original) { this.original = original; } @Override public boolean hasNext() { return original.hasNext(); } @Override public Map.Entry next() { final ObjectObjectCursor obj = original.next(); if (obj == null) { return null; } return new ImmutableEntry<>(obj.key, obj.value); } @Override public void remove() { throw new UnsupportedOperationException("removal is unsupported"); } } private static final class EntrySet extends AbstractSet> { private final ObjectObjectHashMap map; private EntrySet(ObjectObjectHashMap map) { this.map = map; } @Override public int size() { return map.size(); } @Override public void clear() { throw new UnsupportedOperationException("removal is unsupported"); } @Override public Iterator> iterator() { return new ConversionIterator<>(map.iterator()); } @SuppressWarnings("unchecked") @Override public boolean contains(Object o) { if (o instanceof Map.Entry == false) { return false; } Map.Entry e = (Map.Entry) o; Object key = e.getKey(); if (map.containsKey((KType) key) == false) { return false; } Object val = map.get((KType) key); return Objects.equals(val, e.getValue()); } @Override public boolean remove(Object o) { throw new UnsupportedOperationException("removal is not supported"); } @Override public Spliterator> spliterator() { return Spliterators.spliterator(iterator(), size(), Spliterator.SIZED); } @Override public void forEach(Consumer> action) { map.forEach((Consumer>) ooCursor -> { ImmutableEntry entry = new ImmutableEntry<>(ooCursor.key, ooCursor.value); action.accept(entry); }); } } private static final class KeySet extends AbstractSet { private final ObjectObjectHashMap.KeysContainer keys; private KeySet(ObjectObjectHashMap.KeysContainer keys) { this.keys = keys; } @Override public Iterator iterator() { final Iterator> iterator = keys.iterator(); return new Iterator<>() { @Override public boolean hasNext() { return iterator.hasNext(); } @Override public KType next() { return iterator.next().value; } @Override public void remove() { throw new UnsupportedOperationException(); } }; } @Override public int size() { return keys.size(); } @Override @SuppressWarnings("unchecked") public boolean contains(Object o) { return keys.contains((KType) o); } }; @Override public Set keySet() { return new KeySet<>(map.keys()); } @Override public Collection values() { return new AbstractCollection() { @Override public Iterator iterator() { return ImmutableOpenMap.iterator(map.values()); } @Override public int size() { return map.size(); } }; } static Iterator iterator(ObjectCollection collection) { final Iterator> iterator = collection.iterator(); return new Iterator<>() { @Override public boolean hasNext() { return iterator.hasNext(); } @Override public T next() { return iterator.next().value; } @Override public void remove() { throw new UnsupportedOperationException(); } }; } @Override public String toString() { return map.toString(); } @SuppressWarnings({ "unchecked", "rawtypes" }) private static final ImmutableOpenMap EMPTY = new ImmutableOpenMap(new ObjectObjectHashMap()); @SuppressWarnings("unchecked") public static ImmutableOpenMap of() { return EMPTY; } public static Builder builder() { return new Builder<>(); } public static Builder builder(int size) { return new Builder<>(size); } public static Builder builder(Map map) { if (map instanceof ImmutableOpenMap iom) { return new Builder<>(iom); } Builder builder = new Builder<>(map.size()); builder.putAllFromMap(map); return builder; } public static class Builder { // if the Builder was constructed with a reference to an existing ImmutableOpenMap, then this will be non-null // (at least until the point where the builder has been used to actually make some changes to the map that is // being built -- see maybeCloneMap) private ImmutableOpenMap reference; // if the Builder was constructed with a size (only), then this will be non-null (and reference will be null) private ObjectObjectHashMap mutableMap; // n.b. a constructor can either set reference or it can set mutableMap, but it must not set both. /** * This method must be called before reading or writing via the {@code mutableMap} -- so every method * of builder should call this as the first thing it does. If {@code reference} is not null, it will be used to * populate {@code mutableMap} as a clone of the {@code reference} ImmutableOpenMap. It will then null out * {@code reference}. * * If {@code reference} is already null (and by extension, {@code mutableMap} is already *not* null), * then this method is a no-op. */ private void maybeCloneMap() { if (reference != null) { this.mutableMap = reference.map.clone(); // make a mutable clone of the reference ImmutableOpenMap this.reference = null; // and throw away the reference, we now rely on mutableMap } } @SuppressWarnings("unchecked") public Builder() { this(EMPTY); } public Builder(int size) { this.mutableMap = new ObjectObjectHashMap<>(size); } public Builder(ImmutableOpenMap immutableOpenMap) { this.reference = Objects.requireNonNull(immutableOpenMap); } /** * Builds a new ImmutableOpenMap from this builder. */ public ImmutableOpenMap build() { if (reference != null) { ImmutableOpenMap reference = this.reference; this.reference = null; // null out the reference so that you can't reuse this builder return reference; } else { ObjectObjectHashMap mutableMap = this.mutableMap; this.mutableMap = null; // null out the map so that you can't reuse this builder return mutableMap.isEmpty() ? of() : new ImmutableOpenMap<>(mutableMap); } } /** * Puts all the entries in the map to the builder. */ public Builder putAllFromMap(Map map) { maybeCloneMap(); for (Map.Entry entry : map.entrySet()) { this.mutableMap.put(entry.getKey(), entry.getValue()); } return this; } /** * A put operation that can be used in the fluent pattern. */ public Builder fPut(KType key, VType value) { maybeCloneMap(); mutableMap.put(key, value); return this; } public VType put(KType key, VType value) { maybeCloneMap(); return mutableMap.put(key, value); } public VType get(KType key) { maybeCloneMap(); return mutableMap.get(key); } public VType getOrDefault(KType kType, VType vType) { maybeCloneMap(); return mutableMap.getOrDefault(kType, vType); } public void putAll(Builder builder) { maybeCloneMap(); for (var entry : builder.mutableMap) { mutableMap.put(entry.key, entry.value); } } /** * Remove that can be used in the fluent pattern. */ public Builder fRemove(KType key) { maybeCloneMap(); mutableMap.remove(key); return this; } public VType remove(KType key) { maybeCloneMap(); return mutableMap.remove(key); } public boolean containsKey(KType key) { maybeCloneMap(); return mutableMap.containsKey(key); } public int size() { maybeCloneMap(); return mutableMap.size(); } public boolean isEmpty() { maybeCloneMap(); return mutableMap.isEmpty(); } public int removeAll(Predicate predicate) { maybeCloneMap(); return mutableMap.removeAll(predicate::test); } public void removeAllFromCollection(Collection collection) { maybeCloneMap(); for (var k : collection) { mutableMap.remove(k); } } public void clear() { maybeCloneMap(); mutableMap.clear(); } public Set keys() { maybeCloneMap(); return new KeySet<>(mutableMap.keys()); } @SuppressWarnings("unchecked") public Builder cast() { return (Builder) this; } public int removeAll(BiPredicate predicate) { maybeCloneMap(); return mutableMap.removeAll(predicate::test); } public int indexOf(KType key) { maybeCloneMap(); return mutableMap.indexOf(key); } public boolean indexExists(int index) { maybeCloneMap(); return mutableMap.indexExists(index); } public VType indexGet(int index) { maybeCloneMap(); return mutableMap.indexGet(index); } public VType indexReplace(int index, VType newValue) { maybeCloneMap(); return mutableMap.indexReplace(index, newValue); } public void indexInsert(int index, KType key, VType value) { maybeCloneMap(); mutableMap.indexInsert(index, key, value); } public void release() { maybeCloneMap(); mutableMap.release(); } public String visualizeKeyDistribution(int characters) { maybeCloneMap(); return mutableMap.visualizeKeyDistribution(characters); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy