001/* 002 * Copyright (C) 2009 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 java.util.Arrays.asList; 021 022import com.google.common.annotations.GwtCompatible; 023import com.google.common.annotations.GwtIncompatible; 024import com.google.common.base.MoreObjects; 025 026import java.io.IOException; 027import java.io.InvalidObjectException; 028import java.io.ObjectInputStream; 029import java.io.ObjectOutputStream; 030import java.util.Arrays; 031import java.util.Collection; 032import java.util.Collections; 033import java.util.Comparator; 034import java.util.LinkedHashMap; 035import java.util.List; 036import java.util.Map; 037import java.util.Map.Entry; 038 039import javax.annotation.Nullable; 040 041/** 042 * An immutable {@link SetMultimap} with reliable user-specified key and value 043 * iteration order. Does not permit null keys or values. 044 * 045 * <p>Unlike {@link Multimaps#unmodifiableSetMultimap(SetMultimap)}, which is 046 * a <i>view</i> of a separate multimap which can still change, an instance of 047 * {@code ImmutableSetMultimap} contains its own data and will <i>never</i> 048 * change. {@code ImmutableSetMultimap} is convenient for 049 * {@code public static final} multimaps ("constant multimaps") and also lets 050 * you easily make a "defensive copy" of a multimap provided to your class by 051 * a caller. 052 * 053 * <p><b>Note:</b> Although this class is not final, it cannot be subclassed as 054 * it has no public or protected constructors. Thus, instances of this class 055 * are guaranteed to be immutable. 056 * 057 * <p>See the Guava User Guide article on <a href= 058 * "http://code.google.com/p/guava-libraries/wiki/ImmutableCollectionsExplained"> 059 * immutable collections</a>. 060 * 061 * @author Mike Ward 062 * @since 2.0 (imported from Google Collections Library) 063 */ 064@GwtCompatible(serializable = true, emulated = true) 065public class ImmutableSetMultimap<K, V> 066 extends ImmutableMultimap<K, V> 067 implements SetMultimap<K, V> { 068 069 /** Returns the empty multimap. */ 070 // Casting is safe because the multimap will never hold any elements. 071 @SuppressWarnings("unchecked") 072 public static <K, V> ImmutableSetMultimap<K, V> of() { 073 return (ImmutableSetMultimap<K, V>) EmptyImmutableSetMultimap.INSTANCE; 074 } 075 076 /** 077 * Returns an immutable multimap containing a single entry. 078 */ 079 public static <K, V> ImmutableSetMultimap<K, V> of(K k1, V v1) { 080 ImmutableSetMultimap.Builder<K, V> builder = ImmutableSetMultimap.builder(); 081 builder.put(k1, v1); 082 return builder.build(); 083 } 084 085 /** 086 * Returns an immutable multimap containing the given entries, in order. 087 * Repeated occurrences of an entry (according to {@link Object#equals}) after 088 * the first are ignored. 089 */ 090 public static <K, V> ImmutableSetMultimap<K, V> of(K k1, V v1, K k2, V v2) { 091 ImmutableSetMultimap.Builder<K, V> builder = ImmutableSetMultimap.builder(); 092 builder.put(k1, v1); 093 builder.put(k2, v2); 094 return builder.build(); 095 } 096 097 /** 098 * Returns an immutable multimap containing the given entries, in order. 099 * Repeated occurrences of an entry (according to {@link Object#equals}) after 100 * the first are ignored. 101 */ 102 public static <K, V> ImmutableSetMultimap<K, V> of( 103 K k1, V v1, K k2, V v2, K k3, V v3) { 104 ImmutableSetMultimap.Builder<K, V> builder = ImmutableSetMultimap.builder(); 105 builder.put(k1, v1); 106 builder.put(k2, v2); 107 builder.put(k3, v3); 108 return builder.build(); 109 } 110 111 /** 112 * Returns an immutable multimap containing the given entries, in order. 113 * Repeated occurrences of an entry (according to {@link Object#equals}) after 114 * the first are ignored. 115 */ 116 public static <K, V> ImmutableSetMultimap<K, V> of( 117 K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { 118 ImmutableSetMultimap.Builder<K, V> builder = ImmutableSetMultimap.builder(); 119 builder.put(k1, v1); 120 builder.put(k2, v2); 121 builder.put(k3, v3); 122 builder.put(k4, v4); 123 return builder.build(); 124 } 125 126 /** 127 * Returns an immutable multimap containing the given entries, in order. 128 * Repeated occurrences of an entry (according to {@link Object#equals}) after 129 * the first are ignored. 130 */ 131 public static <K, V> ImmutableSetMultimap<K, V> of( 132 K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { 133 ImmutableSetMultimap.Builder<K, V> builder = ImmutableSetMultimap.builder(); 134 builder.put(k1, v1); 135 builder.put(k2, v2); 136 builder.put(k3, v3); 137 builder.put(k4, v4); 138 builder.put(k5, v5); 139 return builder.build(); 140 } 141 142 // looking for of() with > 5 entries? Use the builder instead. 143 144 /** 145 * Returns a new {@link Builder}. 146 */ 147 public static <K, V> Builder<K, V> builder() { 148 return new Builder<K, V>(); 149 } 150 151 /** 152 * Multimap for {@link ImmutableSetMultimap.Builder} that maintains key 153 * and value orderings and performs better than {@link LinkedHashMultimap}. 154 */ 155 private static class BuilderMultimap<K, V> extends AbstractMapBasedMultimap<K, V> { 156 BuilderMultimap() { 157 super(new LinkedHashMap<K, Collection<V>>()); 158 } 159 @Override Collection<V> createCollection() { 160 return Sets.newLinkedHashSet(); 161 } 162 private static final long serialVersionUID = 0; 163 } 164 165 /** 166 * A builder for creating immutable {@code SetMultimap} instances, especially 167 * {@code public static final} multimaps ("constant multimaps"). Example: 168 * <pre> {@code 169 * 170 * static final Multimap<String, Integer> STRING_TO_INTEGER_MULTIMAP = 171 * new ImmutableSetMultimap.Builder<String, Integer>() 172 * .put("one", 1) 173 * .putAll("several", 1, 2, 3) 174 * .putAll("many", 1, 2, 3, 4, 5) 175 * .build();}</pre> 176 * 177 * <p>Builder instances can be reused; it is safe to call {@link #build} multiple 178 * times to build multiple multimaps in series. Each multimap contains the 179 * key-value mappings in the previously created multimaps. 180 * 181 * @since 2.0 (imported from Google Collections Library) 182 */ 183 public static final class Builder<K, V> 184 extends ImmutableMultimap.Builder<K, V> { 185 /** 186 * Creates a new builder. The returned builder is equivalent to the builder 187 * generated by {@link ImmutableSetMultimap#builder}. 188 */ 189 public Builder() { 190 builderMultimap = new BuilderMultimap<K, V>(); 191 } 192 193 /** 194 * Adds a key-value mapping to the built multimap if it is not already 195 * present. 196 */ 197 @Override public Builder<K, V> put(K key, V value) { 198 builderMultimap.put(checkNotNull(key), checkNotNull(value)); 199 return this; 200 } 201 202 /** 203 * Adds an entry to the built multimap if it is not already present. 204 * 205 * @since 11.0 206 */ 207 @Override public Builder<K, V> put(Entry<? extends K, ? extends V> entry) { 208 builderMultimap.put( 209 checkNotNull(entry.getKey()), checkNotNull(entry.getValue())); 210 return this; 211 } 212 213 @Override public Builder<K, V> putAll(K key, Iterable<? extends V> values) { 214 Collection<V> collection = builderMultimap.get(checkNotNull(key)); 215 for (V value : values) { 216 collection.add(checkNotNull(value)); 217 } 218 return this; 219 } 220 221 @Override public Builder<K, V> putAll(K key, V... values) { 222 return putAll(key, Arrays.asList(values)); 223 } 224 225 @Override public Builder<K, V> putAll( 226 Multimap<? extends K, ? extends V> multimap) { 227 for (Entry<? extends K, ? extends Collection<? extends V>> entry 228 : multimap.asMap().entrySet()) { 229 putAll(entry.getKey(), entry.getValue()); 230 } 231 return this; 232 } 233 234 /** 235 * {@inheritDoc} 236 * 237 * @since 8.0 238 */ 239 @Override 240 public Builder<K, V> orderKeysBy(Comparator<? super K> keyComparator) { 241 this.keyComparator = checkNotNull(keyComparator); 242 return this; 243 } 244 245 /** 246 * Specifies the ordering of the generated multimap's values for each key. 247 * 248 * <p>If this method is called, the sets returned by the {@code get()} 249 * method of the generated multimap and its {@link Multimap#asMap()} view 250 * are {@link ImmutableSortedSet} instances. However, serialization does not 251 * preserve that property, though it does maintain the key and value 252 * ordering. 253 * 254 * @since 8.0 255 */ 256 // TODO: Make serialization behavior consistent. 257 @Override 258 public Builder<K, V> orderValuesBy(Comparator<? super V> valueComparator) { 259 super.orderValuesBy(valueComparator); 260 return this; 261 } 262 263 /** 264 * Returns a newly-created immutable set multimap. 265 */ 266 @Override public ImmutableSetMultimap<K, V> build() { 267 if (keyComparator != null) { 268 Multimap<K, V> sortedCopy = new BuilderMultimap<K, V>(); 269 List<Map.Entry<K, Collection<V>>> entries = Lists.newArrayList( 270 builderMultimap.asMap().entrySet()); 271 Collections.sort( 272 entries, 273 Ordering.from(keyComparator).<K>onKeys()); 274 for (Map.Entry<K, Collection<V>> entry : entries) { 275 sortedCopy.putAll(entry.getKey(), entry.getValue()); 276 } 277 builderMultimap = sortedCopy; 278 } 279 return copyOf(builderMultimap, valueComparator); 280 } 281 } 282 283 /** 284 * Returns an immutable set multimap containing the same mappings as 285 * {@code multimap}. The generated multimap's key and value orderings 286 * correspond to the iteration ordering of the {@code multimap.asMap()} view. 287 * Repeated occurrences of an entry in the multimap after the first are 288 * ignored. 289 * 290 * <p>Despite the method name, this method attempts to avoid actually copying 291 * the data when it is safe to do so. The exact circumstances under which a 292 * copy will or will not be performed are undocumented and subject to change. 293 * 294 * @throws NullPointerException if any key or value in {@code multimap} is 295 * null 296 */ 297 public static <K, V> ImmutableSetMultimap<K, V> copyOf( 298 Multimap<? extends K, ? extends V> multimap) { 299 return copyOf(multimap, null); 300 } 301 302 private static <K, V> ImmutableSetMultimap<K, V> copyOf( 303 Multimap<? extends K, ? extends V> multimap, 304 Comparator<? super V> valueComparator) { 305 checkNotNull(multimap); // eager for GWT 306 if (multimap.isEmpty() && valueComparator == null) { 307 return of(); 308 } 309 310 if (multimap instanceof ImmutableSetMultimap) { 311 @SuppressWarnings("unchecked") // safe since multimap is not writable 312 ImmutableSetMultimap<K, V> kvMultimap 313 = (ImmutableSetMultimap<K, V>) multimap; 314 if (!kvMultimap.isPartialView()) { 315 return kvMultimap; 316 } 317 } 318 319 ImmutableMap.Builder<K, ImmutableSet<V>> builder = ImmutableMap.builder(); 320 int size = 0; 321 322 for (Entry<? extends K, ? extends Collection<? extends V>> entry 323 : multimap.asMap().entrySet()) { 324 K key = entry.getKey(); 325 Collection<? extends V> values = entry.getValue(); 326 ImmutableSet<V> set = valueSet(valueComparator, values); 327 if (!set.isEmpty()) { 328 builder.put(key, set); 329 size += set.size(); 330 } 331 } 332 333 return new ImmutableSetMultimap<K, V>( 334 builder.build(), size, valueComparator); 335 } 336 337 /** 338 * Returned by get() when a missing key is provided. Also holds the 339 * comparator, if any, used for values. 340 */ 341 private final transient ImmutableSet<V> emptySet; 342 343 ImmutableSetMultimap(ImmutableMap<K, ImmutableSet<V>> map, int size, 344 @Nullable Comparator<? super V> valueComparator) { 345 super(map, size); 346 this.emptySet = emptySet(valueComparator); 347 } 348 349 // views 350 351 /** 352 * Returns an immutable set of the values for the given key. If no mappings 353 * in the multimap have the provided key, an empty immutable set is returned. 354 * The values are in the same order as the parameters used to build this 355 * multimap. 356 */ 357 @Override public ImmutableSet<V> get(@Nullable K key) { 358 // This cast is safe as its type is known in constructor. 359 ImmutableSet<V> set = (ImmutableSet<V>) map.get(key); 360 return MoreObjects.firstNonNull(set, emptySet); 361 } 362 363 private transient ImmutableSetMultimap<V, K> inverse; 364 365 /** 366 * {@inheritDoc} 367 * 368 * <p>Because an inverse of a set multimap cannot contain multiple pairs with 369 * the same key and value, this method returns an {@code ImmutableSetMultimap} 370 * rather than the {@code ImmutableMultimap} specified in the {@code 371 * ImmutableMultimap} class. 372 * 373 * @since 11.0 374 */ 375 public ImmutableSetMultimap<V, K> inverse() { 376 ImmutableSetMultimap<V, K> result = inverse; 377 return (result == null) ? (inverse = invert()) : result; 378 } 379 380 private ImmutableSetMultimap<V, K> invert() { 381 Builder<V, K> builder = builder(); 382 for (Entry<K, V> entry : entries()) { 383 builder.put(entry.getValue(), entry.getKey()); 384 } 385 ImmutableSetMultimap<V, K> invertedMultimap = builder.build(); 386 invertedMultimap.inverse = this; 387 return invertedMultimap; 388 } 389 390 /** 391 * Guaranteed to throw an exception and leave the multimap unmodified. 392 * 393 * @throws UnsupportedOperationException always 394 * @deprecated Unsupported operation. 395 */ 396 @Deprecated @Override public ImmutableSet<V> removeAll(Object key) { 397 throw new UnsupportedOperationException(); 398 } 399 400 /** 401 * Guaranteed to throw an exception and leave the multimap unmodified. 402 * 403 * @throws UnsupportedOperationException always 404 * @deprecated Unsupported operation. 405 */ 406 @Deprecated @Override public ImmutableSet<V> replaceValues( 407 K key, Iterable<? extends V> values) { 408 throw new UnsupportedOperationException(); 409 } 410 411 private transient ImmutableSet<Entry<K, V>> entries; 412 413 /** 414 * Returns an immutable collection of all key-value pairs in the multimap. 415 * Its iterator traverses the values for the first key, the values for the 416 * second key, and so on. 417 */ 418 @Override public ImmutableSet<Entry<K, V>> entries() { 419 ImmutableSet<Entry<K, V>> result = entries; 420 return (result == null) 421 ? (entries = new EntrySet<K, V>(this)) 422 : result; 423 } 424 425 private static final class EntrySet<K, V> extends ImmutableSet<Entry<K, V>> { 426 private transient final ImmutableSetMultimap<K, V> multimap; 427 428 EntrySet(ImmutableSetMultimap<K, V> multimap) { 429 this.multimap = multimap; 430 } 431 432 @Override 433 public boolean contains(@Nullable Object object) { 434 if (object instanceof Entry) { 435 Entry<?, ?> entry = (Entry<?, ?>) object; 436 return multimap.containsEntry(entry.getKey(), entry.getValue()); 437 } 438 return false; 439 } 440 441 @Override 442 public int size() { 443 return multimap.size(); 444 } 445 446 @Override 447 public UnmodifiableIterator<Entry<K, V>> iterator() { 448 return multimap.entryIterator(); 449 } 450 451 @Override 452 boolean isPartialView() { 453 return false; 454 } 455 } 456 457 private static <V> ImmutableSet<V> valueSet( 458 @Nullable Comparator<? super V> valueComparator, 459 Collection<? extends V> values) { 460 return (valueComparator == null) 461 ? ImmutableSet.copyOf(values) 462 : ImmutableSortedSet.copyOf(valueComparator, values); 463 } 464 465 private static <V> ImmutableSet<V> emptySet( 466 @Nullable Comparator<? super V> valueComparator) { 467 return (valueComparator == null) 468 ? ImmutableSet.<V>of() 469 : ImmutableSortedSet.<V>emptySet(valueComparator); 470 } 471 472 /** 473 * @serialData number of distinct keys, and then for each distinct key: the 474 * key, the number of values for that key, and the key's values 475 */ 476 @GwtIncompatible("java.io.ObjectOutputStream") 477 private void writeObject(ObjectOutputStream stream) throws IOException { 478 stream.defaultWriteObject(); 479 stream.writeObject(valueComparator()); 480 Serialization.writeMultimap(this, stream); 481 } 482 483 @Nullable Comparator<? super V> valueComparator() { 484 return emptySet instanceof ImmutableSortedSet 485 ? ((ImmutableSortedSet<V>) emptySet).comparator() 486 : null; 487 } 488 489 @GwtIncompatible("java.io.ObjectInputStream") 490 // Serialization type safety is at the caller's mercy. 491 @SuppressWarnings("unchecked") 492 private void readObject(ObjectInputStream stream) 493 throws IOException, ClassNotFoundException { 494 stream.defaultReadObject(); 495 Comparator<Object> valueComparator = 496 (Comparator<Object>) stream.readObject(); 497 int keyCount = stream.readInt(); 498 if (keyCount < 0) { 499 throw new InvalidObjectException("Invalid key count " + keyCount); 500 } 501 ImmutableMap.Builder<Object, ImmutableSet<Object>> builder 502 = ImmutableMap.builder(); 503 int tmpSize = 0; 504 505 for (int i = 0; i < keyCount; i++) { 506 Object key = stream.readObject(); 507 int valueCount = stream.readInt(); 508 if (valueCount <= 0) { 509 throw new InvalidObjectException("Invalid value count " + valueCount); 510 } 511 512 Object[] array = new Object[valueCount]; 513 for (int j = 0; j < valueCount; j++) { 514 array[j] = stream.readObject(); 515 } 516 ImmutableSet<Object> valueSet = valueSet(valueComparator, asList(array)); 517 if (valueSet.size() != array.length) { 518 throw new InvalidObjectException( 519 "Duplicate key-value pairs exist for key " + key); 520 } 521 builder.put(key, valueSet); 522 tmpSize += valueCount; 523 } 524 525 ImmutableMap<Object, ImmutableSet<Object>> tmpMap; 526 try { 527 tmpMap = builder.build(); 528 } catch (IllegalArgumentException e) { 529 throw (InvalidObjectException) 530 new InvalidObjectException(e.getMessage()).initCause(e); 531 } 532 533 FieldSettersHolder.MAP_FIELD_SETTER.set(this, tmpMap); 534 FieldSettersHolder.SIZE_FIELD_SETTER.set(this, tmpSize); 535 FieldSettersHolder.EMPTY_SET_FIELD_SETTER.set( 536 this, emptySet(valueComparator)); 537 } 538 539 @GwtIncompatible("not needed in emulated source.") 540 private static final long serialVersionUID = 0; 541}