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