1 /* 2 * Copyright (C) 2009 Google Inc. 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 import com.google.common.annotations.GwtCompatible; 20 import static com.google.common.base.Preconditions.checkNotNull; 21 22 import java.io.IOException; 23 import java.io.InvalidObjectException; 24 import java.io.ObjectInputStream; 25 import java.io.ObjectOutputStream; 26 import java.util.Arrays; 27 import java.util.Collection; 28 import java.util.LinkedHashMap; 29 import java.util.Map; 30 31 import javax.annotation.Nullable; 32 33 /** 34 * An immutable {@link SetMultimap} with reliable user-specified key and value 35 * iteration order. Does not permit null keys or values. 36 * 37 * <p>Unlike {@link Multimaps#unmodifiableSetMultimap(SetMultimap)}, which is 38 * a <i>view</i> of a separate multimap which can still change, an instance of 39 * {@code ImmutableSetMultimap} contains its own data and will <i>never</i> 40 * change. {@code ImmutableSetMultimap} is convenient for 41 * {@code public static final} multimaps ("constant multimaps") and also lets 42 * you easily make a "defensive copy" of a multimap provided to your class by 43 * a caller. 44 * 45 * <p><b>Note</b>: Although this class is not final, it cannot be subclassed as 46 * it has no public or protected constructors. Thus, instances of this class 47 * are guaranteed to be immutable. 48 * 49 * @author Mike Ward 50 * @since 2010.01.04 <b>stable</b> (imported from Google Collections Library) 51 */ 52 @GwtCompatible(serializable = true) 53 public class ImmutableSetMultimap<K, V> 54 extends ImmutableMultimap<K, V> 55 implements SetMultimap<K, V> { 56 57 /** Returns the empty multimap. */ 58 // Casting is safe because the multimap will never hold any elements. 59 @SuppressWarnings("unchecked") 60 public static <K, V> ImmutableSetMultimap<K, V> of() { 61 // BEGIN android-changed 62 return (ImmutableSetMultimap) EmptyImmutableSetMultimap.INSTANCE; 63 // END android-changed 64 } 65 66 /** 67 * Returns an immutable multimap containing a single entry. 68 */ 69 public static <K, V> ImmutableSetMultimap<K, V> of(K k1, V v1) { 70 ImmutableSetMultimap.Builder<K, V> builder = ImmutableSetMultimap.builder(); 71 builder.put(k1, v1); 72 return builder.build(); 73 } 74 75 /** 76 * Returns an immutable multimap containing the given entries, in order. 77 * Repeated occurrences of an entry (according to {@link Object#equals}) after 78 * the first are ignored. 79 */ 80 public static <K, V> ImmutableSetMultimap<K, V> of(K k1, V v1, K k2, V v2) { 81 ImmutableSetMultimap.Builder<K, V> builder = ImmutableSetMultimap.builder(); 82 builder.put(k1, v1); 83 builder.put(k2, v2); 84 return builder.build(); 85 } 86 87 /** 88 * Returns an immutable multimap containing the given entries, in order. 89 * Repeated occurrences of an entry (according to {@link Object#equals}) after 90 * the first are ignored. 91 */ 92 public static <K, V> ImmutableSetMultimap<K, V> of( 93 K k1, V v1, K k2, V v2, K k3, V v3) { 94 ImmutableSetMultimap.Builder<K, V> builder = ImmutableSetMultimap.builder(); 95 builder.put(k1, v1); 96 builder.put(k2, v2); 97 builder.put(k3, v3); 98 return builder.build(); 99 } 100 101 /** 102 * Returns an immutable multimap containing the given entries, in order. 103 * Repeated occurrences of an entry (according to {@link Object#equals}) after 104 * the first are ignored. 105 */ 106 public static <K, V> ImmutableSetMultimap<K, V> of( 107 K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { 108 ImmutableSetMultimap.Builder<K, V> builder = ImmutableSetMultimap.builder(); 109 builder.put(k1, v1); 110 builder.put(k2, v2); 111 builder.put(k3, v3); 112 builder.put(k4, v4); 113 return builder.build(); 114 } 115 116 /** 117 * Returns an immutable multimap containing the given entries, in order. 118 * Repeated occurrences of an entry (according to {@link Object#equals}) after 119 * the first are ignored. 120 */ 121 public static <K, V> ImmutableSetMultimap<K, V> of( 122 K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { 123 ImmutableSetMultimap.Builder<K, V> builder = ImmutableSetMultimap.builder(); 124 builder.put(k1, v1); 125 builder.put(k2, v2); 126 builder.put(k3, v3); 127 builder.put(k4, v4); 128 builder.put(k5, v5); 129 return builder.build(); 130 } 131 132 // looking for of() with > 5 entries? Use the builder instead. 133 134 /** 135 * Returns a new {@link Builder}. 136 */ 137 public static <K, V> Builder<K, V> builder() { 138 return new Builder<K, V>(); 139 } 140 141 /** 142 * Multimap for {@link ImmutableSetMultimap.Builder} that maintains key 143 * and value orderings and performs better than {@link LinkedHashMultimap}. 144 */ 145 private static class BuilderMultimap<K, V> extends AbstractMultimap<K, V> { 146 BuilderMultimap() { 147 super(new LinkedHashMap<K, Collection<V>>()); 148 } 149 @Override Collection<V> createCollection() { 150 return Sets.newLinkedHashSet(); 151 } 152 private static final long serialVersionUID = 0; 153 } 154 155 /** 156 * A builder for creating immutable {@code SetMultimap} instances, especially 157 * {@code public static final} multimaps ("constant multimaps"). Example: 158 * <pre> {@code 159 * 160 * static final Multimap<String, Integer> STRING_TO_INTEGER_MULTIMAP = 161 * new ImmutableSetMultimap.Builder<String, Integer>() 162 * .put("one", 1) 163 * .putAll("several", 1, 2, 3) 164 * .putAll("many", 1, 2, 3, 4, 5) 165 * .build();}</pre> 166 * 167 * <p>Builder instances can be reused - it is safe to call {@link #build} 168 * multiple times to build multiple multimaps in series. Each multimap 169 * contains the key-value mappings in the previously created multimaps. 170 */ 171 public static final class Builder<K, V> 172 extends ImmutableMultimap.Builder<K, V> { 173 private final Multimap<K, V> builderMultimap = new BuilderMultimap<K, V>(); 174 175 /** 176 * Creates a new builder. The returned builder is equivalent to the builder 177 * generated by {@link ImmutableSetMultimap#builder}. 178 */ 179 public Builder() {} 180 181 /** 182 * Adds a key-value mapping to the built multimap if it is not already 183 * present. 184 */ 185 @Override public Builder<K, V> put(K key, V value) { 186 builderMultimap.put(checkNotNull(key), checkNotNull(value)); 187 return this; 188 } 189 190 /** 191 * Stores a collection of values with the same key in the built multimap. 192 * 193 * @throws NullPointerException if {@code key}, {@code values}, or any 194 * element in {@code values} is null. The builder is left in an invalid 195 * state. 196 */ 197 @Override public Builder<K, V> putAll(K key, Iterable<? extends V> values) { 198 Collection<V> collection = builderMultimap.get(checkNotNull(key)); 199 for (V value : values) { 200 collection.add(checkNotNull(value)); 201 } 202 return this; 203 } 204 205 /** 206 * Stores an array of values with the same key in the built multimap. 207 * 208 * @throws NullPointerException if the key or any value is null. The 209 * builder is left in an invalid state. 210 */ 211 @Override public Builder<K, V> putAll(K key, V... values) { 212 return putAll(key, Arrays.asList(values)); 213 } 214 215 /** 216 * Stores another multimap's entries in the built multimap. The generated 217 * multimap's key and value orderings correspond to the iteration ordering 218 * of the {@code multimap.asMap()} view, with new keys and values following 219 * any existing keys and values. 220 * 221 * @throws NullPointerException if any key or value in {@code multimap} is 222 * null. The builder is left in an invalid state. 223 */ 224 @Override public Builder<K, V> putAll( 225 Multimap<? extends K, ? extends V> multimap) { 226 for (Map.Entry<? extends K, ? extends Collection<? extends V>> entry 227 : multimap.asMap().entrySet()) { 228 putAll(entry.getKey(), entry.getValue()); 229 } 230 return this; 231 } 232 233 /** 234 * Returns a newly-created immutable set multimap. 235 */ 236 @Override public ImmutableSetMultimap<K, V> build() { 237 return copyOf(builderMultimap); 238 } 239 } 240 241 /** 242 * Returns an immutable set multimap containing the same mappings as 243 * {@code multimap}. The generated multimap's key and value orderings 244 * correspond to the iteration ordering of the {@code multimap.asMap()} view. 245 * Repeated occurrences of an entry in the multimap after the first are 246 * ignored. 247 * 248 * <p><b>Note:</b> Despite what the method name suggests, if 249 * {@code multimap} is an {@code ImmutableSetMultimap}, no copy will actually 250 * be performed, and the given multimap itself will be returned. 251 * 252 * @throws NullPointerException if any key or value in {@code multimap} is 253 * null 254 */ 255 public static <K, V> ImmutableSetMultimap<K, V> copyOf( 256 Multimap<? extends K, ? extends V> multimap) { 257 if (multimap.isEmpty()) { 258 return of(); 259 } 260 261 if (multimap instanceof ImmutableSetMultimap) { 262 @SuppressWarnings("unchecked") // safe since multimap is not writable 263 ImmutableSetMultimap<K, V> kvMultimap 264 = (ImmutableSetMultimap<K, V>) multimap; 265 return kvMultimap; 266 } 267 268 ImmutableMap.Builder<K, ImmutableSet<V>> builder = ImmutableMap.builder(); 269 int size = 0; 270 271 for (Map.Entry<? extends K, ? extends Collection<? extends V>> entry 272 : multimap.asMap().entrySet()) { 273 K key = entry.getKey(); 274 Collection<? extends V> values = entry.getValue(); 275 ImmutableSet<V> set = ImmutableSet.copyOf(values); 276 if (!set.isEmpty()) { 277 builder.put(key, set); 278 size += set.size(); 279 } 280 } 281 282 return new ImmutableSetMultimap<K, V>(builder.build(), size); 283 } 284 285 ImmutableSetMultimap(ImmutableMap<K, ImmutableSet<V>> map, int size) { 286 super(map, size); 287 } 288 289 // views 290 291 /** 292 * Returns an immutable set of the values for the given key. If no mappings 293 * in the multimap have the provided key, an empty immutable set is returned. 294 * The values are in the same order as the parameters used to build this 295 * multimap. 296 */ 297 @Override public ImmutableSet<V> get(@Nullable K key) { 298 // This cast is safe as its type is known in constructor. 299 ImmutableSet<V> set = (ImmutableSet<V>) map.get(key); 300 return (set == null) ? ImmutableSet.<V>of() : set; 301 } 302 303 /** 304 * Guaranteed to throw an exception and leave the multimap unmodified. 305 * 306 * @throws UnsupportedOperationException always 307 */ 308 @Override public ImmutableSet<V> removeAll(Object key) { 309 throw new UnsupportedOperationException(); 310 } 311 312 /** 313 * Guaranteed to throw an exception and leave the multimap unmodified. 314 * 315 * @throws UnsupportedOperationException always 316 */ 317 @Override public ImmutableSet<V> replaceValues( 318 K key, Iterable<? extends V> values) { 319 throw new UnsupportedOperationException(); 320 } 321 322 private transient ImmutableSet<Map.Entry<K, V>> entries; 323 324 /** 325 * Returns an immutable collection of all key-value pairs in the multimap. 326 * Its iterator traverses the values for the first key, the values for the 327 * second key, and so on. 328 */ 329 // TODO: Fix this so that two copies of the entries are not created. 330 @Override public ImmutableSet<Map.Entry<K, V>> entries() { 331 ImmutableSet<Map.Entry<K, V>> result = entries; 332 return (result == null) 333 ? (entries = ImmutableSet.copyOf(super.entries())) 334 : result; 335 } 336 337 /** 338 * @serialData number of distinct keys, and then for each distinct key: the 339 * key, the number of values for that key, and the key's values 340 */ 341 private void writeObject(ObjectOutputStream stream) throws IOException { 342 stream.defaultWriteObject(); 343 Serialization.writeMultimap(this, stream); 344 } 345 346 private void readObject(ObjectInputStream stream) 347 throws IOException, ClassNotFoundException { 348 stream.defaultReadObject(); 349 int keyCount = stream.readInt(); 350 if (keyCount < 0) { 351 throw new InvalidObjectException("Invalid key count " + keyCount); 352 } 353 ImmutableMap.Builder<Object, ImmutableSet<Object>> builder 354 = ImmutableMap.builder(); 355 int tmpSize = 0; 356 357 for (int i = 0; i < keyCount; i++) { 358 Object key = stream.readObject(); 359 int valueCount = stream.readInt(); 360 if (valueCount <= 0) { 361 throw new InvalidObjectException("Invalid value count " + valueCount); 362 } 363 364 Object[] array = new Object[valueCount]; 365 for (int j = 0; j < valueCount; j++) { 366 array[j] = stream.readObject(); 367 } 368 ImmutableSet<Object> valueSet = ImmutableSet.of(array); 369 if (valueSet.size() != array.length) { 370 throw new InvalidObjectException( 371 "Duplicate key-value pairs exist for key " + key); 372 } 373 builder.put(key, valueSet); 374 tmpSize += valueCount; 375 } 376 377 ImmutableMap<Object, ImmutableSet<Object>> tmpMap; 378 try { 379 tmpMap = builder.build(); 380 } catch (IllegalArgumentException e) { 381 throw (InvalidObjectException) 382 new InvalidObjectException(e.getMessage()).initCause(e); 383 } 384 385 FieldSettersHolder.MAP_FIELD_SETTER.set(this, tmpMap); 386 FieldSettersHolder.SIZE_FIELD_SETTER.set(this, tmpSize); 387 } 388 389 private static final long serialVersionUID = 0; 390 } 391