001/*
002 * Copyright (C) 2013 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package com.google.common.collect;
018
019import static com.google.common.base.Preconditions.checkNotNull;
020import static com.google.common.collect.CollectPreconditions.checkNonnegative;
021
022import com.google.common.annotations.GwtCompatible;
023import com.google.common.annotations.GwtIncompatible;
024import com.google.common.annotations.J2ktIncompatible;
025import com.google.common.base.Supplier;
026import java.io.Serializable;
027import java.util.ArrayList;
028import java.util.Collection;
029import java.util.Comparator;
030import java.util.EnumMap;
031import java.util.EnumSet;
032import java.util.LinkedList;
033import java.util.List;
034import java.util.Map;
035import java.util.Set;
036import java.util.SortedSet;
037import java.util.TreeMap;
038import java.util.TreeSet;
039import org.jspecify.annotations.Nullable;
040
041/**
042 * An immutable builder for {@link Multimap} instances, letting you independently select the desired
043 * behaviors (for example, ordering) of the backing map and value-collections. Example:
044 *
045 * {@snippet :
046 * ListMultimap<UserId, ErrorResponse> errorsByUser =
047 *     MultimapBuilder.linkedHashKeys().arrayListValues().build();
048 * SortedSetMultimap<String, Method> methodsForName =
049 *     MultimapBuilder.treeKeys().treeSetValues(this::compareMethods).build();
050 * }
051 *
052 * <p>{@code MultimapBuilder} instances are immutable. Invoking a configuration method has no effect
053 * on the receiving instance; you must store and use the new builder instance it returns instead.
054 *
055 * <p>The generated multimaps are serializable if the key and value types are serializable, unless
056 * stated otherwise in one of the configuration methods.
057 *
058 * @author Louis Wasserman
059 * @param <K0> An upper bound on the key type of the generated multimap.
060 * @param <V0> An upper bound on the value type of the generated multimap.
061 * @since 16.0
062 */
063@GwtCompatible
064public abstract class MultimapBuilder<K0 extends @Nullable Object, V0 extends @Nullable Object> {
065  /*
066   * Leaving K and V as upper bounds rather than the actual key and value types allows type
067   * parameters to be left implicit more often. CacheBuilder uses the same technique.
068   */
069
070  private MultimapBuilder() {}
071
072  private static final int DEFAULT_EXPECTED_KEYS = 8;
073
074  /** Uses a hash table to map keys to value collections. */
075  public static MultimapBuilderWithKeys<@Nullable Object> hashKeys() {
076    return hashKeys(DEFAULT_EXPECTED_KEYS);
077  }
078
079  /**
080   * Uses a hash table to map keys to value collections, initialized to expect the specified number
081   * of keys.
082   *
083   * @throws IllegalArgumentException if {@code expectedKeys < 0}
084   */
085  public static MultimapBuilderWithKeys<@Nullable Object> hashKeys(int expectedKeys) {
086    checkNonnegative(expectedKeys, "expectedKeys");
087    return new MultimapBuilderWithKeys<@Nullable Object>() {
088      @Override
089      <K extends @Nullable Object, V extends @Nullable Object> Map<K, Collection<V>> createMap() {
090        return Platform.newHashMapWithExpectedSize(expectedKeys);
091      }
092    };
093  }
094
095  /**
096   * Uses a hash table to map keys to value collections.
097   *
098   * <p>The collections returned by {@link Multimap#keySet()}, {@link Multimap#keys()}, and {@link
099   * Multimap#asMap()} will iterate through the keys in the order that they were first added to the
100   * multimap, save that if all values associated with a key are removed and then the key is added
101   * back into the multimap, that key will come last in the key iteration order.
102   */
103  public static MultimapBuilderWithKeys<@Nullable Object> linkedHashKeys() {
104    return linkedHashKeys(DEFAULT_EXPECTED_KEYS);
105  }
106
107  /**
108   * Uses a hash table to map keys to value collections, initialized to expect the specified number
109   * of keys.
110   *
111   * <p>The collections returned by {@link Multimap#keySet()}, {@link Multimap#keys()}, and {@link
112   * Multimap#asMap()} will iterate through the keys in the order that they were first added to the
113   * multimap, save that if all values associated with a key are removed and then the key is added
114   * back into the multimap, that key will come last in the key iteration order.
115   */
116  public static MultimapBuilderWithKeys<@Nullable Object> linkedHashKeys(int expectedKeys) {
117    checkNonnegative(expectedKeys, "expectedKeys");
118    return new MultimapBuilderWithKeys<@Nullable Object>() {
119      @Override
120      <K extends @Nullable Object, V extends @Nullable Object> Map<K, Collection<V>> createMap() {
121        return Platform.newLinkedHashMapWithExpectedSize(expectedKeys);
122      }
123    };
124  }
125
126  /**
127   * Uses a naturally-ordered {@link TreeMap} to map keys to value collections.
128   *
129   * <p>The collections returned by {@link Multimap#keySet()}, {@link Multimap#keys()}, and {@link
130   * Multimap#asMap()} will iterate through the keys in sorted order.
131   *
132   * <p>For all multimaps generated by the resulting builder, the {@link Multimap#keySet()} can be
133   * safely cast to a {@link java.util.SortedSet}, and the {@link Multimap#asMap()} can safely be
134   * cast to a {@link java.util.SortedMap}.
135   */
136  @SuppressWarnings("rawtypes")
137  public static MultimapBuilderWithKeys<Comparable> treeKeys() {
138    return treeKeys(Ordering.natural());
139  }
140
141  /**
142   * Uses a {@link TreeMap} sorted by the specified comparator to map keys to value collections.
143   *
144   * <p>The collections returned by {@link Multimap#keySet()}, {@link Multimap#keys()}, and {@link
145   * Multimap#asMap()} will iterate through the keys in sorted order.
146   *
147   * <p>For all multimaps generated by the resulting builder, the {@link Multimap#keySet()} can be
148   * safely cast to a {@link java.util.SortedSet}, and the {@link Multimap#asMap()} can safely be
149   * cast to a {@link java.util.SortedMap}.
150   *
151   * <p>Multimaps generated by the resulting builder will not be serializable if {@code comparator}
152   * is not serializable.
153   */
154  public static <K0 extends @Nullable Object> MultimapBuilderWithKeys<K0> treeKeys(
155      Comparator<K0> comparator) {
156    checkNotNull(comparator);
157    return new MultimapBuilderWithKeys<K0>() {
158      @Override
159      <K extends K0, V extends @Nullable Object> Map<K, Collection<V>> createMap() {
160        return new TreeMap<>(comparator);
161      }
162    };
163  }
164
165  /**
166   * Uses an {@link EnumMap} to map keys to value collections.
167   *
168   * @since 16.0
169   */
170  public static <K0 extends Enum<K0>> MultimapBuilderWithKeys<K0> enumKeys(Class<K0> keyClass) {
171    checkNotNull(keyClass);
172    return new MultimapBuilderWithKeys<K0>() {
173      @SuppressWarnings("unchecked")
174      @Override
175      <K extends K0, V extends @Nullable Object> Map<K, Collection<V>> createMap() {
176        // K must actually be K0, since enums are effectively final
177        // (their subclasses are inaccessible)
178        return (Map<K, Collection<V>>) new EnumMap<K0, Collection<V>>(keyClass);
179      }
180    };
181  }
182
183  private static final class ArrayListSupplier<V extends @Nullable Object>
184      implements Supplier<List<V>>, Serializable {
185    private final int expectedValuesPerKey;
186
187    ArrayListSupplier(int expectedValuesPerKey) {
188      this.expectedValuesPerKey = checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey");
189    }
190
191    @Override
192    public List<V> get() {
193      return new ArrayList<>(expectedValuesPerKey);
194    }
195
196    @GwtIncompatible @J2ktIncompatible     private static final long serialVersionUID = 5789124452413809038L;
197  }
198
199  private enum LinkedListSupplier implements Supplier<List<?>> {
200    INSTANCE;
201
202    static <V extends @Nullable Object> Supplier<List<V>> instance() {
203      // Each call generates a fresh LinkedList, which can serve as a List<V> for any V.
204      @SuppressWarnings({"rawtypes", "unchecked"})
205      Supplier<List<V>> result = (Supplier) INSTANCE;
206      return result;
207    }
208
209    @Override
210    // We recommend against linkedListValues but need to keep it for compatibility.
211    @SuppressWarnings("JdkObsolete")
212    public List<?> get() {
213      return new LinkedList<>();
214    }
215  }
216
217  private static final class HashSetSupplier<V extends @Nullable Object>
218      implements Supplier<Set<V>>, Serializable {
219    private final int expectedValuesPerKey;
220
221    HashSetSupplier(int expectedValuesPerKey) {
222      this.expectedValuesPerKey = checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey");
223    }
224
225    @Override
226    public Set<V> get() {
227      return Platform.newHashSetWithExpectedSize(expectedValuesPerKey);
228    }
229
230    @GwtIncompatible @J2ktIncompatible     private static final long serialVersionUID = 6136599187613520511L;
231  }
232
233  private static final class LinkedHashSetSupplier<V extends @Nullable Object>
234      implements Supplier<Set<V>>, Serializable {
235    private final int expectedValuesPerKey;
236
237    LinkedHashSetSupplier(int expectedValuesPerKey) {
238      this.expectedValuesPerKey = checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey");
239    }
240
241    @Override
242    public Set<V> get() {
243      return Platform.newLinkedHashSetWithExpectedSize(expectedValuesPerKey);
244    }
245
246    @GwtIncompatible @J2ktIncompatible     private static final long serialVersionUID = -1886791952729442050L;
247  }
248
249  private static final class TreeSetSupplier<V extends @Nullable Object>
250      implements Supplier<SortedSet<V>>, Serializable {
251    private final Comparator<? super V> comparator;
252
253    TreeSetSupplier(Comparator<? super V> comparator) {
254      this.comparator = checkNotNull(comparator);
255    }
256
257    @Override
258    public SortedSet<V> get() {
259      return new TreeSet<>(comparator);
260    }
261
262    @GwtIncompatible @J2ktIncompatible     private static final long serialVersionUID = 7297924014757663263L;
263  }
264
265  private static final class EnumSetSupplier<V extends Enum<V>>
266      implements Supplier<Set<V>>, Serializable {
267    private final Class<V> clazz;
268
269    EnumSetSupplier(Class<V> clazz) {
270      this.clazz = checkNotNull(clazz);
271    }
272
273    @Override
274    public Set<V> get() {
275      return EnumSet.noneOf(clazz);
276    }
277
278    @GwtIncompatible @J2ktIncompatible     private static final long serialVersionUID = -6321539988632337635L;
279  }
280
281  /**
282   * An intermediate stage in a {@link MultimapBuilder} in which the key-value collection map
283   * implementation has been specified, but the value collection implementation has not.
284   *
285   * @param <K0> The upper bound on the key type of the generated multimap.
286   * @since 16.0
287   */
288  public abstract static class MultimapBuilderWithKeys<K0 extends @Nullable Object> {
289
290    private static final int DEFAULT_EXPECTED_VALUES_PER_KEY = 2;
291
292    MultimapBuilderWithKeys() {}
293
294    abstract <K extends K0, V extends @Nullable Object> Map<K, Collection<V>> createMap();
295
296    /** Uses an {@link ArrayList} to store value collections. */
297    public final ListMultimapBuilder<K0, @Nullable Object> arrayListValues() {
298      return arrayListValues(DEFAULT_EXPECTED_VALUES_PER_KEY);
299    }
300
301    /**
302     * Uses an {@link ArrayList} to store value collections, initialized to expect the specified
303     * number of values per key.
304     *
305     * @throws IllegalArgumentException if {@code expectedValuesPerKey < 0}
306     */
307    public final ListMultimapBuilder<K0, @Nullable Object> arrayListValues(
308        int expectedValuesPerKey) {
309      checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey");
310      return new ListMultimapBuilder<K0, @Nullable Object>() {
311        @Override
312        public <K extends K0, V extends @Nullable Object> ListMultimap<K, V> build() {
313          return Multimaps.newListMultimap(
314              MultimapBuilderWithKeys.this.createMap(),
315              new ArrayListSupplier<V>(expectedValuesPerKey));
316        }
317      };
318    }
319
320    /**
321     * Uses a {@link LinkedList} to store value collections.
322     *
323     * <p><b>Performance note:</b> {@link ArrayList} and {@link java.util.ArrayDeque} consistently
324     * outperform {@code LinkedList} except in certain rare and specific situations. Unless you have
325     * spent a lot of time benchmarking your specific needs, use one of those instead. (However, we
326     * do not currently offer a {@link Multimap} implementation based on {@link
327     * java.util.ArrayDeque}.)
328     */
329    public final ListMultimapBuilder<K0, @Nullable Object> linkedListValues() {
330      return new ListMultimapBuilder<K0, @Nullable Object>() {
331        @Override
332        public <K extends K0, V extends @Nullable Object> ListMultimap<K, V> build() {
333          return Multimaps.newListMultimap(
334              MultimapBuilderWithKeys.this.createMap(), LinkedListSupplier.instance());
335        }
336      };
337    }
338
339    /** Uses a hash-based {@code Set} to store value collections. */
340    public final SetMultimapBuilder<K0, @Nullable Object> hashSetValues() {
341      return hashSetValues(DEFAULT_EXPECTED_VALUES_PER_KEY);
342    }
343
344    /**
345     * Uses a hash-based {@code Set} to store value collections, initialized to expect the specified
346     * number of values per key.
347     *
348     * @throws IllegalArgumentException if {@code expectedValuesPerKey < 0}
349     */
350    public final SetMultimapBuilder<K0, @Nullable Object> hashSetValues(int expectedValuesPerKey) {
351      checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey");
352      return new SetMultimapBuilder<K0, @Nullable Object>() {
353        @Override
354        public <K extends K0, V extends @Nullable Object> SetMultimap<K, V> build() {
355          return Multimaps.newSetMultimap(
356              MultimapBuilderWithKeys.this.createMap(),
357              new HashSetSupplier<V>(expectedValuesPerKey));
358        }
359      };
360    }
361
362    /** Uses an insertion-ordered hash-based {@code Set} to store value collections. */
363    public final SetMultimapBuilder<K0, @Nullable Object> linkedHashSetValues() {
364      return linkedHashSetValues(DEFAULT_EXPECTED_VALUES_PER_KEY);
365    }
366
367    /**
368     * Uses an insertion-ordered hash-based {@code Set} to store value collections, initialized to
369     * expect the specified number of values per key.
370     *
371     * @throws IllegalArgumentException if {@code expectedValuesPerKey < 0}
372     */
373    public final SetMultimapBuilder<K0, @Nullable Object> linkedHashSetValues(
374        int expectedValuesPerKey) {
375      checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey");
376      return new SetMultimapBuilder<K0, @Nullable Object>() {
377        @Override
378        public <K extends K0, V extends @Nullable Object> SetMultimap<K, V> build() {
379          return Multimaps.newSetMultimap(
380              MultimapBuilderWithKeys.this.createMap(),
381              new LinkedHashSetSupplier<V>(expectedValuesPerKey));
382        }
383      };
384    }
385
386    /** Uses a naturally-ordered {@link TreeSet} to store value collections. */
387    @SuppressWarnings("rawtypes")
388    public final SortedSetMultimapBuilder<K0, Comparable> treeSetValues() {
389      return treeSetValues(Ordering.natural());
390    }
391
392    /**
393     * Uses a {@link TreeSet} ordered by the specified comparator to store value collections.
394     *
395     * <p>Multimaps generated by the resulting builder will not be serializable if {@code
396     * comparator} is not serializable.
397     */
398    public final <V0 extends @Nullable Object> SortedSetMultimapBuilder<K0, V0> treeSetValues(
399        Comparator<V0> comparator) {
400      checkNotNull(comparator, "comparator");
401      return new SortedSetMultimapBuilder<K0, V0>() {
402        @Override
403        public <K extends K0, V extends V0> SortedSetMultimap<K, V> build() {
404          return Multimaps.newSortedSetMultimap(
405              MultimapBuilderWithKeys.this.createMap(), new TreeSetSupplier<V>(comparator));
406        }
407      };
408    }
409
410    /** Uses an {@link EnumSet} to store value collections. */
411    public final <V0 extends Enum<V0>> SetMultimapBuilder<K0, V0> enumSetValues(
412        Class<V0> valueClass) {
413      checkNotNull(valueClass, "valueClass");
414      return new SetMultimapBuilder<K0, V0>() {
415        @Override
416        public <K extends K0, V extends V0> SetMultimap<K, V> build() {
417          // V must actually be V0, since enums are effectively final
418          // (their subclasses are inaccessible)
419          @SuppressWarnings({"unchecked", "rawtypes"})
420          Supplier<Set<V>> factory = (Supplier) new EnumSetSupplier<V0>(valueClass);
421          return Multimaps.newSetMultimap(MultimapBuilderWithKeys.this.createMap(), factory);
422        }
423      };
424    }
425  }
426
427  /** Returns a new, empty {@code Multimap} with the specified implementation. */
428  public abstract <K extends K0, V extends V0> Multimap<K, V> build();
429
430  /**
431   * Returns a {@code Multimap} with the specified implementation, initialized with the entries of
432   * {@code multimap}.
433   */
434  public <K extends K0, V extends V0> Multimap<K, V> build(
435      Multimap<? extends K, ? extends V> multimap) {
436    Multimap<K, V> result = build();
437    result.putAll(multimap);
438    return result;
439  }
440
441  /**
442   * A specialization of {@link MultimapBuilder} that generates {@link ListMultimap} instances.
443   *
444   * @since 16.0
445   */
446  public abstract static class ListMultimapBuilder<
447          K0 extends @Nullable Object, V0 extends @Nullable Object>
448      extends MultimapBuilder<K0, V0> {
449    ListMultimapBuilder() {}
450
451    @Override
452    public abstract <K extends K0, V extends V0> ListMultimap<K, V> build();
453
454    @Override
455    public <K extends K0, V extends V0> ListMultimap<K, V> build(
456        Multimap<? extends K, ? extends V> multimap) {
457      return (ListMultimap<K, V>) super.<K, V>build(multimap);
458    }
459  }
460
461  /**
462   * A specialization of {@link MultimapBuilder} that generates {@link SetMultimap} instances.
463   *
464   * @since 16.0
465   */
466  public abstract static class SetMultimapBuilder<
467          K0 extends @Nullable Object, V0 extends @Nullable Object>
468      extends MultimapBuilder<K0, V0> {
469    SetMultimapBuilder() {}
470
471    @Override
472    public abstract <K extends K0, V extends V0> SetMultimap<K, V> build();
473
474    @Override
475    public <K extends K0, V extends V0> SetMultimap<K, V> build(
476        Multimap<? extends K, ? extends V> multimap) {
477      return (SetMultimap<K, V>) super.<K, V>build(multimap);
478    }
479  }
480
481  /**
482   * A specialization of {@link MultimapBuilder} that generates {@link SortedSetMultimap} instances.
483   *
484   * @since 16.0
485   */
486  public abstract static class SortedSetMultimapBuilder<
487          K0 extends @Nullable Object, V0 extends @Nullable Object>
488      extends SetMultimapBuilder<K0, V0> {
489    SortedSetMultimapBuilder() {}
490
491    @Override
492    public abstract <K extends K0, V extends V0> SortedSetMultimap<K, V> build();
493
494    @Override
495    public <K extends K0, V extends V0> SortedSetMultimap<K, V> build(
496        Multimap<? extends K, ? extends V> multimap) {
497      return (SortedSetMultimap<K, V>) super.<K, V>build(multimap);
498    }
499  }
500}