Coverage Summary for Class: ImmutableListMultimap (com.google.common.collect)

Class Method, % Line, %
ImmutableListMultimap 25% (5/20) 22.3% (21/94)
ImmutableListMultimap$Builder 27.3% (3/11) 20% (4/20)
Total 25.8% (8/31) 21.9% (25/114)


1 /* 2  * Copyright (C) 2008 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  20 import com.google.common.annotations.Beta; 21 import com.google.common.annotations.GwtCompatible; 22 import com.google.common.annotations.GwtIncompatible; 23 import com.google.errorprone.annotations.CanIgnoreReturnValue; 24 import com.google.errorprone.annotations.DoNotCall; 25 import com.google.errorprone.annotations.concurrent.LazyInit; 26 import com.google.j2objc.annotations.RetainedWith; 27 import java.io.IOException; 28 import java.io.InvalidObjectException; 29 import java.io.ObjectInputStream; 30 import java.io.ObjectOutputStream; 31 import java.util.Collection; 32 import java.util.Comparator; 33 import java.util.Map; 34 import java.util.Map.Entry; 35 import java.util.function.Function; 36 import java.util.stream.Collector; 37 import java.util.stream.Stream; 38 import org.checkerframework.checker.nullness.qual.Nullable; 39  40 /** 41  * A {@link ListMultimap} whose contents will never change, with many other important properties 42  * detailed at {@link ImmutableCollection}. 43  * 44  * <p>See the Guava User Guide article on <a href= 45  * "https://github.com/google/guava/wiki/ImmutableCollectionsExplained"> immutable collections</a>. 46  * 47  * @author Jared Levy 48  * @since 2.0 49  */ 50 @GwtCompatible(serializable = true, emulated = true) 51 public class ImmutableListMultimap<K, V> extends ImmutableMultimap<K, V> 52  implements ListMultimap<K, V> { 53  /** 54  * Returns a {@link Collector} that accumulates elements into an {@code ImmutableListMultimap} 55  * whose keys and values are the result of applying the provided mapping functions to the input 56  * elements. 57  * 58  * <p>For streams with defined encounter order (as defined in the Ordering section of the {@link 59  * java.util.stream} Javadoc), that order is preserved, but entries are <a 60  * href="ImmutableMultimap.html#iteration">grouped by key</a>. 61  * 62  * <p>Example: 63  * 64  * <pre>{@code 65  * static final Multimap<Character, String> FIRST_LETTER_MULTIMAP = 66  * Stream.of("banana", "apple", "carrot", "asparagus", "cherry") 67  * .collect(toImmutableListMultimap(str -> str.charAt(0), str -> str.substring(1))); 68  * 69  * // is equivalent to 70  * 71  * static final Multimap<Character, String> FIRST_LETTER_MULTIMAP = 72  * new ImmutableListMultimap.Builder<Character, String>() 73  * .put('b', "anana") 74  * .putAll('a', "pple", "sparagus") 75  * .putAll('c', "arrot", "herry") 76  * .build(); 77  * }</pre> 78  * 79  * @since 21.0 80  */ 81  public static <T, K, V> Collector<T, ?, ImmutableListMultimap<K, V>> toImmutableListMultimap( 82  Function<? super T, ? extends K> keyFunction, 83  Function<? super T, ? extends V> valueFunction) { 84  return CollectCollectors.toImmutableListMultimap(keyFunction, valueFunction); 85  } 86  87  /** 88  * Returns a {@code Collector} accumulating entries into an {@code ImmutableListMultimap}. Each 89  * input element is mapped to a key and a stream of values, each of which are put into the 90  * resulting {@code Multimap}, in the encounter order of the stream and the encounter order of the 91  * streams of values. 92  * 93  * <p>Example: 94  * 95  * <pre>{@code 96  * static final ImmutableListMultimap<Character, Character> FIRST_LETTER_MULTIMAP = 97  * Stream.of("banana", "apple", "carrot", "asparagus", "cherry") 98  * .collect( 99  * flatteningToImmutableListMultimap( 100  * str -> str.charAt(0), 101  * str -> str.substring(1).chars().mapToObj(c -> (char) c)); 102  * 103  * // is equivalent to 104  * 105  * static final ImmutableListMultimap<Character, Character> FIRST_LETTER_MULTIMAP = 106  * ImmutableListMultimap.<Character, Character>builder() 107  * .putAll('b', Arrays.asList('a', 'n', 'a', 'n', 'a')) 108  * .putAll('a', Arrays.asList('p', 'p', 'l', 'e')) 109  * .putAll('c', Arrays.asList('a', 'r', 'r', 'o', 't')) 110  * .putAll('a', Arrays.asList('s', 'p', 'a', 'r', 'a', 'g', 'u', 's')) 111  * .putAll('c', Arrays.asList('h', 'e', 'r', 'r', 'y')) 112  * .build(); 113  * } 114  * }</pre> 115  * 116  * @since 21.0 117  */ 118  public static <T, K, V> 119  Collector<T, ?, ImmutableListMultimap<K, V>> flatteningToImmutableListMultimap( 120  Function<? super T, ? extends K> keyFunction, 121  Function<? super T, ? extends Stream<? extends V>> valuesFunction) { 122  return CollectCollectors.flatteningToImmutableListMultimap(keyFunction, valuesFunction); 123  } 124  125  /** 126  * Returns the empty multimap. 127  * 128  * <p><b>Performance note:</b> the instance returned is a singleton. 129  */ 130  // Casting is safe because the multimap will never hold any elements. 131  @SuppressWarnings("unchecked") 132  public static <K, V> ImmutableListMultimap<K, V> of() { 133  return (ImmutableListMultimap<K, V>) EmptyImmutableListMultimap.INSTANCE; 134  } 135  136  /** Returns an immutable multimap containing a single entry. */ 137  public static <K, V> ImmutableListMultimap<K, V> of(K k1, V v1) { 138  ImmutableListMultimap.Builder<K, V> builder = ImmutableListMultimap.builder(); 139  builder.put(k1, v1); 140  return builder.build(); 141  } 142  143  /** Returns an immutable multimap containing the given entries, in order. */ 144  public static <K, V> ImmutableListMultimap<K, V> of(K k1, V v1, K k2, V v2) { 145  ImmutableListMultimap.Builder<K, V> builder = ImmutableListMultimap.builder(); 146  builder.put(k1, v1); 147  builder.put(k2, v2); 148  return builder.build(); 149  } 150  151  /** Returns an immutable multimap containing the given entries, in order. */ 152  public static <K, V> ImmutableListMultimap<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3) { 153  ImmutableListMultimap.Builder<K, V> builder = ImmutableListMultimap.builder(); 154  builder.put(k1, v1); 155  builder.put(k2, v2); 156  builder.put(k3, v3); 157  return builder.build(); 158  } 159  160  /** Returns an immutable multimap containing the given entries, in order. */ 161  public static <K, V> ImmutableListMultimap<K, V> of( 162  K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { 163  ImmutableListMultimap.Builder<K, V> builder = ImmutableListMultimap.builder(); 164  builder.put(k1, v1); 165  builder.put(k2, v2); 166  builder.put(k3, v3); 167  builder.put(k4, v4); 168  return builder.build(); 169  } 170  171  /** Returns an immutable multimap containing the given entries, in order. */ 172  public static <K, V> ImmutableListMultimap<K, V> of( 173  K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { 174  ImmutableListMultimap.Builder<K, V> builder = ImmutableListMultimap.builder(); 175  builder.put(k1, v1); 176  builder.put(k2, v2); 177  builder.put(k3, v3); 178  builder.put(k4, v4); 179  builder.put(k5, v5); 180  return builder.build(); 181  } 182  183  // looking for of() with > 5 entries? Use the builder instead. 184  185  /** 186  * Returns a new builder. The generated builder is equivalent to the builder created by the {@link 187  * Builder} constructor. 188  */ 189  public static <K, V> Builder<K, V> builder() { 190  return new Builder<>(); 191  } 192  193  /** 194  * A builder for creating immutable {@code ListMultimap} instances, especially {@code public 195  * static final} multimaps ("constant multimaps"). Example: 196  * 197  * <pre>{@code 198  * static final Multimap<String, Integer> STRING_TO_INTEGER_MULTIMAP = 199  * new ImmutableListMultimap.Builder<String, Integer>() 200  * .put("one", 1) 201  * .putAll("several", 1, 2, 3) 202  * .putAll("many", 1, 2, 3, 4, 5) 203  * .build(); 204  * }</pre> 205  * 206  * <p>Builder instances can be reused; it is safe to call {@link #build} multiple times to build 207  * multiple multimaps in series. Each multimap contains the key-value mappings in the previously 208  * created multimaps. 209  * 210  * @since 2.0 211  */ 212  public static final class Builder<K, V> extends ImmutableMultimap.Builder<K, V> { 213  /** 214  * Creates a new builder. The returned builder is equivalent to the builder generated by {@link 215  * ImmutableListMultimap#builder}. 216  */ 217  public Builder() {} 218  219  @CanIgnoreReturnValue 220  @Override 221  public Builder<K, V> put(K key, V value) { 222  super.put(key, value); 223  return this; 224  } 225  226  /** 227  * {@inheritDoc} 228  * 229  * @since 11.0 230  */ 231  @CanIgnoreReturnValue 232  @Override 233  public Builder<K, V> put(Entry<? extends K, ? extends V> entry) { 234  super.put(entry); 235  return this; 236  } 237  238  /** 239  * {@inheritDoc} 240  * 241  * @since 19.0 242  */ 243  @CanIgnoreReturnValue 244  @Beta 245  @Override 246  public Builder<K, V> putAll(Iterable<? extends Entry<? extends K, ? extends V>> entries) { 247  super.putAll(entries); 248  return this; 249  } 250  251  @CanIgnoreReturnValue 252  @Override 253  public Builder<K, V> putAll(K key, Iterable<? extends V> values) { 254  super.putAll(key, values); 255  return this; 256  } 257  258  @CanIgnoreReturnValue 259  @Override 260  public Builder<K, V> putAll(K key, V... values) { 261  super.putAll(key, values); 262  return this; 263  } 264  265  @CanIgnoreReturnValue 266  @Override 267  public Builder<K, V> putAll(Multimap<? extends K, ? extends V> multimap) { 268  super.putAll(multimap); 269  return this; 270  } 271  272  @CanIgnoreReturnValue 273  @Override 274  Builder<K, V> combine(ImmutableMultimap.Builder<K, V> other) { 275  super.combine(other); 276  return this; 277  } 278  279  /** 280  * {@inheritDoc} 281  * 282  * @since 8.0 283  */ 284  @CanIgnoreReturnValue 285  @Override 286  public Builder<K, V> orderKeysBy(Comparator<? super K> keyComparator) { 287  super.orderKeysBy(keyComparator); 288  return this; 289  } 290  291  /** 292  * {@inheritDoc} 293  * 294  * @since 8.0 295  */ 296  @CanIgnoreReturnValue 297  @Override 298  public Builder<K, V> orderValuesBy(Comparator<? super V> valueComparator) { 299  super.orderValuesBy(valueComparator); 300  return this; 301  } 302  303  /** Returns a newly-created immutable list multimap. */ 304  @Override 305  public ImmutableListMultimap<K, V> build() { 306  return (ImmutableListMultimap<K, V>) super.build(); 307  } 308  } 309  310  /** 311  * Returns an immutable multimap containing the same mappings as {@code multimap}. The generated 312  * multimap's key and value orderings correspond to the iteration ordering of the {@code 313  * multimap.asMap()} view. 314  * 315  * <p>Despite the method name, this method attempts to avoid actually copying the data when it is 316  * safe to do so. The exact circumstances under which a copy will or will not be performed are 317  * undocumented and subject to change. 318  * 319  * @throws NullPointerException if any key or value in {@code multimap} is null 320  */ 321  public static <K, V> ImmutableListMultimap<K, V> copyOf( 322  Multimap<? extends K, ? extends V> multimap) { 323  if (multimap.isEmpty()) { 324  return of(); 325  } 326  327  // TODO(lowasser): copy ImmutableSetMultimap by using asList() on the sets 328  if (multimap instanceof ImmutableListMultimap) { 329  @SuppressWarnings("unchecked") // safe since multimap is not writable 330  ImmutableListMultimap<K, V> kvMultimap = (ImmutableListMultimap<K, V>) multimap; 331  if (!kvMultimap.isPartialView()) { 332  return kvMultimap; 333  } 334  } 335  336  return fromMapEntries(multimap.asMap().entrySet(), null); 337  } 338  339  /** 340  * Returns an immutable multimap containing the specified entries. The returned multimap iterates 341  * over keys in the order they were first encountered in the input, and the values for each key 342  * are iterated in the order they were encountered. 343  * 344  * @throws NullPointerException if any key, value, or entry is null 345  * @since 19.0 346  */ 347  @Beta 348  public static <K, V> ImmutableListMultimap<K, V> copyOf( 349  Iterable<? extends Entry<? extends K, ? extends V>> entries) { 350  return new Builder<K, V>().putAll(entries).build(); 351  } 352  353  /** Creates an ImmutableListMultimap from an asMap.entrySet. */ 354  static <K, V> ImmutableListMultimap<K, V> fromMapEntries( 355  Collection<? extends Map.Entry<? extends K, ? extends Collection<? extends V>>> mapEntries, 356  @Nullable Comparator<? super V> valueComparator) { 357  if (mapEntries.isEmpty()) { 358  return of(); 359  } 360  ImmutableMap.Builder<K, ImmutableList<V>> builder = 361  new ImmutableMap.Builder<>(mapEntries.size()); 362  int size = 0; 363  364  for (Entry<? extends K, ? extends Collection<? extends V>> entry : mapEntries) { 365  K key = entry.getKey(); 366  Collection<? extends V> values = entry.getValue(); 367  ImmutableList<V> list = 368  (valueComparator == null) 369  ? ImmutableList.copyOf(values) 370  : ImmutableList.sortedCopyOf(valueComparator, values); 371  if (!list.isEmpty()) { 372  builder.put(key, list); 373  size += list.size(); 374  } 375  } 376  377  return new ImmutableListMultimap<>(builder.build(), size); 378  } 379  380  ImmutableListMultimap(ImmutableMap<K, ImmutableList<V>> map, int size) { 381  super(map, size); 382  } 383  384  // views 385  386  /** 387  * Returns an immutable list of the values for the given key. If no mappings in the multimap have 388  * the provided key, an empty immutable list is returned. The values are in the same order as the 389  * parameters used to build this multimap. 390  */ 391  @Override 392  public ImmutableList<V> get(@Nullable K key) { 393  // This cast is safe as its type is known in constructor. 394  ImmutableList<V> list = (ImmutableList<V>) map.get(key); 395  return (list == null) ? ImmutableList.<V>of() : list; 396  } 397  398  @LazyInit @RetainedWith private transient ImmutableListMultimap<V, K> inverse; 399  400  /** 401  * {@inheritDoc} 402  * 403  * <p>Because an inverse of a list multimap can contain multiple pairs with the same key and 404  * value, this method returns an {@code ImmutableListMultimap} rather than the {@code 405  * ImmutableMultimap} specified in the {@code ImmutableMultimap} class. 406  * 407  * @since 11.0 408  */ 409  @Override 410  public ImmutableListMultimap<V, K> inverse() { 411  ImmutableListMultimap<V, K> result = inverse; 412  return (result == null) ? (inverse = invert()) : result; 413  } 414  415  private ImmutableListMultimap<V, K> invert() { 416  Builder<V, K> builder = builder(); 417  for (Entry<K, V> entry : entries()) { 418  builder.put(entry.getValue(), entry.getKey()); 419  } 420  ImmutableListMultimap<V, K> invertedMultimap = builder.build(); 421  invertedMultimap.inverse = this; 422  return invertedMultimap; 423  } 424  425  /** 426  * Guaranteed to throw an exception and leave the multimap unmodified. 427  * 428  * @throws UnsupportedOperationException always 429  * @deprecated Unsupported operation. 430  */ 431  @CanIgnoreReturnValue 432  @Deprecated 433  @Override 434  @DoNotCall("Always throws UnsupportedOperationException") 435  public final ImmutableList<V> removeAll(Object key) { 436  throw new UnsupportedOperationException(); 437  } 438  439  /** 440  * Guaranteed to throw an exception and leave the multimap unmodified. 441  * 442  * @throws UnsupportedOperationException always 443  * @deprecated Unsupported operation. 444  */ 445  @CanIgnoreReturnValue 446  @Deprecated 447  @Override 448  @DoNotCall("Always throws UnsupportedOperationException") 449  public final ImmutableList<V> replaceValues(K key, Iterable<? extends V> values) { 450  throw new UnsupportedOperationException(); 451  } 452  453  /** 454  * @serialData number of distinct keys, and then for each distinct key: the key, the number of 455  * values for that key, and the key's values 456  */ 457  @GwtIncompatible // java.io.ObjectOutputStream 458  private void writeObject(ObjectOutputStream stream) throws IOException { 459  stream.defaultWriteObject(); 460  Serialization.writeMultimap(this, stream); 461  } 462  463  @GwtIncompatible // java.io.ObjectInputStream 464  private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { 465  stream.defaultReadObject(); 466  int keyCount = stream.readInt(); 467  if (keyCount < 0) { 468  throw new InvalidObjectException("Invalid key count " + keyCount); 469  } 470  ImmutableMap.Builder<Object, ImmutableList<Object>> builder = ImmutableMap.builder(); 471  int tmpSize = 0; 472  473  for (int i = 0; i < keyCount; i++) { 474  Object key = stream.readObject(); 475  int valueCount = stream.readInt(); 476  if (valueCount <= 0) { 477  throw new InvalidObjectException("Invalid value count " + valueCount); 478  } 479  480  ImmutableList.Builder<Object> valuesBuilder = ImmutableList.builder(); 481  for (int j = 0; j < valueCount; j++) { 482  valuesBuilder.add(stream.readObject()); 483  } 484  builder.put(key, valuesBuilder.build()); 485  tmpSize += valueCount; 486  } 487  488  ImmutableMap<Object, ImmutableList<Object>> tmpMap; 489  try { 490  tmpMap = builder.build(); 491  } catch (IllegalArgumentException e) { 492  throw (InvalidObjectException) new InvalidObjectException(e.getMessage()).initCause(e); 493  } 494  495  FieldSettersHolder.MAP_FIELD_SETTER.set(this, tmpMap); 496  FieldSettersHolder.SIZE_FIELD_SETTER.set(this, tmpSize); 497  } 498  499  @GwtIncompatible // Not needed in emulated source 500  private static final long serialVersionUID = 0; 501 }