tech.ydb.shaded.google.common.collect.ImmutableMap Maven / Gradle / Ivy
/*
 * Copyright (C) 2008 The Guava Authors
 *
 * 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 com.google.common.collect;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.CollectPreconditions.checkEntryNotNull;
import static com.google.common.collect.CollectPreconditions.checkNonnegative;
import static java.util.Objects.requireNonNull;
import com.google.common.annotations.GwtCompatible;
import com.google.common.annotations.J2ktIncompatible;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.annotations.DoNotCall;
import com.google.errorprone.annotations.DoNotMock;
import com.google.errorprone.annotations.concurrent.LazyInit;
import com.google.j2objc.annotations.RetainedWith;
import com.google.j2objc.annotations.WeakOuter;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.AbstractMap;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import javax.annotation.CheckForNull;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
 * A {@link Map} whose contents will never change, with many other important properties detailed at
 * {@link ImmutableCollection}.
 *
 * See the Guava User Guide article on immutable collections.
 *
 * @author Jesse Wilson
 * @author Kevin Bourrillion
 * @since 2.0
 */
@DoNotMock("Use ImmutableMap.of or another implementation")
@GwtCompatible(serializable = true, emulated = true)
@SuppressWarnings("serial") // we're overriding default serialization
@ElementTypesAreNonnullByDefault
public abstract class ImmutableMap implements Map, Serializable {
  /**
   * Returns the empty map. This map behaves and performs comparably to {@link
   * Collections#emptyMap}, and is preferable mainly for consistency and maintainability of your
   * code.
   *
   * Performance note: the instance returned is a singleton.
   */
  @SuppressWarnings("unchecked")
  public static  ImmutableMap of() {
    return (ImmutableMap) RegularImmutableMap.EMPTY;
  }
  /**
   * Returns an immutable map containing a single entry. This map behaves and performs comparably to
   * {@link Collections#singletonMap} but will not accept a null key or value. It is preferable
   * mainly for consistency and maintainability of your code.
   */
  public static  ImmutableMap of(K k1, V v1) {
    checkEntryNotNull(k1, v1);
    return RegularImmutableMap.create(1, new Object[] {k1, v1});
  }
  /**
   * Returns an immutable map containing the given entries, in order.
   *
   * @throws IllegalArgumentException if duplicate keys are provided
   */
  public static  ImmutableMap of(K k1, V v1, K k2, V v2) {
    checkEntryNotNull(k1, v1);
    checkEntryNotNull(k2, v2);
    return RegularImmutableMap.create(2, new Object[] {k1, v1, k2, v2});
  }
  /**
   * Returns an immutable map containing the given entries, in order.
   *
   * @throws IllegalArgumentException if duplicate keys are provided
   */
  public static  ImmutableMap of(K k1, V v1, K k2, V v2, K k3, V v3) {
    checkEntryNotNull(k1, v1);
    checkEntryNotNull(k2, v2);
    checkEntryNotNull(k3, v3);
    return RegularImmutableMap.create(3, new Object[] {k1, v1, k2, v2, k3, v3});
  }
  /**
   * Returns an immutable map containing the given entries, in order.
   *
   * @throws IllegalArgumentException if duplicate keys are provided
   */
  public static  ImmutableMap of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) {
    checkEntryNotNull(k1, v1);
    checkEntryNotNull(k2, v2);
    checkEntryNotNull(k3, v3);
    checkEntryNotNull(k4, v4);
    return RegularImmutableMap.create(4, new Object[] {k1, v1, k2, v2, k3, v3, k4, v4});
  }
  /**
   * Returns an immutable map containing the given entries, in order.
   *
   * @throws IllegalArgumentException if duplicate keys are provided
   */
  public static  ImmutableMap of(
      K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) {
    checkEntryNotNull(k1, v1);
    checkEntryNotNull(k2, v2);
    checkEntryNotNull(k3, v3);
    checkEntryNotNull(k4, v4);
    checkEntryNotNull(k5, v5);
    return RegularImmutableMap.create(5, new Object[] {k1, v1, k2, v2, k3, v3, k4, v4, k5, v5});
  }
  /**
   * Returns an immutable map containing the given entries, in order.
   *
   * @throws IllegalArgumentException if duplicate keys are provided
   * @since 31.0
   */
  public static  ImmutableMap of(
      K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6) {
    checkEntryNotNull(k1, v1);
    checkEntryNotNull(k2, v2);
    checkEntryNotNull(k3, v3);
    checkEntryNotNull(k4, v4);
    checkEntryNotNull(k5, v5);
    checkEntryNotNull(k6, v6);
    return RegularImmutableMap.create(
        6, new Object[] {k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6});
  }
  /**
   * Returns an immutable map containing the given entries, in order.
   *
   * @throws IllegalArgumentException if duplicate keys are provided
   * @since 31.0
   */
  public static  ImmutableMap of(
      K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7) {
    checkEntryNotNull(k1, v1);
    checkEntryNotNull(k2, v2);
    checkEntryNotNull(k3, v3);
    checkEntryNotNull(k4, v4);
    checkEntryNotNull(k5, v5);
    checkEntryNotNull(k6, v6);
    checkEntryNotNull(k7, v7);
    return RegularImmutableMap.create(
        7, new Object[] {k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7});
  }
  /**
   * Returns an immutable map containing the given entries, in order.
   *
   * @throws IllegalArgumentException if duplicate keys are provided
   * @since 31.0
   */
  public static  ImmutableMap of(
      K k1,
      V v1,
      K k2,
      V v2,
      K k3,
      V v3,
      K k4,
      V v4,
      K k5,
      V v5,
      K k6,
      V v6,
      K k7,
      V v7,
      K k8,
      V v8) {
    checkEntryNotNull(k1, v1);
    checkEntryNotNull(k2, v2);
    checkEntryNotNull(k3, v3);
    checkEntryNotNull(k4, v4);
    checkEntryNotNull(k5, v5);
    checkEntryNotNull(k6, v6);
    checkEntryNotNull(k7, v7);
    checkEntryNotNull(k8, v8);
    return RegularImmutableMap.create(
        8, new Object[] {k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8});
  }
  /**
   * Returns an immutable map containing the given entries, in order.
   *
   * @throws IllegalArgumentException if duplicate keys are provided
   * @since 31.0
   */
  public static  ImmutableMap of(
      K k1,
      V v1,
      K k2,
      V v2,
      K k3,
      V v3,
      K k4,
      V v4,
      K k5,
      V v5,
      K k6,
      V v6,
      K k7,
      V v7,
      K k8,
      V v8,
      K k9,
      V v9) {
    checkEntryNotNull(k1, v1);
    checkEntryNotNull(k2, v2);
    checkEntryNotNull(k3, v3);
    checkEntryNotNull(k4, v4);
    checkEntryNotNull(k5, v5);
    checkEntryNotNull(k6, v6);
    checkEntryNotNull(k7, v7);
    checkEntryNotNull(k8, v8);
    checkEntryNotNull(k9, v9);
    return RegularImmutableMap.create(
        9, new Object[] {k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9});
  }
  /**
   * Returns an immutable map containing the given entries, in order.
   *
   * @throws IllegalArgumentException if duplicate keys are provided
   * @since 31.0
   */
  public static  ImmutableMap of(
      K k1,
      V v1,
      K k2,
      V v2,
      K k3,
      V v3,
      K k4,
      V v4,
      K k5,
      V v5,
      K k6,
      V v6,
      K k7,
      V v7,
      K k8,
      V v8,
      K k9,
      V v9,
      K k10,
      V v10) {
    checkEntryNotNull(k1, v1);
    checkEntryNotNull(k2, v2);
    checkEntryNotNull(k3, v3);
    checkEntryNotNull(k4, v4);
    checkEntryNotNull(k5, v5);
    checkEntryNotNull(k6, v6);
    checkEntryNotNull(k7, v7);
    checkEntryNotNull(k8, v8);
    checkEntryNotNull(k9, v9);
    checkEntryNotNull(k10, v10);
    return RegularImmutableMap.create(
        10,
        new Object[] {
          k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10
        });
  }
  // looking for of() with > 10 entries? Use the builder or ofEntries instead.
  /**
   * Returns an immutable map containing the given entries, in order.
   *
   * @throws IllegalArgumentException if duplicate keys are provided
   * @since 31.0
   */
  @SafeVarargs
  public static  ImmutableMap ofEntries(Entry extends K, ? extends V>... entries) {
    @SuppressWarnings("unchecked") // we will only ever read these
    Entry[] entries2 = (Entry[]) entries;
    return copyOf(Arrays.asList(entries2));
  }
  /**
   * Verifies that {@code key} and {@code value} are non-null, and returns a new immutable entry
   * with those values.
   *
   * A call to {@link Entry#setValue} on the returned entry will always throw {@link
   * UnsupportedOperationException}.
   */
  static  Entry entryOf(K key, V value) {
    checkEntryNotNull(key, value);
    return new AbstractMap.SimpleImmutableEntry<>(key, value);
  }
  /**
   * Returns a new builder. The generated builder is equivalent to the builder created by the {@link
   * Builder} constructor.
   */
  public static  Builder builder() {
    return new Builder<>();
  }
  /**
   * Returns a new builder, expecting the specified number of entries to be added.
   *
   * If {@code expectedSize} is exactly the number of entries added to the builder before {@link
   * Builder#build} is called, the builder is likely to perform better than an unsized {@link
   * #builder()} would have.
   *
   * 
It is not specified if any performance benefits apply if {@code expectedSize} is close to,
   * but not exactly, the number of entries added to the builder.
   *
   * @since 23.1
   */
  public static  Builder builderWithExpectedSize(int expectedSize) {
    checkNonnegative(expectedSize, "expectedSize");
    return new Builder<>(expectedSize);
  }
  static void checkNoConflict(
      boolean safe, String conflictDescription, Object entry1, Object entry2) {
    if (!safe) {
      throw conflictException(conflictDescription, entry1, entry2);
    }
  }
  static IllegalArgumentException conflictException(
      String conflictDescription, Object entry1, Object entry2) {
    return new IllegalArgumentException(
        "Multiple entries with same " + conflictDescription + ": " + entry1 + " and " + entry2);
  }
  /**
   * A builder for creating immutable map instances, especially {@code public static final} maps
   * ("constant maps"). Example:
   *
   * {@code
   * static final ImmutableMap WORD_TO_INT =
   *     new ImmutableMap.Builder()
   *         .put("one", 1)
   *         .put("two", 2)
   *         .put("three", 3)
   *         .buildOrThrow();
   * }  
   *
   * For small immutable maps, the {@code ImmutableMap.of()} methods are even more
   * convenient.
   *
   * 
By default, a {@code Builder} will generate maps that iterate over entries in the order they
   * were inserted into the builder, equivalently to {@code LinkedHashMap}. For example, in the
   * above example, {@code WORD_TO_INT.entrySet()} is guaranteed to iterate over the entries in the
   * order {@code "one"=1, "two"=2, "three"=3}, and {@code keySet()} and {@code values()} respect
   * the same order. If you want a different order, consider using {@link ImmutableSortedMap} to
   * sort by keys, or call {@link #orderEntriesByValue(Comparator)}, which changes this builder to
   * sort entries by value.
   *
   * 
Builder instances can be reused - it is safe to call {@link #buildOrThrow} multiple times to
   * build multiple maps in series. Each map is a superset of the maps created before it.
   *
   * @since 2.0
   */
  @DoNotMock
  public static class Builder {
    @CheckForNull Comparator super V> valueComparator;
    @Nullable Object[] alternatingKeysAndValues;
    int size;
    boolean entriesUsed;
    /**
     * If non-null, a duplicate key we found in a previous buildKeepingLast() or buildOrThrow()
     * call. A later buildOrThrow() can simply report this duplicate immediately.
     */
    @Nullable DuplicateKey duplicateKey;
    /**
     * Creates a new builder. The returned builder is equivalent to the builder generated by {@link
     * ImmutableMap#builder}.
     */
    public Builder() {
      this(ImmutableCollection.Builder.DEFAULT_INITIAL_CAPACITY);
    }
    @SuppressWarnings({"unchecked", "rawtypes"})
    Builder(int initialCapacity) {
      this.alternatingKeysAndValues = new @Nullable Object[2 * initialCapacity];
      this.size = 0;
      this.entriesUsed = false;
    }
    private void ensureCapacity(int minCapacity) {
      if (minCapacity * 2 > alternatingKeysAndValues.length) {
        alternatingKeysAndValues =
            Arrays.copyOf(
                alternatingKeysAndValues,
                ImmutableCollection.Builder.expandedCapacity(
                    alternatingKeysAndValues.length, minCapacity * 2));
        entriesUsed = false;
      }
    }
    /**
     * Associates {@code key} with {@code value} in the built map. If the same key is put more than
     * once, {@link #buildOrThrow} will fail, while {@link #buildKeepingLast} will keep the last
     * value put for that key.
     */
    @CanIgnoreReturnValue
    public Builder put(K key, V value) {
      ensureCapacity(size + 1);
      checkEntryNotNull(key, value);
      alternatingKeysAndValues[2 * size] = key;
      alternatingKeysAndValues[2 * size + 1] = value;
      size++;
      return this;
    }
    /**
     * Adds the given {@code entry} to the map, making it immutable if necessary. If the same key is
     * put more than once, {@link #buildOrThrow} will fail, while {@link #buildKeepingLast} will
     * keep the last value put for that key.
     *
     * @since 11.0
     */
    @CanIgnoreReturnValue
    public Builder put(Entry extends K, ? extends V> entry) {
      return put(entry.getKey(), entry.getValue());
    }
    /**
     * Associates all of the given map's keys and values in the built map. If the same key is put
     * more than once, {@link #buildOrThrow} will fail, while {@link #buildKeepingLast} will keep
     * the last value put for that key.
     *
     * @throws NullPointerException if any key or value in {@code map} is null
     */
    @CanIgnoreReturnValue
    public Builder putAll(Map extends K, ? extends V> map) {
      return putAll(map.entrySet());
    }
    /**
     * Adds all of the given entries to the built map. If the same key is put more than once, {@link
     * #buildOrThrow} will fail, while {@link #buildKeepingLast} will keep the last value put for
     * that key.
     *
     * @throws NullPointerException if any key, value, or entry is null
     * @since 19.0
     */
    @CanIgnoreReturnValue
    public Builder putAll(Iterable extends Entry extends K, ? extends V>> entries) {
      if (entries instanceof Collection) {
        ensureCapacity(size + ((Collection>) entries).size());
      }
      for (Entry extends K, ? extends V> entry : entries) {
        put(entry);
      }
      return this;
    }
    /**
     * Configures this {@code Builder} to order entries by value according to the specified
     * comparator.
     *
     * The sort order is stable, that is, if two entries have values that compare as equivalent,
     * the entry that was inserted first will be first in the built map's iteration order.
     *
     * @throws IllegalStateException if this method was already called
     * @since 19.0
     */
    @CanIgnoreReturnValue
    public Builder orderEntriesByValue(Comparator super V> valueComparator) {
      checkState(this.valueComparator == null, "valueComparator was already set");
      this.valueComparator = checkNotNull(valueComparator, "valueComparator");
      return this;
    }
    @CanIgnoreReturnValue
    Builder combine(Builder other) {
      checkNotNull(other);
      ensureCapacity(this.size + other.size);
      System.arraycopy(
          other.alternatingKeysAndValues,
          0,
          this.alternatingKeysAndValues,
          this.size * 2,
          other.size * 2);
      this.size += other.size;
      return this;
    }
    private ImmutableMap build(boolean throwIfDuplicateKeys) {
      if (throwIfDuplicateKeys && duplicateKey != null) {
        throw duplicateKey.exception();
      }
      /*
       * If entries is full, then this implementation may end up using the entries array
       * directly and writing over the entry objects with non-terminal entries, but this is
       * safe; if this Builder is used further, it will grow the entries array (so it can't
       * affect the original array), and future build() calls will always copy any entry
       * objects that cannot be safely reused.
       */
      // localAlternatingKeysAndValues is an alias for the alternatingKeysAndValues field, except if
      // we end up removing duplicates in a copy of the array.
      @Nullable Object[] localAlternatingKeysAndValues;
      int localSize = size;
      if (valueComparator == null) {
        localAlternatingKeysAndValues = alternatingKeysAndValues;
      } else {
        if (entriesUsed) {
          alternatingKeysAndValues = Arrays.copyOf(alternatingKeysAndValues, 2 * size);
        }
        localAlternatingKeysAndValues = alternatingKeysAndValues;
        if (!throwIfDuplicateKeys) {
          // We want to retain only the last-put value for any given key, before sorting.
          // This could be improved, but orderEntriesByValue is rather rarely used anyway.
          localAlternatingKeysAndValues = lastEntryForEachKey(localAlternatingKeysAndValues, size);
          if (localAlternatingKeysAndValues.length < alternatingKeysAndValues.length) {
            localSize = localAlternatingKeysAndValues.length >>> 1;
          }
        }
        sortEntries(localAlternatingKeysAndValues, localSize, valueComparator);
      }
      entriesUsed = true;
      ImmutableMap map =
          RegularImmutableMap.create(localSize, localAlternatingKeysAndValues, this);
      if (throwIfDuplicateKeys && duplicateKey != null) {
        throw duplicateKey.exception();
      }
      return map;
    }
    /**
     * Returns a newly-created immutable map. The iteration order of the returned map is the order
     * in which entries were inserted into the builder, unless {@link #orderEntriesByValue} was
     * called, in which case entries are sorted by value.
     *
     * Prefer the equivalent method {@link #buildOrThrow()} to make it explicit that the method
     * will throw an exception if there are duplicate keys. The {@code build()} method will soon be
     * deprecated.
     *
     * @throws IllegalArgumentException if duplicate keys were added
     */
    public ImmutableMap build() {
      return buildOrThrow();
    }
    /**
     * Returns a newly-created immutable map, or throws an exception if any key was added more than
     * once. The iteration order of the returned map is the order in which entries were inserted
     * into the builder, unless {@link #orderEntriesByValue} was called, in which case entries are
     * sorted by value.
     *
     * @throws IllegalArgumentException if duplicate keys were added
     * @since 31.0
     */
    public ImmutableMap buildOrThrow() {
      return build(true);
    }
    /**
     * Returns a newly-created immutable map, using the last value for any key that was added more
     * than once. The iteration order of the returned map is the order in which entries were
     * inserted into the builder, unless {@link #orderEntriesByValue} was called, in which case
     * entries are sorted by value. If a key was added more than once, it appears in iteration order
     * based on the first time it was added, again unless {@link #orderEntriesByValue} was called.
     *
     * In the current implementation, all values associated with a given key are stored in the
     * {@code Builder} object, even though only one of them will be used in the built map. If there
     * can be many repeated keys, it may be more space-efficient to use a {@link
     * java.util.LinkedHashMap LinkedHashMap} and {@link ImmutableMap#copyOf(Map)} rather than
     * {@code ImmutableMap.Builder}.
     *
     * @since 31.1
     */
    public ImmutableMap buildKeepingLast() {
      return build(false);
    }
    static  void sortEntries(
        @Nullable Object[] alternatingKeysAndValues,
        int size,
        Comparator super V> valueComparator) {
      @SuppressWarnings({"rawtypes", "unchecked"})
      Entry