• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 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 static com.google.common.base.Preconditions.checkNotNull;
20 
21 import com.google.common.annotations.GwtCompatible;
22 
23 import java.util.AbstractCollection;
24 import java.util.Collection;
25 import java.util.Iterator;
26 import java.util.Map;
27 import java.util.Map.Entry;
28 import java.util.Set;
29 
30 import javax.annotation.Nullable;
31 
32 /**
33  * A skeleton {@code Multimap} implementation, not necessarily in terms of a {@code Map}.
34  *
35  * @author Louis Wasserman
36  */
37 @GwtCompatible
38 abstract class AbstractMultimap<K, V> implements Multimap<K, V> {
39   @Override
isEmpty()40   public boolean isEmpty() {
41     return size() == 0;
42   }
43 
44   @Override
containsValue(@ullable Object value)45   public boolean containsValue(@Nullable Object value) {
46     for (Collection<V> collection : asMap().values()) {
47       if (collection.contains(value)) {
48         return true;
49       }
50     }
51 
52     return false;
53   }
54 
55   @Override
containsEntry(@ullable Object key, @Nullable Object value)56   public boolean containsEntry(@Nullable Object key, @Nullable Object value) {
57     Collection<V> collection = asMap().get(key);
58     return collection != null && collection.contains(value);
59   }
60 
61   @Override
remove(@ullable Object key, @Nullable Object value)62   public boolean remove(@Nullable Object key, @Nullable Object value) {
63     Collection<V> collection = asMap().get(key);
64     return collection != null && collection.remove(value);
65   }
66 
67   @Override
put(@ullable K key, @Nullable V value)68   public boolean put(@Nullable K key, @Nullable V value) {
69     return get(key).add(value);
70   }
71 
72   @Override
putAll(@ullable K key, Iterable<? extends V> values)73   public boolean putAll(@Nullable K key, Iterable<? extends V> values) {
74     checkNotNull(values);
75     // make sure we only call values.iterator() once
76     // and we only call get(key) if values is nonempty
77     if (values instanceof Collection) {
78       Collection<? extends V> valueCollection = (Collection<? extends V>) values;
79       return !valueCollection.isEmpty() && get(key).addAll(valueCollection);
80     } else {
81       Iterator<? extends V> valueItr = values.iterator();
82       return valueItr.hasNext() && Iterators.addAll(get(key), valueItr);
83     }
84   }
85 
86   @Override
putAll(Multimap<? extends K, ? extends V> multimap)87   public boolean putAll(Multimap<? extends K, ? extends V> multimap) {
88     boolean changed = false;
89     for (Map.Entry<? extends K, ? extends V> entry : multimap.entries()) {
90       changed |= put(entry.getKey(), entry.getValue());
91     }
92     return changed;
93   }
94 
95   @Override
replaceValues(@ullable K key, Iterable<? extends V> values)96   public Collection<V> replaceValues(@Nullable K key, Iterable<? extends V> values) {
97     checkNotNull(values);
98     Collection<V> result = removeAll(key);
99     putAll(key, values);
100     return result;
101   }
102 
103   private transient Collection<Entry<K, V>> entries;
104 
105   @Override
entries()106   public Collection<Entry<K, V>> entries() {
107     Collection<Entry<K, V>> result = entries;
108     return (result == null) ? entries = createEntries() : result;
109   }
110 
createEntries()111   Collection<Entry<K, V>> createEntries() {
112     if (this instanceof SetMultimap) {
113       return new EntrySet();
114     } else {
115       return new Entries();
116     }
117   }
118 
119   private class Entries extends Multimaps.Entries<K, V> {
120     @Override
multimap()121     Multimap<K, V> multimap() {
122       return AbstractMultimap.this;
123     }
124 
125     @Override
iterator()126     public Iterator<Entry<K, V>> iterator() {
127       return entryIterator();
128     }
129   }
130 
131   private class EntrySet extends Entries implements Set<Entry<K, V>> {
132     @Override
hashCode()133     public int hashCode() {
134       return Sets.hashCodeImpl(this);
135     }
136 
137     @Override
equals(@ullable Object obj)138     public boolean equals(@Nullable Object obj) {
139       return Sets.equalsImpl(this, obj);
140     }
141   }
142 
entryIterator()143   abstract Iterator<Entry<K, V>> entryIterator();
144 
145   private transient Set<K> keySet;
146 
147   @Override
keySet()148   public Set<K> keySet() {
149     Set<K> result = keySet;
150     return (result == null) ? keySet = createKeySet() : result;
151   }
152 
createKeySet()153   Set<K> createKeySet() {
154     return new Maps.KeySet<K, Collection<V>>(asMap());
155   }
156 
157   private transient Multiset<K> keys;
158 
159   @Override
keys()160   public Multiset<K> keys() {
161     Multiset<K> result = keys;
162     return (result == null) ? keys = createKeys() : result;
163   }
164 
createKeys()165   Multiset<K> createKeys() {
166     return new Multimaps.Keys<K, V>(this);
167   }
168 
169   private transient Collection<V> values;
170 
171   @Override
values()172   public Collection<V> values() {
173     Collection<V> result = values;
174     return (result == null) ? values = createValues() : result;
175   }
176 
createValues()177   Collection<V> createValues() {
178     return new Values();
179   }
180 
181   class Values extends AbstractCollection<V> {
iterator()182     @Override public Iterator<V> iterator() {
183       return valueIterator();
184     }
185 
size()186     @Override public int size() {
187       return AbstractMultimap.this.size();
188     }
189 
contains(@ullable Object o)190     @Override public boolean contains(@Nullable Object o) {
191       return AbstractMultimap.this.containsValue(o);
192     }
193 
clear()194     @Override public void clear() {
195       AbstractMultimap.this.clear();
196     }
197   }
198 
valueIterator()199   Iterator<V> valueIterator() {
200     return Maps.valueIterator(entries().iterator());
201   }
202 
203   private transient Map<K, Collection<V>> asMap;
204 
205   @Override
asMap()206   public Map<K, Collection<V>> asMap() {
207     Map<K, Collection<V>> result = asMap;
208     return (result == null) ? asMap = createAsMap() : result;
209   }
210 
createAsMap()211   abstract Map<K, Collection<V>> createAsMap();
212 
213   // Comparison and hashing
214 
equals(@ullable Object object)215   @Override public boolean equals(@Nullable Object object) {
216     return Multimaps.equalsImpl(this, object);
217   }
218 
219   /**
220    * Returns the hash code for this multimap.
221    *
222    * <p>The hash code of a multimap is defined as the hash code of the map view,
223    * as returned by {@link Multimap#asMap}.
224    *
225    * @see Map#hashCode
226    */
hashCode()227   @Override public int hashCode() {
228     return asMap().hashCode();
229   }
230 
231   /**
232    * Returns a string representation of the multimap, generated by calling
233    * {@code toString} on the map returned by {@link Multimap#asMap}.
234    *
235    * @return a string representation of the multimap
236    */
237   @Override
toString()238   public String toString() {
239     return asMap().toString();
240   }
241 }
242