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}