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