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

com.google.common.collect.MultimapBuilder Maven / Gradle / Ivy

/*
 * 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.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
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 {@code com.google.common.collect}, for example: * *

   {@code
 *   ListMultimap treeListMultimap =
 *       MultimapBuilder.treeKeys().arrayListValues().build();
 *   SetMultimap hashEnumMultimap =
 *       MultimapBuilder.hashKeys().enumSetValues(MyEnum.class).build();}
* *

{@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. * *

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 An upper bound on the key type of the generated multimap. * @param An upper bound on the value type of the generated multimap. * @since 16.0 */ @Beta @GwtCompatible public abstract class MultimapBuilder { /* * 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 {@link HashMap} to map keys to value collections. */ public static MultimapBuilderWithKeys hashKeys() { return hashKeys(DEFAULT_EXPECTED_KEYS); } /** * Uses a {@link HashMap} to map keys to value collections, initialized to expect the specified * number of keys. * * @throws IllegalArgumentException if {@code expectedKeys < 0} */ public static MultimapBuilderWithKeys hashKeys(final int expectedKeys) { checkNonnegative(expectedKeys, "expectedKeys"); return new MultimapBuilderWithKeys() { @Override Map> createMap() { return new HashMap>(expectedKeys); } }; } /** * Uses a {@link LinkedHashMap} to map keys to value collections. * *

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 linkedHashKeys() { return linkedHashKeys(DEFAULT_EXPECTED_KEYS); } /** * Uses a {@link LinkedHashMap} to map keys to value collections, initialized to expect the * specified number of keys. * *

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 linkedHashKeys(final int expectedKeys) { checkNonnegative(expectedKeys, "expectedKeys"); return new MultimapBuilderWithKeys() { @Override Map> createMap() { return new LinkedHashMap>(expectedKeys); } }; } /** * Uses a naturally-ordered {@link TreeMap} to map keys to value collections. * *

The collections returned by {@link Multimap#keySet()}, {@link Multimap#keys()}, and * {@link Multimap#asMap()} will iterate through the keys in sorted order. * *

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 treeKeys() { return treeKeys(Ordering.natural()); } /** * Uses a {@link TreeMap} sorted by the specified comparator to map keys to value collections. * *

The collections returned by {@link Multimap#keySet()}, {@link Multimap#keys()}, and * {@link Multimap#asMap()} will iterate through the keys in sorted order. * *

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}. * *

Multimaps generated by the resulting builder will not be serializable if {@code comparator} * is not serializable. */ public static MultimapBuilderWithKeys treeKeys(final Comparator comparator) { checkNotNull(comparator); return new MultimapBuilderWithKeys() { @Override Map> createMap() { return new TreeMap>(comparator); } }; } /** * Uses an {@link EnumMap} to map keys to value collections. */ public static > MultimapBuilderWithKeys enumKeys( final Class keyClass) { checkNotNull(keyClass); return new MultimapBuilderWithKeys() { @SuppressWarnings("unchecked") @Override Map> createMap() { // K must actually be K0, since enums are effectively final // (their subclasses are inaccessible) return (Map>) new EnumMap>(keyClass); } }; } private static final class ArrayListSupplier implements Supplier>, Serializable { private final int expectedValuesPerKey; ArrayListSupplier(int expectedValuesPerKey) { this.expectedValuesPerKey = checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); } @Override public List get() { return new ArrayList(expectedValuesPerKey); } } private enum LinkedListSupplier implements Supplier> { INSTANCE; public static Supplier> instance() { // Each call generates a fresh LinkedList, which can serve as a List for any V. @SuppressWarnings({"rawtypes", "unchecked"}) Supplier> result = (Supplier) INSTANCE; return result; } @Override public List get() { return new LinkedList(); } } private static final class HashSetSupplier implements Supplier>, Serializable { private final int expectedValuesPerKey; HashSetSupplier(int expectedValuesPerKey) { this.expectedValuesPerKey = checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); } @Override public Set get() { return new HashSet(expectedValuesPerKey); } } private static final class LinkedHashSetSupplier implements Supplier>, Serializable { private final int expectedValuesPerKey; LinkedHashSetSupplier(int expectedValuesPerKey) { this.expectedValuesPerKey = checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); } @Override public Set get() { return new LinkedHashSet(expectedValuesPerKey); } } private static final class TreeSetSupplier implements Supplier>, Serializable { private final Comparator comparator; TreeSetSupplier(Comparator comparator) { this.comparator = checkNotNull(comparator); } @Override public SortedSet get() { return new TreeSet(comparator); } } private static final class EnumSetSupplier> implements Supplier>, Serializable { private final Class clazz; EnumSetSupplier(Class clazz) { this.clazz = checkNotNull(clazz); } @Override public Set get() { return EnumSet.noneOf(clazz); } } /** * 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 The upper bound on the key type of the generated multimap. */ public abstract static class MultimapBuilderWithKeys { private static final int DEFAULT_EXPECTED_VALUES_PER_KEY = 2; MultimapBuilderWithKeys() {} abstract Map> createMap(); /** * Uses an {@link ArrayList} to store value collections. */ public ListMultimapBuilder arrayListValues() { return arrayListValues(DEFAULT_EXPECTED_VALUES_PER_KEY); } /** * 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 arrayListValues(final int expectedValuesPerKey) { checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); return new ListMultimapBuilder() { @Override public ListMultimap build() { return Multimaps.newListMultimap( MultimapBuilderWithKeys.this.createMap(), new ArrayListSupplier(expectedValuesPerKey)); } }; } /** * Uses a {@link LinkedList} to store value collections. */ public ListMultimapBuilder linkedListValues() { return new ListMultimapBuilder() { @Override public ListMultimap build() { return Multimaps.newListMultimap( MultimapBuilderWithKeys.this.createMap(), LinkedListSupplier.instance()); } }; } /** * Uses a {@link HashSet} to store value collections. */ public SetMultimapBuilder hashSetValues() { return hashSetValues(DEFAULT_EXPECTED_VALUES_PER_KEY); } /** * Uses a {@link HashSet} to store value collections, initialized to expect the specified number * of values per key. * * @throws IllegalArgumentException if {@code expectedValuesPerKey < 0} */ public SetMultimapBuilder hashSetValues(final int expectedValuesPerKey) { checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); return new SetMultimapBuilder() { @Override public SetMultimap build() { return Multimaps.newSetMultimap( MultimapBuilderWithKeys.this.createMap(), new HashSetSupplier(expectedValuesPerKey)); } }; } /** * Uses a {@link LinkedHashSet} to store value collections. */ public SetMultimapBuilder linkedHashSetValues() { return linkedHashSetValues(DEFAULT_EXPECTED_VALUES_PER_KEY); } /** * Uses a {@link LinkedHashSet} to store value collections, initialized to expect the specified * number of values per key. * * @throws IllegalArgumentException if {@code expectedValuesPerKey < 0} */ public SetMultimapBuilder linkedHashSetValues(final int expectedValuesPerKey) { checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); return new SetMultimapBuilder() { @Override public SetMultimap build() { return Multimaps.newSetMultimap( MultimapBuilderWithKeys.this.createMap(), new LinkedHashSetSupplier(expectedValuesPerKey)); } }; } /** * Uses a naturally-ordered {@link TreeSet} to store value collections. */ @SuppressWarnings("rawtypes") public SortedSetMultimapBuilder treeSetValues() { return treeSetValues(Ordering.natural()); } /** * Uses a {@link TreeSet} ordered by the specified comparator to store value collections. * *

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