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