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