View Javadoc
1   /*
2    * Copyright (C) 2007 The Guava Authors
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.GwtIncompatible;
21  import com.google.common.annotations.VisibleForTesting;
22  import com.google.common.base.Preconditions;
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 2.0
47   */
48  @GwtCompatible(serializable = true, emulated = true)
49  public final class HashMultimap<K, V> extends HashMultimapGwtSerializationDependencies<K, V> {
50    private static final int DEFAULT_VALUES_PER_KEY = 2;
51  
52    @VisibleForTesting transient int expectedValuesPerKey = DEFAULT_VALUES_PER_KEY;
53  
54    /**
55     * Creates a new, empty {@code HashMultimap} with the default initial capacities.
56     *
57     * <p>This method will soon be deprecated in favor of {@code
58     * MultimapBuilder.hashKeys().hashSetValues().build()}.
59     */
60    public static <K, V> HashMultimap<K, V> create() {
61      return new HashMultimap<>();
62    }
63  
64    /**
65     * Constructs an empty {@code HashMultimap} with enough capacity to hold the specified numbers of
66     * keys and values without rehashing.
67     *
68     * <p>This method will soon be deprecated in favor of {@code
69     * MultimapBuilder.hashKeys(expectedKeys).hashSetValues(expectedValuesPerKey).build()}.
70     *
71     * @param expectedKeys the expected number of distinct keys
72     * @param expectedValuesPerKey the expected average number of values per key
73     * @throws IllegalArgumentException if {@code expectedKeys} or {@code expectedValuesPerKey} is
74     *     negative
75     */
76    public static <K, V> HashMultimap<K, V> create(int expectedKeys, int expectedValuesPerKey) {
77      return new HashMultimap<>(expectedKeys, expectedValuesPerKey);
78    }
79  
80    /**
81     * Constructs a {@code HashMultimap} with the same mappings as the specified multimap. If a
82     * key-value mapping appears multiple times in the input multimap, it only appears once in the
83     * constructed multimap.
84     *
85     * <p>This method will soon be deprecated in favor of {@code
86     * MultimapBuilder.hashKeys().hashSetValues().build(multimap)}.
87     *
88     * @param multimap the multimap whose contents are copied to this multimap
89     */
90    public static <K, V> HashMultimap<K, V> create(Multimap<? extends K, ? extends V> multimap) {
91      return new HashMultimap<>(multimap);
92    }
93  
94    private HashMultimap() {
95      super(new HashMap<K, Collection<V>>());
96    }
97  
98    private HashMultimap(int expectedKeys, int expectedValuesPerKey) {
99      super(Maps.<K, Collection<V>>newHashMapWithExpectedSize(expectedKeys));
100     Preconditions.checkArgument(expectedValuesPerKey >= 0);
101     this.expectedValuesPerKey = expectedValuesPerKey;
102   }
103 
104   private HashMultimap(Multimap<? extends K, ? extends V> multimap) {
105     super(Maps.<K, Collection<V>>newHashMapWithExpectedSize(multimap.keySet().size()));
106     putAll(multimap);
107   }
108 
109   /**
110    * {@inheritDoc}
111    *
112    * <p>Creates an empty {@code HashSet} for a collection of values for one key.
113    *
114    * @return a new {@code HashSet} containing a collection of values for one key
115    */
116   @Override
117   Set<V> createCollection() {
118     return Sets.<V>newHashSetWithExpectedSize(expectedValuesPerKey);
119   }
120 
121   /**
122    * @serialData expectedValuesPerKey, number of distinct keys, and then for
123    *     each distinct key: the key, number of values for that key, and the
124    *     key's values
125    */
126   @GwtIncompatible // java.io.ObjectOutputStream
127   private void writeObject(ObjectOutputStream stream) throws IOException {
128     stream.defaultWriteObject();
129     Serialization.writeMultimap(this, stream);
130   }
131 
132   @GwtIncompatible // java.io.ObjectInputStream
133   private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
134     stream.defaultReadObject();
135     expectedValuesPerKey = DEFAULT_VALUES_PER_KEY;
136     int distinctKeys = Serialization.readCount(stream);
137     Map<K, Collection<V>> map = Maps.newHashMap();
138     setMap(map);
139     Serialization.populateMultimap(this, stream, distinctKeys);
140   }
141 
142   @GwtIncompatible // Not needed in emulated source
143   private static final long serialVersionUID = 0;
144 }