source: josm/trunk/src/org/openstreetmap/josm/tools/MultiMap.java@ 13882

Last change on this file since 13882 was 12865, checked in by Don-vip, 7 years ago

see #11390 - SonarQube - squid:S3824 - "Map.get" and value test should be replaced with single method call

  • Property svn:eol-style set to native
File size: 7.5 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.tools;
3
4import java.util.ArrayList;
5import java.util.Collection;
6import java.util.Collections;
7import java.util.HashMap;
8import java.util.LinkedHashSet;
9import java.util.List;
10import java.util.Map;
11import java.util.Map.Entry;
12import java.util.Objects;
13import java.util.Set;
14
15/**
16 * MultiMap - maps keys to multiple values.
17 *
18 * Corresponds to Google guava LinkedHashMultimap and Apache Collections MultiValueMap
19 * but it is an independent (simple) implementation.
20 *
21 * @param <A> Key type
22 * @param <B> Value type
23 *
24 * @since 2702
25 */
26public class MultiMap<A, B> {
27
28 private final Map<A, Set<B>> map;
29
30 /**
31 * Constructs a new {@code MultiMap}.
32 */
33 public MultiMap() {
34 map = new HashMap<>();
35 }
36
37 /**
38 * Constructs a new {@code MultiMap} with the specified initial capacity.
39 * @param capacity the initial capacity
40 */
41 public MultiMap(int capacity) {
42 map = new HashMap<>(capacity);
43 }
44
45 /**
46 * Constructs a new {@code MultiMap} from an ordinary {@code Map}.
47 * @param map0 the {@code Map}
48 */
49 public MultiMap(Map<A, Set<B>> map0) {
50 if (map0 == null) {
51 map = new HashMap<>();
52 } else {
53 map = new HashMap<>(Utils.hashMapInitialCapacity(map0.size()));
54 for (Entry<A, Set<B>> e : map0.entrySet()) {
55 map.put(e.getKey(), new LinkedHashSet<>(e.getValue()));
56 }
57 }
58 }
59
60 /**
61 * Map a key to a value.
62 *
63 * Can be called multiple times with the same key, but different value.
64 * @param key key with which the specified value is to be associated
65 * @param value value to be associated with the specified key
66 */
67 public void put(A key, B value) {
68 map.computeIfAbsent(key, k -> new LinkedHashSet<>()).add(value);
69 }
70
71 /**
72 * Put a key that maps to nothing. (Only if it is not already in the map)
73 *
74 * Afterwards containsKey(key) will return true and get(key) will return
75 * an empty Set instead of null.
76 * @param key key with which an empty set is to be associated
77 */
78 public void putVoid(A key) {
79 if (map.containsKey(key))
80 return;
81 map.put(key, new LinkedHashSet<B>());
82 }
83
84 /**
85 * Map the key to all the given values.
86 *
87 * Adds to the mappings that are already there.
88 * @param key key with which the specified values are to be associated
89 * @param values values to be associated with the specified key
90 */
91 public void putAll(A key, Collection<B> values) {
92 map.computeIfAbsent(key, k -> new LinkedHashSet<>(values)).addAll(values);
93 }
94
95 /**
96 * Get the keySet.
97 * @return a set view of the keys contained in this map
98 * @see Map#keySet()
99 */
100 public Set<A> keySet() {
101 return map.keySet();
102 }
103
104 /**
105 * Returns the Set associated with the given key. Result is null if
106 * nothing has been mapped to this key.
107 *
108 * Modifications of the returned list changes the underling map,
109 * but you should better not do that.
110 * @param key the key whose associated value is to be returned
111 * @return the set of values to which the specified key is mapped, or {@code null} if this map contains no mapping for the key
112 * @see Map#get(Object)
113 */
114 public Set<B> get(A key) {
115 return map.get(key);
116 }
117
118 /**
119 * Like get, but returns an empty Set if nothing has been mapped to the key.
120 * @param key the key whose associated value is to be returned
121 * @return the set of values to which the specified key is mapped, or an empty set if this map contains no mapping for the key
122 */
123 public Set<B> getValues(A key) {
124 if (!map.containsKey(key))
125 return new LinkedHashSet<>();
126 return map.get(key);
127 }
128
129 /**
130 * Returns {@code true} if this map contains no key-value mappings.
131 * @return {@code true} if this map contains no key-value mappings
132 * @see Map#isEmpty()
133 */
134 public boolean isEmpty() {
135 return map.isEmpty();
136 }
137
138 /**
139 * Returns {@code true} if this map contains a mapping for the specified key.
140 * @param key key whose presence in this map is to be tested
141 * @return {@code true} if this map contains a mapping for the specified key
142 * @see Map#containsKey(Object)
143 */
144 public boolean containsKey(A key) {
145 return map.containsKey(key);
146 }
147
148 /**
149 * Returns true if the multimap contains a value for a key.
150 *
151 * @param key The key
152 * @param value The value
153 * @return true if the key contains the value
154 */
155 public boolean contains(A key, B value) {
156 Set<B> values = get(key);
157 return values != null && values.contains(value);
158 }
159
160 /**
161 * Removes all of the mappings from this map. The map will be empty after this call returns.
162 * @see Map#clear()
163 */
164 public void clear() {
165 map.clear();
166 }
167
168 /**
169 * Returns a Set view of the mappings contained in this map.
170 * The set is backed by the map, so changes to the map are reflected in the set, and vice-versa.
171 * @return a set view of the mappings contained in this map
172 * @see Map#entrySet()
173 */
174 public Set<Entry<A, Set<B>>> entrySet() {
175 return map.entrySet();
176 }
177
178 /**
179 * Returns the number of keys.
180 * @return the number of key-value mappings in this map
181 * @see Map#size()
182 */
183 public int size() {
184 return map.size();
185 }
186
187 /**
188 * Returns a collection of all value sets.
189 * @return a collection view of the values contained in this map
190 * @see Map#values()
191 */
192 public Collection<Set<B>> values() {
193 return map.values();
194 }
195
196 /**
197 * Removes a certain key=value mapping.
198 * @param key key whose mapping is to be removed from the map
199 * @param value value whose mapping is to be removed from the map
200 *
201 * @return {@code true}, if something was removed
202 */
203 public boolean remove(A key, B value) {
204 Set<B> values = get(key);
205 if (values != null) {
206 return values.remove(value);
207 }
208 return false;
209 }
210
211 /**
212 * Removes all mappings for a certain key.
213 * @param key key whose mapping is to be removed from the map
214 * @return the previous value associated with key, or {@code null} if there was no mapping for key.
215 * @see Map#remove(Object)
216 */
217 public Set<B> remove(A key) {
218 return map.remove(key);
219 }
220
221 @Override
222 public int hashCode() {
223 return Objects.hash(map);
224 }
225
226 /**
227 * Converts this {@code MultiMap} to a {@code Map} with {@code Set} values.
228 * @return the converted {@code Map}
229 */
230 public Map<A, Set<B>> toMap() {
231 Map<A, Set<B>> result = new HashMap<>();
232 for (Entry<A, Set<B>> e : map.entrySet()) {
233 result.put(e.getKey(), Collections.unmodifiableSet(e.getValue()));
234 }
235 return result;
236 }
237
238 @Override
239 public boolean equals(Object obj) {
240 if (this == obj) return true;
241 if (obj == null || getClass() != obj.getClass()) return false;
242 MultiMap<?, ?> multiMap = (MultiMap<?, ?>) obj;
243 return Objects.equals(map, multiMap.map);
244 }
245
246 @Override
247 public String toString() {
248 List<String> entries = new ArrayList<>(map.size());
249 for (Entry<A, Set<B>> entry : map.entrySet()) {
250 entries.add(entry.getKey() + "->{" + Utils.join(",", entry.getValue()) + '}');
251 }
252 return '(' + Utils.join(",", entries) + ')';
253 }
254}
Note: See TracBrowser for help on using the repository browser.