1 /* 2 * Copyright (C) 2007 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 com.google.common.annotations.VisibleForTesting; 21 import com.google.common.base.Preconditions; 22 23 import java.io.IOException; 24 import java.io.ObjectInputStream; 25 import java.io.ObjectOutputStream; 26 import java.util.Collection; 27 import java.util.HashMap; 28 import java.util.Map; 29 import java.util.Set; 30 31 /** 32 * Implementation of {@link Multimap} using hash tables. 33 * 34 * <p>The multimap does not store duplicate key-value pairs. Adding a new 35 * key-value pair equal to an existing key-value pair has no effect. 36 * 37 * <p>Keys and values may be null. All optional multimap methods are supported, 38 * and all returned views are modifiable. 39 * 40 * <p>This class is not threadsafe when any concurrent operations update the 41 * multimap. Concurrent read operations will work correctly. To allow concurrent 42 * update operations, wrap your multimap with a call to {@link 43 * Multimaps#synchronizedSetMultimap}. 44 * 45 * @author Jared Levy 46 * @since 2010.01.04 <b>stable</b> (imported from Google Collections Library) 47 */ 48 @GwtCompatible(serializable = true) 49 public final class HashMultimap<K, V> extends AbstractSetMultimap<K, V> { 50 private static final int DEFAULT_VALUES_PER_KEY = 8; 51 52 @VisibleForTesting 53 transient int expectedValuesPerKey = DEFAULT_VALUES_PER_KEY; 54 55 /** 56 * Creates a new, empty {@code HashMultimap} with the default initial 57 * capacities. 58 */ 59 public static <K, V> HashMultimap<K, V> create() { 60 return new HashMultimap<K, V>(); 61 } 62 63 /** 64 * Constructs an empty {@code HashMultimap} with enough capacity to hold the 65 * specified numbers of keys and values without rehashing. 66 * 67 * @param expectedKeys the expected number of distinct keys 68 * @param expectedValuesPerKey the expected average number of values per key 69 * @throws IllegalArgumentException if {@code expectedKeys} or {@code 70 * expectedValuesPerKey} is negative 71 */ 72 public static <K, V> HashMultimap<K, V> create( 73 int expectedKeys, int expectedValuesPerKey) { 74 return new HashMultimap<K, V>(expectedKeys, expectedValuesPerKey); 75 } 76 77 /** 78 * Constructs a {@code HashMultimap} with the same mappings as the specified 79 * multimap. If a key-value mapping appears multiple times in the input 80 * multimap, it only appears once in the constructed multimap. 81 * 82 * @param multimap the multimap whose contents are copied to this multimap 83 */ 84 public static <K, V> HashMultimap<K, V> create( 85 Multimap<? extends K, ? extends V> multimap) { 86 return new HashMultimap<K, V>(multimap); 87 } 88 89 private HashMultimap() { 90 super(new HashMap<K, Collection<V>>()); 91 } 92 93 private HashMultimap(int expectedKeys, int expectedValuesPerKey) { 94 super(Maps.<K, Collection<V>>newHashMapWithExpectedSize(expectedKeys)); 95 Preconditions.checkArgument(expectedValuesPerKey >= 0); 96 this.expectedValuesPerKey = expectedValuesPerKey; 97 } 98 99 private HashMultimap(Multimap<? extends K, ? extends V> multimap) { 100 super(Maps.<K, Collection<V>>newHashMapWithExpectedSize( 101 multimap.keySet().size())); 102 putAll(multimap); 103 } 104 105 /** 106 * {@inheritDoc} 107 * 108 * <p>Creates an empty {@code HashSet} for a collection of values for one key. 109 * 110 * @return a new {@code HashSet} containing a collection of values for one key 111 */ 112 @Override Set<V> createCollection() { 113 return Sets.<V>newHashSetWithExpectedSize(expectedValuesPerKey); 114 } 115 116 /** 117 * @serialData expectedValuesPerKey, number of distinct keys, and then for 118 * each distinct key: the key, number of values for that key, and the 119 * key's values 120 */ 121 private void writeObject(ObjectOutputStream stream) throws IOException { 122 stream.defaultWriteObject(); 123 stream.writeInt(expectedValuesPerKey); 124 Serialization.writeMultimap(this, stream); 125 } 126 127 private void readObject(ObjectInputStream stream) 128 throws IOException, ClassNotFoundException { 129 stream.defaultReadObject(); 130 expectedValuesPerKey = stream.readInt(); 131 int distinctKeys = Serialization.readCount(stream); 132 Map<K, Collection<V>> map = Maps.newHashMapWithExpectedSize(distinctKeys); 133 setMap(map); 134 Serialization.populateMultimap(this, stream, distinctKeys); 135 } 136 137 private static final long serialVersionUID = 0; 138 } 139