001/* 002 * Copyright (C) 2007 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 017package com.google.common.collect; 018 019import com.google.common.annotations.GwtCompatible; 020import com.google.common.annotations.GwtIncompatible; 021import com.google.common.annotations.VisibleForTesting; 022import com.google.common.base.Preconditions; 023import java.io.IOException; 024import java.io.ObjectInputStream; 025import java.io.ObjectOutputStream; 026import java.util.Collection; 027import java.util.HashMap; 028import java.util.Map; 029import java.util.Set; 030 031/** 032 * Implementation of {@link Multimap} using hash tables. 033 * 034 * <p>The multimap does not store duplicate key-value pairs. Adding a new 035 * key-value pair equal to an existing key-value pair has no effect. 036 * 037 * <p>Keys and values may be null. All optional multimap methods are supported, 038 * and all returned views are modifiable. 039 * 040 * <p>This class is not threadsafe when any concurrent operations update the 041 * multimap. Concurrent read operations will work correctly. To allow concurrent 042 * update operations, wrap your multimap with a call to {@link 043 * Multimaps#synchronizedSetMultimap}. 044 * 045 * @author Jared Levy 046 * @since 2.0 047 */ 048@GwtCompatible(serializable = true, emulated = true) 049public final class HashMultimap<K, V> extends AbstractSetMultimap<K, V> { 050 private static final int DEFAULT_VALUES_PER_KEY = 2; 051 052 @VisibleForTesting transient int expectedValuesPerKey = DEFAULT_VALUES_PER_KEY; 053 054 /** 055 * Creates a new, empty {@code HashMultimap} with the default initial 056 * capacities. 057 */ 058 public static <K, V> HashMultimap<K, V> create() { 059 return new HashMultimap<K, V>(); 060 } 061 062 /** 063 * Constructs an empty {@code HashMultimap} with enough capacity to hold the 064 * specified numbers of keys and values without rehashing. 065 * 066 * @param expectedKeys the expected number of distinct keys 067 * @param expectedValuesPerKey the expected average number of values per key 068 * @throws IllegalArgumentException if {@code expectedKeys} or {@code 069 * expectedValuesPerKey} is negative 070 */ 071 public static <K, V> HashMultimap<K, V> create(int expectedKeys, int expectedValuesPerKey) { 072 return new HashMultimap<K, V>(expectedKeys, expectedValuesPerKey); 073 } 074 075 /** 076 * Constructs a {@code HashMultimap} with the same mappings as the specified 077 * multimap. If a key-value mapping appears multiple times in the input 078 * multimap, it only appears once in the constructed multimap. 079 * 080 * @param multimap the multimap whose contents are copied to this multimap 081 */ 082 public static <K, V> HashMultimap<K, V> create(Multimap<? extends K, ? extends V> multimap) { 083 return new HashMultimap<K, V>(multimap); 084 } 085 086 private HashMultimap() { 087 super(new HashMap<K, Collection<V>>()); 088 } 089 090 private HashMultimap(int expectedKeys, int expectedValuesPerKey) { 091 super(Maps.<K, Collection<V>>newHashMapWithExpectedSize(expectedKeys)); 092 Preconditions.checkArgument(expectedValuesPerKey >= 0); 093 this.expectedValuesPerKey = expectedValuesPerKey; 094 } 095 096 private HashMultimap(Multimap<? extends K, ? extends V> multimap) { 097 super(Maps.<K, Collection<V>>newHashMapWithExpectedSize(multimap.keySet().size())); 098 putAll(multimap); 099 } 100 101 /** 102 * {@inheritDoc} 103 * 104 * <p>Creates an empty {@code HashSet} for a collection of values for one key. 105 * 106 * @return a new {@code HashSet} containing a collection of values for one key 107 */ 108 @Override 109 Set<V> createCollection() { 110 return Sets.<V>newHashSetWithExpectedSize(expectedValuesPerKey); 111 } 112 113 /** 114 * @serialData expectedValuesPerKey, number of distinct keys, and then for 115 * each distinct key: the key, number of values for that key, and the 116 * key's values 117 */ 118 @GwtIncompatible // java.io.ObjectOutputStream 119 private void writeObject(ObjectOutputStream stream) throws IOException { 120 stream.defaultWriteObject(); 121 Serialization.writeMultimap(this, stream); 122 } 123 124 @GwtIncompatible // java.io.ObjectInputStream 125 private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { 126 stream.defaultReadObject(); 127 expectedValuesPerKey = DEFAULT_VALUES_PER_KEY; 128 int distinctKeys = Serialization.readCount(stream); 129 Map<K, Collection<V>> map = Maps.newHashMap(); 130 setMap(map); 131 Serialization.populateMultimap(this, stream, distinctKeys); 132 } 133 134 @GwtIncompatible // Not needed in emulated source 135 private static final long serialVersionUID = 0; 136}