Home | History | Annotate | Download | only in collect
      1 /*
      2  * Copyright (C) 2013 The Guava Authors
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.google.common.collect;
     18 
     19 import static com.google.common.base.Preconditions.checkNotNull;
     20 import static com.google.common.collect.CollectPreconditions.checkNonnegative;
     21 
     22 import com.google.common.annotations.Beta;
     23 import com.google.common.annotations.GwtCompatible;
     24 import com.google.common.base.Supplier;
     25 
     26 import java.io.Serializable;
     27 import java.util.ArrayList;
     28 import java.util.Collection;
     29 import java.util.Comparator;
     30 import java.util.EnumMap;
     31 import java.util.EnumSet;
     32 import java.util.HashMap;
     33 import java.util.HashSet;
     34 import java.util.LinkedHashMap;
     35 import java.util.LinkedHashSet;
     36 import java.util.LinkedList;
     37 import java.util.List;
     38 import java.util.Map;
     39 import java.util.Set;
     40 import java.util.SortedSet;
     41 import java.util.TreeMap;
     42 import java.util.TreeSet;
     43 
     44 /**
     45  * A builder for a multimap implementation that allows customization of the backing map and value
     46  * collection implementations used in a particular multimap.
     47  *
     48  * <p>This can be used to easily configure multimap data structure implementations not provided
     49  * explicitly in {@code com.google.common.collect}, for example:
     50  *
     51  * <pre>   {@code
     52  *   ListMultimap<String, Integer> treeListMultimap =
     53  *       MultimapBuilder.treeKeys().arrayListValues().build();
     54  *   SetMultimap<Integer, MyEnum> hashEnumMultimap =
     55  *       MultimapBuilder.hashKeys().enumSetValues(MyEnum.class).build();}</pre>
     56  *
     57  * <p>{@code MultimapBuilder} instances are immutable.  Invoking a configuration method has no
     58  * effect on the receiving instance; you must store and use the new builder instance it returns
     59  * instead.
     60  *
     61  * <p>The generated multimaps are serializable if the key and value types are serializable,
     62  * unless stated otherwise in one of the configuration methods.
     63  *
     64  * @author Louis Wasserman
     65  * @param  An upper bound on the key type of the generated multimap.
     66  * @param  An upper bound on the value type of the generated multimap.
     67  * @since 16.0
     68  */
     69 @Beta
     70 @GwtCompatible
     71 public abstract class MultimapBuilder<K0, V0> {
     72   /*
     73    * Leaving K and V as upper bounds rather than the actual key and value types allows type
     74    * parameters to be left implicit more often. CacheBuilder uses the same technique.
     75    */
     76 
     77   private MultimapBuilder() {}
     78 
     79   private static final int DEFAULT_EXPECTED_KEYS = 8;
     80 
     81   /**
     82    * Uses a {@link HashMap} to map keys to value collections.
     83    */
     84   public static MultimapBuilderWithKeys<Object> hashKeys() {
     85     return hashKeys(DEFAULT_EXPECTED_KEYS);
     86   }
     87 
     88   /**
     89    * Uses a {@link HashMap} to map keys to value collections, initialized to expect the specified
     90    * number of keys.
     91    *
     92    * @throws IllegalArgumentException if {@code expectedKeys < 0}
     93    */
     94   public static MultimapBuilderWithKeys<Object> hashKeys(final int expectedKeys) {
     95     checkNonnegative(expectedKeys, "expectedKeys");
     96     return new MultimapBuilderWithKeys<Object>() {
     97       @Override
     98       <K, V> Map<K, Collection<V>> createMap() {
     99         return new HashMap<K, Collection<V>>(expectedKeys);
    100       }
    101     };
    102   }
    103 
    104   /**
    105    * Uses a {@link LinkedHashMap} to map keys to value collections.
    106    *
    107    * <p>The collections returned by {@link Multimap#keySet()}, {@link Multimap#keys()}, and
    108    * {@link Multimap#asMap()} will iterate through the keys in the order that they were first added
    109    * to the multimap, save that if all values associated with a key are removed and then the key is
    110    * added back into the multimap, that key will come last in the key iteration order.
    111    */
    112   public static MultimapBuilderWithKeys<Object> linkedHashKeys() {
    113     return linkedHashKeys(DEFAULT_EXPECTED_KEYS);
    114   }
    115 
    116   /**
    117    * Uses a {@link LinkedHashMap} to map keys to value collections, initialized to expect the
    118    * specified number of keys.
    119    *
    120    * <p>The collections returned by {@link Multimap#keySet()}, {@link Multimap#keys()}, and
    121    * {@link Multimap#asMap()} will iterate through the keys in the order that they were first added
    122    * to the multimap, save that if all values associated with a key are removed and then the key is
    123    * added back into the multimap, that key will come last in the key iteration order.
    124    */
    125   public static MultimapBuilderWithKeys<Object> linkedHashKeys(final int expectedKeys) {
    126     checkNonnegative(expectedKeys, "expectedKeys");
    127     return new MultimapBuilderWithKeys<Object>() {
    128       @Override
    129       <K, V> Map<K, Collection<V>> createMap() {
    130         return new LinkedHashMap<K, Collection<V>>(expectedKeys);
    131       }
    132     };
    133   }
    134 
    135   /**
    136    * Uses a naturally-ordered {@link TreeMap} to map keys to value collections.
    137    *
    138    * <p>The collections returned by {@link Multimap#keySet()}, {@link Multimap#keys()}, and
    139    * {@link Multimap#asMap()} will iterate through the keys in sorted order.
    140    *
    141    * <p>For all multimaps generated by the resulting builder, the {@link Multimap#keySet()} can be
    142    * safely cast to a {@link java.util.SortedSet}, and the {@link Multimap#asMap()} can safely be
    143    * cast to a {@link java.util.SortedMap}.
    144    */
    145   @SuppressWarnings("rawtypes")
    146   public static MultimapBuilderWithKeys<Comparable> treeKeys() {
    147     return treeKeys(Ordering.natural());
    148   }
    149 
    150   /**
    151    * Uses a {@link TreeMap} sorted by the specified comparator to map keys to value collections.
    152    *
    153    * <p>The collections returned by {@link Multimap#keySet()}, {@link Multimap#keys()}, and
    154    * {@link Multimap#asMap()} will iterate through the keys in sorted order.
    155    *
    156    * <p>For all multimaps generated by the resulting builder, the {@link Multimap#keySet()} can be
    157    * safely cast to a {@link java.util.SortedSet}, and the {@link Multimap#asMap()} can safely be
    158    * cast to a {@link java.util.SortedMap}.
    159    *
    160    * <p>Multimaps generated by the resulting builder will not be serializable if {@code comparator}
    161    * is not serializable.
    162    */
    163   public static <K0> MultimapBuilderWithKeys<K0> treeKeys(final Comparator<K0> comparator) {
    164     checkNotNull(comparator);
    165     return new MultimapBuilderWithKeys<K0>() {
    166       @Override
    167       <K extends K0, V> Map<K, Collection<V>> createMap() {
    168         return new TreeMap<K, Collection<V>>(comparator);
    169       }
    170     };
    171   }
    172 
    173   /**
    174    * Uses an {@link EnumMap} to map keys to value collections.
    175    */
    176   public static <K0 extends Enum<K0>> MultimapBuilderWithKeys<K0> enumKeys(
    177       final Class<K0> keyClass) {
    178     checkNotNull(keyClass);
    179     return new MultimapBuilderWithKeys<K0>() {
    180       @SuppressWarnings("unchecked")
    181       @Override
    182       <K extends K0, V> Map<K, Collection<V>> createMap() {
    183         // K must actually be K0, since enums are effectively final
    184         // (their subclasses are inaccessible)
    185         return (Map<K, Collection<V>>) new EnumMap<K0, Collection<V>>(keyClass);
    186       }
    187     };
    188   }
    189 
    190   private static final class ArrayListSupplier<V> implements Supplier<List<V>>, Serializable {
    191     private final int expectedValuesPerKey;
    192 
    193     ArrayListSupplier(int expectedValuesPerKey) {
    194       this.expectedValuesPerKey = checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey");
    195     }
    196 
    197     @Override
    198     public List<V> get() {
    199       return new ArrayList<V>(expectedValuesPerKey);
    200     }
    201   }
    202 
    203   private enum LinkedListSupplier implements Supplier<List<Object>> {
    204     INSTANCE;
    205 
    206     public static <V> Supplier<List<V>> instance() {
    207       // Each call generates a fresh LinkedList, which can serve as a List<V> for any V.
    208       @SuppressWarnings({"rawtypes", "unchecked"})
    209       Supplier<List<V>> result = (Supplier) INSTANCE;
    210       return result;
    211     }
    212 
    213     @Override
    214     public List<Object> get() {
    215       return new LinkedList<Object>();
    216     }
    217   }
    218 
    219   private static final class HashSetSupplier<V> implements Supplier<Set<V>>, Serializable {
    220     private final int expectedValuesPerKey;
    221 
    222     HashSetSupplier(int expectedValuesPerKey) {
    223       this.expectedValuesPerKey = checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey");
    224     }
    225 
    226     @Override
    227     public Set<V> get() {
    228       return new HashSet<V>(expectedValuesPerKey);
    229     }
    230   }
    231 
    232   private static final class LinkedHashSetSupplier<V> implements Supplier<Set<V>>, Serializable {
    233     private final int expectedValuesPerKey;
    234 
    235     LinkedHashSetSupplier(int expectedValuesPerKey) {
    236       this.expectedValuesPerKey = checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey");
    237     }
    238 
    239     @Override
    240     public Set<V> get() {
    241       return new LinkedHashSet<V>(expectedValuesPerKey);
    242     }
    243   }
    244 
    245   private static final class TreeSetSupplier<V> implements Supplier<SortedSet<V>>, Serializable {
    246     private final Comparator<? super V> comparator;
    247 
    248     TreeSetSupplier(Comparator<? super V> comparator) {
    249       this.comparator = checkNotNull(comparator);
    250     }
    251 
    252     @Override
    253     public SortedSet<V> get() {
    254       return new TreeSet<V>(comparator);
    255     }
    256   }
    257 
    258   private static final class EnumSetSupplier<V extends Enum<V>>
    259       implements Supplier<Set<V>>, Serializable {
    260     private final Class<V> clazz;
    261 
    262     EnumSetSupplier(Class<V> clazz) {
    263       this.clazz = checkNotNull(clazz);
    264     }
    265 
    266     @Override
    267     public Set<V> get() {
    268       return EnumSet.noneOf(clazz);
    269     }
    270   }
    271 
    272   /**
    273    * An intermediate stage in a {@link MultimapBuilder} in which the key-value collection map
    274    * implementation has been specified, but the value collection implementation has not.
    275    *
    276    * @param  The upper bound on the key type of the generated multimap.
    277    */
    278   public abstract static class MultimapBuilderWithKeys<K0> {
    279 
    280     private static final int DEFAULT_EXPECTED_VALUES_PER_KEY = 2;
    281 
    282     MultimapBuilderWithKeys() {}
    283 
    284     abstract <K extends K0, V> Map<K, Collection<V>> createMap();
    285 
    286     /**
    287      * Uses an {@link ArrayList} to store value collections.
    288      */
    289     public ListMultimapBuilder<K0, Object> arrayListValues() {
    290       return arrayListValues(DEFAULT_EXPECTED_VALUES_PER_KEY);
    291     }
    292 
    293     /**
    294      * Uses an {@link ArrayList} to store value collections, initialized to expect the specified
    295      * number of values per key.
    296      *
    297      * @throws IllegalArgumentException if {@code expectedValuesPerKey < 0}
    298      */
    299     public ListMultimapBuilder<K0, Object> arrayListValues(final int expectedValuesPerKey) {
    300       checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey");
    301       return new ListMultimapBuilder<K0, Object>() {
    302         @Override
    303         public <K extends K0, V> ListMultimap<K, V> build() {
    304           return Multimaps.newListMultimap(
    305               MultimapBuilderWithKeys.this.<K, V>createMap(),
    306               new ArrayListSupplier<V>(expectedValuesPerKey));
    307         }
    308       };
    309     }
    310 
    311     /**
    312      * Uses a {@link LinkedList} to store value collections.
    313      */
    314     public ListMultimapBuilder<K0, Object> linkedListValues() {
    315       return new ListMultimapBuilder<K0, Object>() {
    316         @Override
    317         public <K extends K0, V> ListMultimap<K, V> build() {
    318           return Multimaps.newListMultimap(
    319               MultimapBuilderWithKeys.this.<K, V>createMap(),
    320               LinkedListSupplier.<V>instance());
    321         }
    322       };
    323     }
    324 
    325     /**
    326      * Uses a {@link HashSet} to store value collections.
    327      */
    328     public SetMultimapBuilder<K0, Object> hashSetValues() {
    329       return hashSetValues(DEFAULT_EXPECTED_VALUES_PER_KEY);
    330     }
    331 
    332     /**
    333      * Uses a {@link HashSet} to store value collections, initialized to expect the specified number
    334      * of values per key.
    335      *
    336      * @throws IllegalArgumentException if {@code expectedValuesPerKey < 0}
    337      */
    338     public SetMultimapBuilder<K0, Object> hashSetValues(final int expectedValuesPerKey) {
    339       checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey");
    340       return new SetMultimapBuilder<K0, Object>() {
    341         @Override
    342         public <K extends K0, V> SetMultimap<K, V> build() {
    343           return Multimaps.newSetMultimap(
    344               MultimapBuilderWithKeys.this.<K, V>createMap(),
    345               new HashSetSupplier<V>(expectedValuesPerKey));
    346         }
    347       };
    348     }
    349 
    350     /**
    351      * Uses a {@link LinkedHashSet} to store value collections.
    352      */
    353     public SetMultimapBuilder<K0, Object> linkedHashSetValues() {
    354       return linkedHashSetValues(DEFAULT_EXPECTED_VALUES_PER_KEY);
    355     }
    356 
    357     /**
    358      * Uses a {@link LinkedHashSet} to store value collections, initialized to expect the specified
    359      * number of values per key.
    360      *
    361      * @throws IllegalArgumentException if {@code expectedValuesPerKey < 0}
    362      */
    363     public SetMultimapBuilder<K0, Object> linkedHashSetValues(final int expectedValuesPerKey) {
    364       checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey");
    365       return new SetMultimapBuilder<K0, Object>() {
    366         @Override
    367         public <K extends K0, V> SetMultimap<K, V> build() {
    368           return Multimaps.newSetMultimap(
    369               MultimapBuilderWithKeys.this.<K, V>createMap(),
    370               new LinkedHashSetSupplier<V>(expectedValuesPerKey));
    371         }
    372       };
    373     }
    374 
    375     /**
    376      * Uses a naturally-ordered {@link TreeSet} to store value collections.
    377      */
    378     @SuppressWarnings("rawtypes")
    379     public SortedSetMultimapBuilder<K0, Comparable> treeSetValues() {
    380       return treeSetValues(Ordering.natural());
    381     }
    382 
    383     /**
    384      * Uses a {@link TreeSet} ordered by the specified comparator to store value collections.
    385      *
    386      * <p>Multimaps generated by the resulting builder will not be serializable if
    387      * {@code comparator} is not serializable.
    388      */
    389     public <V0> SortedSetMultimapBuilder<K0, V0> treeSetValues(final Comparator<V0> comparator) {
    390       checkNotNull(comparator, "comparator");
    391       return new SortedSetMultimapBuilder<K0, V0>() {
    392         @Override
    393         public <K extends K0, V extends V0> SortedSetMultimap<K, V> build() {
    394           return Multimaps.newSortedSetMultimap(
    395               MultimapBuilderWithKeys.this.<K, V>createMap(),
    396               new TreeSetSupplier<V>(comparator));
    397         }
    398       };
    399     }
    400 
    401     /**
    402      * Uses an {@link EnumSet} to store value collections.
    403      */
    404     public <V0 extends Enum<V0>> SetMultimapBuilder<K0, V0> enumSetValues(
    405         final Class<V0> valueClass) {
    406       checkNotNull(valueClass, "valueClass");
    407       return new SetMultimapBuilder<K0, V0>() {
    408         @Override
    409         public <K extends K0, V extends V0> SetMultimap<K, V> build() {
    410           // V must actually be V0, since enums are effectively final
    411           // (their subclasses are inaccessible)
    412           @SuppressWarnings({"unchecked", "rawtypes"})
    413           Supplier<Set<V>> factory = (Supplier) new EnumSetSupplier<V0>(valueClass);
    414           return Multimaps.newSetMultimap(
    415               MultimapBuilderWithKeys.this.<K, V>createMap(),
    416               factory);
    417         }
    418       };
    419     }
    420   }
    421 
    422   /**
    423    * Returns a new, empty {@code Multimap} with the specified implementation.
    424    */
    425   public abstract <K extends K0, V extends V0> Multimap<K, V> build();
    426 
    427   /**
    428    * Returns a {@code Multimap} with the specified implementation, initialized with the entries of
    429    * {@code multimap}.
    430    */
    431   public <K extends K0, V extends V0> Multimap<K, V> build(
    432       Multimap<? extends K, ? extends V> multimap) {
    433     Multimap<K, V> result = build();
    434     result.putAll(multimap);
    435     return result;
    436   }
    437 
    438   /**
    439    * A specialization of {@link MultimapBuilder} that generates {@link ListMultimap} instances.
    440    */
    441   public abstract static class ListMultimapBuilder<K0, V0> extends MultimapBuilder<K0, V0> {
    442     ListMultimapBuilder() {}
    443 
    444     @Override
    445     public abstract <K extends K0, V extends V0> ListMultimap<K, V> build();
    446 
    447     @Override
    448     public <K extends K0, V extends V0> ListMultimap<K, V> build(
    449         Multimap<? extends K, ? extends V> multimap) {
    450       return (ListMultimap<K, V>) super.build(multimap);
    451     }
    452   }
    453 
    454   /**
    455    * A specialization of {@link MultimapBuilder} that generates {@link SetMultimap} instances.
    456    */
    457   public abstract static class SetMultimapBuilder<K0, V0> extends MultimapBuilder<K0, V0> {
    458     SetMultimapBuilder() {}
    459 
    460     @Override
    461     public abstract <K extends K0, V extends V0> SetMultimap<K, V> build();
    462 
    463     @Override
    464     public <K extends K0, V extends V0> SetMultimap<K, V> build(
    465         Multimap<? extends K, ? extends V> multimap) {
    466       return (SetMultimap<K, V>) super.build(multimap);
    467     }
    468   }
    469 
    470   /**
    471    * A specialization of {@link MultimapBuilder} that generates {@link SortedSetMultimap} instances.
    472    */
    473   public abstract static class SortedSetMultimapBuilder<K0, V0> extends SetMultimapBuilder<K0, V0> {
    474     SortedSetMultimapBuilder() {}
    475 
    476     @Override
    477     public abstract <K extends K0, V extends V0> SortedSetMultimap<K, V> build();
    478 
    479     @Override
    480     public <K extends K0, V extends V0> SortedSetMultimap<K, V> build(
    481         Multimap<? extends K, ? extends V> multimap) {
    482       return (SortedSetMultimap<K, V>) super.build(multimap);
    483     }
    484   }
    485 }
    486