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

Last change on this file since 13695 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
RevLine 
[8378]1// License: GPL. For details, see LICENSE file.
[2702]2package org.openstreetmap.josm.tools;
3
[4668]4import java.util.ArrayList;
[3674]5import java.util.Collection;
[9755]6import java.util.Collections;
[2990]7import java.util.HashMap;
[5822]8import java.util.LinkedHashSet;
[4668]9import java.util.List;
[3637]10import java.util.Map;
[3674]11import java.util.Map.Entry;
[9371]12import java.util.Objects;
[3637]13import java.util.Set;
[2702]14
15/**
[7275]16 * MultiMap - maps keys to multiple values.
[3674]17 *
18 * Corresponds to Google guava LinkedHashMultimap and Apache Collections MultiValueMap
[4668]19 * but it is an independent (simple) implementation.
[3674]20 *
[7275]21 * @param <A> Key type
22 * @param <B> Value type
23 *
24 * @since 2702
[2702]25 */
[3674]26public class MultiMap<A, B> {
[3637]27
[5783]28 private final Map<A, Set<B>> map;
[3674]29
[7005]30 /**
31 * Constructs a new {@code MultiMap}.
32 */
[3674]33 public MultiMap() {
[7005]34 map = new HashMap<>();
[3674]35 }
36
[7005]37 /**
[7275]38 * Constructs a new {@code MultiMap} with the specified initial capacity.
[7005]39 * @param capacity the initial capacity
40 */
[3674]41 public MultiMap(int capacity) {
[7005]42 map = new HashMap<>(capacity);
[3674]43 }
44
[2946]45 /**
[9755]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 /**
[4685]61 * Map a key to a value.
62 *
63 * Can be called multiple times with the same key, but different value.
[7275]64 * @param key key with which the specified value is to be associated
65 * @param value value to be associated with the specified key
[2946]66 */
67 public void put(A key, B value) {
[12865]68 map.computeIfAbsent(key, k -> new LinkedHashSet<>()).add(value);
[2702]69 }
[2990]70
[2946]71 /**
[3674]72 * Put a key that maps to nothing. (Only if it is not already in the map)
[4685]73 *
[3674]74 * Afterwards containsKey(key) will return true and get(key) will return
75 * an empty Set instead of null.
[7275]76 * @param key key with which an empty set is to be associated
[2946]77 */
78 public void putVoid(A key) {
[3637]79 if (map.containsKey(key))
[2946]80 return;
[5822]81 map.put(key, new LinkedHashSet<B>());
[2946]82 }
83
84 /**
[4685]85 * Map the key to all the given values.
86 *
87 * Adds to the mappings that are already there.
[7275]88 * @param key key with which the specified values are to be associated
89 * @param values values to be associated with the specified key
[2946]90 */
[4685]91 public void putAll(A key, Collection<B> values) {
[12865]92 map.computeIfAbsent(key, k -> new LinkedHashSet<>(values)).addAll(values);
[4685]93 }
94
95 /**
96 * Get the keySet.
[7275]97 * @return a set view of the keys contained in this map
98 * @see Map#keySet()
[4685]99 */
[3674]100 public Set<A> keySet() {
101 return map.keySet();
102 }
103
104 /**
[4685]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.
[7275]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)
[3674]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.
[7275]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
[3674]122 */
[5783]123 public Set<B> getValues(A key) {
[3637]124 if (!map.containsKey(key))
[7005]125 return new LinkedHashSet<>();
[3637]126 return map.get(key);
[2946]127 }
[3637]128
[7275]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 */
[3674]134 public boolean isEmpty() {
135 return map.isEmpty();
[3637]136 }
137
[7275]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 */
[3674]144 public boolean containsKey(A key) {
145 return map.containsKey(key);
[3637]146 }
[3674]147
148 /**
[4685]149 * Returns true if the multimap contains a value for a key.
150 *
[3674]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);
[9074]157 return values != null && values.contains(value);
[3674]158 }
159
[7275]160 /**
161 * Removes all of the mappings from this map. The map will be empty after this call returns.
162 * @see Map#clear()
163 */
[3674]164 public void clear() {
165 map.clear();
166 }
167
[7275]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 */
[5783]174 public Set<Entry<A, Set<B>>> entrySet() {
[3674]175 return map.entrySet();
176 }
177
178 /**
[4685]179 * Returns the number of keys.
[7275]180 * @return the number of key-value mappings in this map
181 * @see Map#size()
[3674]182 */
183 public int size() {
184 return map.size();
185 }
186
187 /**
[4685]188 * Returns a collection of all value sets.
[7275]189 * @return a collection view of the values contained in this map
190 * @see Map#values()
[3674]191 */
[5783]192 public Collection<Set<B>> values() {
[3674]193 return map.values();
194 }
[4668]195
196 /**
[7275]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
[4668]200 *
[7275]201 * @return {@code true}, if something was removed
[4668]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 /**
[4685]212 * Removes all mappings for a certain key.
[7275]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)
[4668]216 */
[5783]217 public Set<B> remove(A key) {
[4668]218 return map.remove(key);
219 }
220
[5783]221 @Override
[7276]222 public int hashCode() {
[9371]223 return Objects.hash(map);
[7276]224 }
225
[9755]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
[7276]238 @Override
239 public boolean equals(Object obj) {
[9371]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);
[7276]244 }
245
246 @Override
[4668]247 public String toString() {
[7005]248 List<String> entries = new ArrayList<>(map.size());
[8332]249 for (Entry<A, Set<B>> entry : map.entrySet()) {
[8846]250 entries.add(entry.getKey() + "->{" + Utils.join(",", entry.getValue()) + '}');
[4668]251 }
[8846]252 return '(' + Utils.join(",", entries) + ')';
[4668]253 }
[2702]254}
Note: See TracBrowser for help on using the repository browser.