/*
 * Copyright (C) 2013 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.collect.CollectPreconditions.checkNonnegative;

import com.google.common.annotations.Beta;
import com.google.common.annotations.GwtCompatible;
import com.google.common.base.Supplier;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;

A builder for a multimap implementation that allows customization of the backing map and value collection implementations used in a particular multimap.

This can be used to easily configure multimap data structure implementations not provided explicitly in com.google.common.collect, for example:


ListMultimap<String, Integer> treeListMultimap =
    MultimapBuilder.treeKeys().arrayListValues().build();
SetMultimap<Integer, MyEnum> hashEnumMultimap =
    MultimapBuilder.hashKeys().enumSetValues(MyEnum.class).build();

MultimapBuilder instances are immutable. Invoking a configuration method has no effect on the receiving instance; you must store and use the new builder instance it returns instead.

The generated multimaps are serializable if the key and value types are serializable, unless stated otherwise in one of the configuration methods.

Author:Louis Wasserman
Type parameters:
  • <K0> – An upper bound on the key type of the generated multimap.
  • <V0> – An upper bound on the value type of the generated multimap.
Since:16.0
/** * A builder for a multimap implementation that allows customization of the backing map and value * collection implementations used in a particular multimap. * * <p>This can be used to easily configure multimap data structure implementations not provided * explicitly in {@code com.google.common.collect}, for example: * * <pre>{@code * ListMultimap<String, Integer> treeListMultimap = * MultimapBuilder.treeKeys().arrayListValues().build(); * SetMultimap<Integer, MyEnum> hashEnumMultimap = * MultimapBuilder.hashKeys().enumSetValues(MyEnum.class).build(); * }</pre> * * <p>{@code MultimapBuilder} instances are immutable. Invoking a configuration method has no effect * on the receiving instance; you must store and use the new builder instance it returns instead. * * <p>The generated multimaps are serializable if the key and value types are serializable, unless * stated otherwise in one of the configuration methods. * * @author Louis Wasserman * @param <K0> An upper bound on the key type of the generated multimap. * @param <V0> An upper bound on the value type of the generated multimap. * @since 16.0 */
@Beta @GwtCompatible public abstract class MultimapBuilder<K0, V0> { /* * Leaving K and V as upper bounds rather than the actual key and value types allows type * parameters to be left implicit more often. CacheBuilder uses the same technique. */ private MultimapBuilder() {} private static final int DEFAULT_EXPECTED_KEYS = 8;
Uses a hash table to map keys to value collections.
/** Uses a hash table to map keys to value collections. */
public static MultimapBuilderWithKeys<Object> hashKeys() { return hashKeys(DEFAULT_EXPECTED_KEYS); }
Uses a hash table to map keys to value collections, initialized to expect the specified number of keys.
Throws:
  • IllegalArgumentException – if expectedKeys < 0
/** * Uses a hash table to map keys to value collections, initialized to expect the specified * number of keys. * * @throws IllegalArgumentException if {@code expectedKeys < 0} */
public static MultimapBuilderWithKeys<Object> hashKeys(final int expectedKeys) { checkNonnegative(expectedKeys, "expectedKeys"); return new MultimapBuilderWithKeys<Object>() { @Override <K, V> Map<K, Collection<V>> createMap() { return Platform.newHashMapWithExpectedSize(expectedKeys); } }; }
Uses a hash table to map keys to value collections.

The collections returned by Multimap.keySet(), Multimap.keys(), and Multimap.asMap() will iterate through the keys in the order that they were first added to the multimap, save that if all values associated with a key are removed and then the key is added back into the multimap, that key will come last in the key iteration order.

/** * Uses a hash table to map keys to value collections. * * <p>The collections returned by {@link Multimap#keySet()}, {@link Multimap#keys()}, and {@link * Multimap#asMap()} will iterate through the keys in the order that they were first added to the * multimap, save that if all values associated with a key are removed and then the key is added * back into the multimap, that key will come last in the key iteration order. */
public static MultimapBuilderWithKeys<Object> linkedHashKeys() { return linkedHashKeys(DEFAULT_EXPECTED_KEYS); }
Uses an hash table to map keys to value collections, initialized to expect the specified number of keys.

The collections returned by Multimap.keySet(), Multimap.keys(), and Multimap.asMap() will iterate through the keys in the order that they were first added to the multimap, save that if all values associated with a key are removed and then the key is added back into the multimap, that key will come last in the key iteration order.

/** * Uses an hash table to map keys to value collections, initialized to expect the * specified number of keys. * * <p>The collections returned by {@link Multimap#keySet()}, {@link Multimap#keys()}, and {@link * Multimap#asMap()} will iterate through the keys in the order that they were first added to the * multimap, save that if all values associated with a key are removed and then the key is added * back into the multimap, that key will come last in the key iteration order. */
public static MultimapBuilderWithKeys<Object> linkedHashKeys(final int expectedKeys) { checkNonnegative(expectedKeys, "expectedKeys"); return new MultimapBuilderWithKeys<Object>() { @Override <K, V> Map<K, Collection<V>> createMap() { return Platform.newLinkedHashMapWithExpectedSize(expectedKeys); } }; }
Uses a naturally-ordered TreeMap to map keys to value collections.

The collections returned by Multimap.keySet(), Multimap.keys(), and Multimap.asMap() will iterate through the keys in sorted order.

For all multimaps generated by the resulting builder, the Multimap.keySet() can be safely cast to a SortedSet, and the Multimap.asMap() can safely be cast to a SortedMap.

/** * Uses a naturally-ordered {@link TreeMap} to map keys to value collections. * * <p>The collections returned by {@link Multimap#keySet()}, {@link Multimap#keys()}, and {@link * Multimap#asMap()} will iterate through the keys in sorted order. * * <p>For all multimaps generated by the resulting builder, the {@link Multimap#keySet()} can be * safely cast to a {@link java.util.SortedSet}, and the {@link Multimap#asMap()} can safely be * cast to a {@link java.util.SortedMap}. */
@SuppressWarnings("rawtypes") public static MultimapBuilderWithKeys<Comparable> treeKeys() { return treeKeys(Ordering.natural()); }
Uses a TreeMap sorted by the specified comparator to map keys to value collections.

The collections returned by Multimap.keySet(), Multimap.keys(), and Multimap.asMap() will iterate through the keys in sorted order.

For all multimaps generated by the resulting builder, the Multimap.keySet() can be safely cast to a SortedSet, and the Multimap.asMap() can safely be cast to a SortedMap.

Multimaps generated by the resulting builder will not be serializable if comparator is not serializable.

/** * Uses a {@link TreeMap} sorted by the specified comparator to map keys to value collections. * * <p>The collections returned by {@link Multimap#keySet()}, {@link Multimap#keys()}, and {@link * Multimap#asMap()} will iterate through the keys in sorted order. * * <p>For all multimaps generated by the resulting builder, the {@link Multimap#keySet()} can be * safely cast to a {@link java.util.SortedSet}, and the {@link Multimap#asMap()} can safely be * cast to a {@link java.util.SortedMap}. * * <p>Multimaps generated by the resulting builder will not be serializable if {@code comparator} * is not serializable. */
public static <K0> MultimapBuilderWithKeys<K0> treeKeys(final Comparator<K0> comparator) { checkNotNull(comparator); return new MultimapBuilderWithKeys<K0>() { @Override <K extends K0, V> Map<K, Collection<V>> createMap() { return new TreeMap<>(comparator); } }; }
Uses an EnumMap to map keys to value collections.
Since:16.0
/** * Uses an {@link EnumMap} to map keys to value collections. * * @since 16.0 */
public static <K0 extends Enum<K0>> MultimapBuilderWithKeys<K0> enumKeys( final Class<K0> keyClass) { checkNotNull(keyClass); return new MultimapBuilderWithKeys<K0>() { @SuppressWarnings("unchecked") @Override <K extends K0, V> Map<K, Collection<V>> createMap() { // K must actually be K0, since enums are effectively final // (their subclasses are inaccessible) return (Map<K, Collection<V>>) new EnumMap<K0, Collection<V>>(keyClass); } }; } private static final class ArrayListSupplier<V> implements Supplier<List<V>>, Serializable { private final int expectedValuesPerKey; ArrayListSupplier(int expectedValuesPerKey) { this.expectedValuesPerKey = checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); } @Override public List<V> get() { return new ArrayList<V>(expectedValuesPerKey); } } private enum LinkedListSupplier implements Supplier<List<Object>> { INSTANCE; public static <V> Supplier<List<V>> instance() { // Each call generates a fresh LinkedList, which can serve as a List<V> for any V. @SuppressWarnings({"rawtypes", "unchecked"}) Supplier<List<V>> result = (Supplier) INSTANCE; return result; } @Override public List<Object> get() { return new LinkedList<>(); } } private static final class HashSetSupplier<V> implements Supplier<Set<V>>, Serializable { private final int expectedValuesPerKey; HashSetSupplier(int expectedValuesPerKey) { this.expectedValuesPerKey = checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); } @Override public Set<V> get() { return Platform.newHashSetWithExpectedSize(expectedValuesPerKey); } } private static final class LinkedHashSetSupplier<V> implements Supplier<Set<V>>, Serializable { private final int expectedValuesPerKey; LinkedHashSetSupplier(int expectedValuesPerKey) { this.expectedValuesPerKey = checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); } @Override public Set<V> get() { return Platform.newLinkedHashSetWithExpectedSize(expectedValuesPerKey); } } private static final class TreeSetSupplier<V> implements Supplier<SortedSet<V>>, Serializable { private final Comparator<? super V> comparator; TreeSetSupplier(Comparator<? super V> comparator) { this.comparator = checkNotNull(comparator); } @Override public SortedSet<V> get() { return new TreeSet<V>(comparator); } } private static final class EnumSetSupplier<V extends Enum<V>> implements Supplier<Set<V>>, Serializable { private final Class<V> clazz; EnumSetSupplier(Class<V> clazz) { this.clazz = checkNotNull(clazz); } @Override public Set<V> get() { return EnumSet.noneOf(clazz); } }
An intermediate stage in a MultimapBuilder in which the key-value collection map implementation has been specified, but the value collection implementation has not.
Type parameters:
  • <K0> – The upper bound on the key type of the generated multimap.
Since:16.0
/** * An intermediate stage in a {@link MultimapBuilder} in which the key-value collection map * implementation has been specified, but the value collection implementation has not. * * @param <K0> The upper bound on the key type of the generated multimap. * @since 16.0 */
public abstract static class MultimapBuilderWithKeys<K0> { private static final int DEFAULT_EXPECTED_VALUES_PER_KEY = 2; MultimapBuilderWithKeys() {} abstract <K extends K0, V> Map<K, Collection<V>> createMap();
Uses an ArrayList to store value collections.
/** Uses an {@link ArrayList} to store value collections. */
public ListMultimapBuilder<K0, Object> arrayListValues() { return arrayListValues(DEFAULT_EXPECTED_VALUES_PER_KEY); }
Uses an ArrayList to store value collections, initialized to expect the specified number of values per key.
Throws:
/** * Uses an {@link ArrayList} to store value collections, initialized to expect the specified * number of values per key. * * @throws IllegalArgumentException if {@code expectedValuesPerKey < 0} */
public ListMultimapBuilder<K0, Object> arrayListValues(final int expectedValuesPerKey) { checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); return new ListMultimapBuilder<K0, Object>() { @Override public <K extends K0, V> ListMultimap<K, V> build() { return Multimaps.newListMultimap( MultimapBuilderWithKeys.this.<K, V>createMap(), new ArrayListSupplier<V>(expectedValuesPerKey)); } }; }
Uses a LinkedList to store value collections.
/** Uses a {@link LinkedList} to store value collections. */
public ListMultimapBuilder<K0, Object> linkedListValues() { return new ListMultimapBuilder<K0, Object>() { @Override public <K extends K0, V> ListMultimap<K, V> build() { return Multimaps.newListMultimap( MultimapBuilderWithKeys.this.<K, V>createMap(), LinkedListSupplier.<V>instance()); } }; }
Uses a hash-based Set to store value collections.
/** Uses a hash-based {@code Set} to store value collections. */
public SetMultimapBuilder<K0, Object> hashSetValues() { return hashSetValues(DEFAULT_EXPECTED_VALUES_PER_KEY); }
Uses a hash-based Set to store value collections, initialized to expect the specified number of values per key.
Throws:
/** * Uses a hash-based {@code Set} to store value collections, initialized to expect the specified number * of values per key. * * @throws IllegalArgumentException if {@code expectedValuesPerKey < 0} */
public SetMultimapBuilder<K0, Object> hashSetValues(final int expectedValuesPerKey) { checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); return new SetMultimapBuilder<K0, Object>() { @Override public <K extends K0, V> SetMultimap<K, V> build() { return Multimaps.newSetMultimap( MultimapBuilderWithKeys.this.<K, V>createMap(), new HashSetSupplier<V>(expectedValuesPerKey)); } }; }
Uses an insertion-ordered hash-based Set to store value collections.
/** Uses an insertion-ordered hash-based {@code Set} to store value collections. */
public SetMultimapBuilder<K0, Object> linkedHashSetValues() { return linkedHashSetValues(DEFAULT_EXPECTED_VALUES_PER_KEY); }
Uses an insertion-ordered hash-based Set to store value collections, initialized to expect the specified number of values per key.
Throws:
/** * Uses an insertion-ordered hash-based {@code Set} to store value collections, initialized to expect the specified * number of values per key. * * @throws IllegalArgumentException if {@code expectedValuesPerKey < 0} */
public SetMultimapBuilder<K0, Object> linkedHashSetValues(final int expectedValuesPerKey) { checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); return new SetMultimapBuilder<K0, Object>() { @Override public <K extends K0, V> SetMultimap<K, V> build() { return Multimaps.newSetMultimap( MultimapBuilderWithKeys.this.<K, V>createMap(), new LinkedHashSetSupplier<V>(expectedValuesPerKey)); } }; }
Uses a naturally-ordered TreeSet to store value collections.
/** Uses a naturally-ordered {@link TreeSet} to store value collections. */
@SuppressWarnings("rawtypes") public SortedSetMultimapBuilder<K0, Comparable> treeSetValues() { return treeSetValues(Ordering.natural()); }
Uses a TreeSet ordered by the specified comparator to store value collections.

Multimaps generated by the resulting builder will not be serializable if comparator is not serializable.

/** * Uses a {@link TreeSet} ordered by the specified comparator to store value collections. * * <p>Multimaps generated by the resulting builder will not be serializable if {@code * comparator} is not serializable. */
public <V0> SortedSetMultimapBuilder<K0, V0> treeSetValues(final Comparator<V0> comparator) { checkNotNull(comparator, "comparator"); return new SortedSetMultimapBuilder<K0, V0>() { @Override public <K extends K0, V extends V0> SortedSetMultimap<K, V> build() { return Multimaps.newSortedSetMultimap( MultimapBuilderWithKeys.this.<K, V>createMap(), new TreeSetSupplier<V>(comparator)); } }; }
Uses an EnumSet to store value collections.
/** Uses an {@link EnumSet} to store value collections. */
public <V0 extends Enum<V0>> SetMultimapBuilder<K0, V0> enumSetValues( final Class<V0> valueClass) { checkNotNull(valueClass, "valueClass"); return new SetMultimapBuilder<K0, V0>() { @Override public <K extends K0, V extends V0> SetMultimap<K, V> build() { // V must actually be V0, since enums are effectively final // (their subclasses are inaccessible) @SuppressWarnings({"unchecked", "rawtypes"}) Supplier<Set<V>> factory = (Supplier) new EnumSetSupplier<V0>(valueClass); return Multimaps.newSetMultimap(MultimapBuilderWithKeys.this.<K, V>createMap(), factory); } }; } }
Returns a new, empty Multimap with the specified implementation.
/** Returns a new, empty {@code Multimap} with the specified implementation. */
public abstract <K extends K0, V extends V0> Multimap<K, V> build();
Returns a Multimap with the specified implementation, initialized with the entries of multimap.
/** * Returns a {@code Multimap} with the specified implementation, initialized with the entries of * {@code multimap}. */
public <K extends K0, V extends V0> Multimap<K, V> build( Multimap<? extends K, ? extends V> multimap) { Multimap<K, V> result = build(); result.putAll(multimap); return result; }
A specialization of MultimapBuilder that generates ListMultimap instances.
Since:16.0
/** * A specialization of {@link MultimapBuilder} that generates {@link ListMultimap} instances. * * @since 16.0 */
public abstract static class ListMultimapBuilder<K0, V0> extends MultimapBuilder<K0, V0> { ListMultimapBuilder() {} @Override public abstract <K extends K0, V extends V0> ListMultimap<K, V> build(); @Override public <K extends K0, V extends V0> ListMultimap<K, V> build( Multimap<? extends K, ? extends V> multimap) { return (ListMultimap<K, V>) super.build(multimap); } }
A specialization of MultimapBuilder that generates SetMultimap instances.
Since:16.0
/** * A specialization of {@link MultimapBuilder} that generates {@link SetMultimap} instances. * * @since 16.0 */
public abstract static class SetMultimapBuilder<K0, V0> extends MultimapBuilder<K0, V0> { SetMultimapBuilder() {} @Override public abstract <K extends K0, V extends V0> SetMultimap<K, V> build(); @Override public <K extends K0, V extends V0> SetMultimap<K, V> build( Multimap<? extends K, ? extends V> multimap) { return (SetMultimap<K, V>) super.build(multimap); } }
A specialization of MultimapBuilder that generates SortedSetMultimap instances.
Since:16.0
/** * A specialization of {@link MultimapBuilder} that generates {@link SortedSetMultimap} instances. * * @since 16.0 */
public abstract static class SortedSetMultimapBuilder<K0, V0> extends SetMultimapBuilder<K0, V0> { SortedSetMultimapBuilder() {} @Override public abstract <K extends K0, V extends V0> SortedSetMultimap<K, V> build(); @Override public <K extends K0, V extends V0> SortedSetMultimap<K, V> build( Multimap<? extends K, ? extends V> multimap) { return (SortedSetMultimap<K, V>) super.build(multimap); } } }