1 package org.djutils.immutablecollections;
2
3 import java.io.Serializable;
4 import java.util.Comparator;
5 import java.util.ConcurrentModificationException;
6 import java.util.LinkedHashMap;
7 import java.util.Map;
8 import java.util.Map.Entry;
9 import java.util.Objects;
10 import java.util.Set;
11 import java.util.function.BiConsumer;
12
13 /**
14 * A Map interface without the methods that can change it. The constructor of the ImmutableMap needs to be given an initial Map.
15 * <p>
16 * Copyright (c) 2016-2020 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
17 * for project information <a href="https://djutils.org" target="_blank"> https://djutils.org</a>. The DJUTILS project is
18 * distributed under a three-clause BSD-style license, which can be found at
19 * <a href="https://djutils.org/docs/license.html" target="_blank"> https://djutils.org/docs/license.html</a>.
20 * </p>
21 * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
22 * @author <a href="https://www.tudelft.nl/staff/p.knoppers/">Peter Knoppers</a>
23 * @author <a href="https://www.transport.citg.tudelft.nl">Wouter Schakel</a>
24 * @param <K> the key type of content of this Map
25 * @param <V> the value type of content of this Map
26 */
27 public interface ImmutableMap<K, V> extends Serializable
28 {
29 /**
30 * Returns the number of elements in this immutable collection. If this immutable collection contains more than
31 * <code>Integer.MAX_VALUE</code> elements, returns <code>Integer.MAX_VALUE</code>.
32 * @return the number of elements in this immutable collection
33 */
34 int size();
35
36 /**
37 * Returns <code>true</code> if this immutable collection contains no elements.
38 * @return <code>true</code> if this immutable collection contains no elements
39 */
40 boolean isEmpty();
41
42 /**
43 * Returns <code>true</code> if this map contains a mapping for the specified key. More formally, returns <code>true</code>
44 * if and only if this map contains a mapping for a key <code>k</code> such that
45 * <code>(key==null ? k==null : key.equals(k))</code>. (There can be at most one such mapping.)
46 * @param key Object; key whose presence in this map is to be tested
47 * @return <code>true</code> if this map contains a mapping for the specified key
48 * @throws ClassCastException if the key is of an inappropriate type for this map
49 * @throws NullPointerException if the specified key is null and this map does not permit null keys
50 */
51 boolean containsKey(Object key);
52
53 /**
54 * Returns <code>true</code> if this map maps one or more keys to the specified value. More formally, returns
55 * <code>true</code> if and only if this map contains at least one mapping to a value <code>v</code> such that
56 * <code>(value==null ? v==null : value.equals(v))</code>. This operation will probably require time linear in the map size
57 * for most implementations of the <code>Map</code> interface.
58 * @param value Object; value whose presence in this map is to be tested
59 * @return <code>true</code> if this map maps one or more keys to the specified value
60 * @throws ClassCastException if the value is of an inappropriate type for this map
61 * @throws NullPointerException if the specified value is null and this map does not permit null values
62 */
63 boolean containsValue(Object value);
64
65 /**
66 * Returns the value to which the specified key is mapped, or {@code null} if this map contains no mapping for the key.
67 * <p>
68 * More formally, if this map contains a mapping from a key {@code k} to a value {@code v} such that
69 * {@code (key==null ? k==null : key.equals(k))}, then this method returns {@code v}; otherwise it returns {@code null}.
70 * (There can be at most one such mapping.)
71 * <p>
72 * If this map permits null values, then a return value of {@code null} does not <i>necessarily</i> indicate that the map
73 * contains no mapping for the key; it's also possible that the map explicitly maps the key to {@code null}. The
74 * {@link #containsKey containsKey} operation may be used to distinguish these two cases.
75 * @param key Object; the key whose associated value is to be returned
76 * @return the value to which the specified key is mapped, or {@code null} if this map contains no mapping for the key
77 * @throws ClassCastException if the key is of an inappropriate type for this map
78 * @throws NullPointerException if the specified key is null and this map does not permit null keys
79 */
80 V get(Object key);
81
82 /**
83 * Returns a {@link Set} view of the keys contained in this map.
84 * @return an immutable set of the keys contained in this map
85 */
86 ImmutableSet<K> keySet();
87
88 /**
89 * Returns a {@link Set} view of the entries contained in this map.
90 * @return an immutable set of the entries contained in this map
91 */
92 ImmutableSet<ImmutableEntry<K, V>> entrySet();
93
94 /**
95 * Returns a {@link ImmutableCollection} view of the values contained in this map.
96 * @return an immutable collection view of the values contained in this map
97 */
98 ImmutableCollection<V> values();
99
100 /**
101 * Returns the value to which the specified key is mapped, or {@code defaultValue} if this map contains no mapping for the
102 * key. The default implementation makes no guarantees about synchronization or atomicity properties of this method. Any
103 * implementation providing atomicity guarantees must override this method and document its concurrency properties.
104 * @param key Object; the key whose associated value is to be returned
105 * @param defaultValue V; the default mapping of the key
106 * @return the value to which the specified key is mapped, or {@code defaultValue} if this map contains no mapping for the
107 * key
108 * @throws ClassCastException if the key is of an inappropriate type for this map
109 * @throws NullPointerException if the specified key is null and this map does not permit null keys
110 */
111 default V getOrDefault(Object key, V defaultValue)
112 {
113 V v = get(key);
114 return ((v != null) || containsKey(key)) ? v : defaultValue;
115 }
116
117 /**
118 * Performs the given action for each entry in this map until all entries have been processed or the action throws an
119 * exception. Unless otherwise specified by the implementing class, actions are performed in the order of entry set
120 * iteration (if an iteration order is specified.) Exceptions thrown by the action are relayed to the caller. The default
121 * implementation makes no guarantees about synchronization or atomicity properties of this method. Any implementation
122 * providing atomicity guarantees must override this method and document its concurrency properties.
123 * @param action BiConsumer<? super K,? super V>; The action to be performed for each entry
124 * @throws NullPointerException if the specified action is null
125 * @throws ConcurrentModificationException if an entry is found to be removed during iteration
126 */
127 default void forEach(BiConsumer<? super K, ? super V> action)
128 {
129 Objects.requireNonNull(action);
130 for (ImmutableEntry<K, V> entry : entrySet())
131 {
132 K k;
133 V v;
134 try
135 {
136 k = entry.getKey();
137 v = entry.getValue();
138 }
139 catch (IllegalStateException ise)
140 {
141 // this usually means the entry is no longer in the map.
142 throw new ConcurrentModificationException(ise);
143 }
144 action.accept(k, v);
145 }
146 }
147
148 /**
149 * Returns a modifiable copy of this immutable map.
150 * @return a modifiable copy of this immutable map.
151 */
152 Map<K, V> toMap();
153
154 /**
155 * Force to redefine equals for the implementations of immutable collection classes.
156 * @param obj Object; the object to compare this collection with
157 * @return whether the objects are equal
158 */
159 @Override
160 boolean equals(Object obj);
161
162 /**
163 * Force to redefine hashCode for the implementations of immutable collection classes.
164 * @return the calculated hashCode
165 */
166 @Override
167 int hashCode();
168
169 /**
170 * Return whether the internal storage is a wrapped pointer to the original map. If true, this means that anyone holding a
171 * pointer to this data structure can still change it. The users of the ImmutableMap itself can, however, not make any
172 * changes.
173 * @return boolean; whether the internal storage is a wrapped pointer to the original map
174 */
175 boolean isWrap();
176
177 /**
178 * Return whether the internal storage is a (shallow) copy of the original map. If true, this means that anyone holding a
179 * pointer to the original of the data structure can not change it anymore. Nor can the users of the ImmutableMap itself
180 * make any changes.
181 * @return boolean; whether the internal storage is a safe copy of the original map
182 */
183 default boolean isCopy()
184 {
185 return !isWrap();
186 }
187
188 /**
189 * Return an empty ImmutableMap, backed by a LinkedHashMap.
190 * @param <K> the key type
191 * @param <V> the value type
192 * @return ImmutableMap<K, V>; an empty ImmutableMap
193 */
194 static <K, V> ImmutableMap<K, V> of()
195 {
196 return new ImmutableLinkedHashMap<>(new LinkedHashMap<K, V>(), Immutable.WRAP);
197 }
198
199 /**
200 * Return an ImmutableMap with 1 entry, backed by a LinkedHashMap.
201 * @param <K> the key type
202 * @param <V> the value type
203 * @param k1 K; key 1
204 * @param v1 V; value 1
205 * @return ImmutableMap<K, V>; an ImmutableMap with 1 entry, backed by a LinkedHashMap
206 */
207 static <K, V> ImmutableMap<K, V> of(final K k1, final V v1)
208 {
209 LinkedHashMap<K, V> map = new LinkedHashMap<>();
210 map.put(k1, v1);
211 return new ImmutableLinkedHashMap<>(map, Immutable.WRAP);
212 }
213
214 /**
215 * Return an ImmutableMap with 2 entries, backed by a LinkedHashMap.
216 * @param <K> the key type
217 * @param <V> the value type
218 * @param k1 K; key 1
219 * @param v1 V; value 1
220 * @param k2 K; key 2
221 * @param v2 V; value 2
222 * @return ImmutableMap<K, V>; an ImmutableMap with 2 entries, backed by a LinkedHashMap
223 */
224 static <K, V> ImmutableMap<K, V> of(final K k1, final V v1, final K k2, final V v2)
225 {
226 LinkedHashMap<K, V> map = new LinkedHashMap<>();
227 map.put(k1, v1);
228 map.put(k2, v2);
229 return new ImmutableLinkedHashMap<>(map, Immutable.WRAP);
230 }
231
232 /**
233 * Return an ImmutableMap with 3 entries, backed by a LinkedHashMap.
234 * @param <K> the key type
235 * @param <V> the value type
236 * @param k1 K; key 1
237 * @param v1 V; value 1
238 * @param k2 K; key 2
239 * @param v2 V; value 2
240 * @param k3 K; key 3
241 * @param v3 V; value 3
242 * @return ImmutableMap<K, V>; an ImmutableMap with 3 entries, backed by a LinkedHashMap
243 */
244 static <K, V> ImmutableMap<K, V> of(final K k1, final V v1, final K k2, final V v2, final K k3, final V v3)
245 {
246 LinkedHashMap<K, V> map = new LinkedHashMap<>();
247 map.put(k1, v1);
248 map.put(k2, v2);
249 map.put(k3, v3);
250 return new ImmutableLinkedHashMap<>(map, Immutable.WRAP);
251 }
252
253 /**
254 * Return an ImmutableMap with 4 entries, backed by a LinkedHashMap.
255 * @param <K> the key type
256 * @param <V> the value type
257 * @param k1 K; key 1
258 * @param v1 V; value 1
259 * @param k2 K; key 2
260 * @param v2 V; value 2
261 * @param k3 K; key 3
262 * @param v3 V; value 3
263 * @param k4 K; key 4
264 * @param v4 V; value 4
265 * @return ImmutableMap<K, V>; an ImmutableMap with 4 entries, backed by a LinkedHashMap
266 */
267 @SuppressWarnings("checkstyle:parameternumber")
268 static <K, V> ImmutableMap<K, V> of(final K k1, final V v1, final K k2, final V v2, final K k3, final V v3, final K k4,
269 final V v4)
270 {
271 LinkedHashMap<K, V> map = new LinkedHashMap<>();
272 map.put(k1, v1);
273 map.put(k2, v2);
274 map.put(k3, v3);
275 map.put(k4, v4);
276 return new ImmutableLinkedHashMap<>(map, Immutable.WRAP);
277 }
278
279 /**
280 * Return an ImmutableMap with 5 entries, backed by a LinkedHashMap.
281 * @param <K> the key type
282 * @param <V> the value type
283 * @param k1 K; key 1
284 * @param v1 V; value 1
285 * @param k2 K; key 2
286 * @param v2 V; value 2
287 * @param k3 K; key 3
288 * @param v3 V; value 3
289 * @param k4 K; key 4
290 * @param v4 V; value 4
291 * @param k5 K; key 5
292 * @param v5 V; value 5
293 * @return ImmutableMap<K, V>; an ImmutableMap with 5 entries, backed by a LinkedHashMap
294 */
295 @SuppressWarnings("checkstyle:parameternumber")
296 static <K, V> ImmutableMap<K, V> of(final K k1, final V v1, final K k2, final V v2, final K k3, final V v3, final K k4,
297 final V v4, final K k5, final V v5)
298 {
299 LinkedHashMap<K, V> map = new LinkedHashMap<>();
300 map.put(k1, v1);
301 map.put(k2, v2);
302 map.put(k3, v3);
303 map.put(k4, v4);
304 map.put(k5, v5);
305 return new ImmutableLinkedHashMap<>(map, Immutable.WRAP);
306 }
307
308 /**
309 * Return an ImmutableMap with 6 entries, backed by a LinkedHashMap.
310 * @param <K> the key type
311 * @param <V> the value type
312 * @param k1 K; key 1
313 * @param v1 V; value 1
314 * @param k2 K; key 2
315 * @param v2 V; value 2
316 * @param k3 K; key 3
317 * @param v3 V; value 3
318 * @param k4 K; key 4
319 * @param v4 V; value 4
320 * @param k5 K; key 5
321 * @param v5 V; value 5
322 * @param k6 K; key 6
323 * @param v6 V; value 6
324 * @return ImmutableMap<K, V>; an ImmutableMap with 6 entries, backed by a LinkedHashMap
325 */
326 @SuppressWarnings("checkstyle:parameternumber")
327 static <K, V> ImmutableMap<K, V> of(final K k1, final V v1, final K k2, final V v2, final K k3, final V v3, final K k4,
328 final V v4, final K k5, final V v5, final K k6, final V v6)
329 {
330 LinkedHashMap<K, V> map = new LinkedHashMap<>();
331 map.put(k1, v1);
332 map.put(k2, v2);
333 map.put(k3, v3);
334 map.put(k4, v4);
335 map.put(k5, v5);
336 map.put(k6, v6);
337 return new ImmutableLinkedHashMap<>(map, Immutable.WRAP);
338 }
339
340 /**
341 * Return an ImmutableMap with 7 entries, backed by a LinkedHashMap.
342 * @param <K> the key type
343 * @param <V> the value type
344 * @param k1 K; key 1
345 * @param v1 V; value 1
346 * @param k2 K; key 2
347 * @param v2 V; value 2
348 * @param k3 K; key 3
349 * @param v3 V; value 3
350 * @param k4 K; key 4
351 * @param v4 V; value 4
352 * @param k5 K; key 5
353 * @param v5 V; value 5
354 * @param k6 K; key 6
355 * @param v6 V; value 6
356 * @param k7 K; key 7
357 * @param v7 V; value 7
358 * @return ImmutableMap<K, V>; an ImmutableMap with 7 entries, backed by a LinkedHashMap
359 */
360 @SuppressWarnings("checkstyle:parameternumber")
361 static <K, V> ImmutableMap<K, V> of(final K k1, final V v1, final K k2, final V v2, final K k3, final V v3, final K k4,
362 final V v4, final K k5, final V v5, final K k6, final V v6, final K k7, final V v7)
363 {
364 LinkedHashMap<K, V> map = new LinkedHashMap<>();
365 map.put(k1, v1);
366 map.put(k2, v2);
367 map.put(k3, v3);
368 map.put(k4, v4);
369 map.put(k5, v5);
370 map.put(k6, v6);
371 map.put(k7, v7);
372 return new ImmutableLinkedHashMap<>(map, Immutable.WRAP);
373 }
374
375 /**
376 * Return an ImmutableMap with 8 entries, backed by a LinkedHashMap.
377 * @param <K> the key type
378 * @param <V> the value type
379 * @param k1 K; key 1
380 * @param v1 V; value 1
381 * @param k2 K; key 2
382 * @param v2 V; value 2
383 * @param k3 K; key 3
384 * @param v3 V; value 3
385 * @param k4 K; key 4
386 * @param v4 V; value 4
387 * @param k5 K; key 5
388 * @param v5 V; value 5
389 * @param k6 K; key 6
390 * @param v6 V; value 6
391 * @param k7 K; key 7
392 * @param v7 V; value 7
393 * @param k8 K; key 8
394 * @param v8 V; value 8
395 * @return ImmutableMap<K, V>; an ImmutableMap with 8 entries, backed by a LinkedHashMap
396 */
397 @SuppressWarnings("checkstyle:parameternumber")
398 static <K, V> ImmutableMap<K, V> of(final K k1, final V v1, final K k2, final V v2, final K k3, final V v3, final K k4,
399 final V v4, final K k5, final V v5, final K k6, final V v6, final K k7, final V v7, final K k8, final V v8)
400 {
401 LinkedHashMap<K, V> map = new LinkedHashMap<>();
402 map.put(k1, v1);
403 map.put(k2, v2);
404 map.put(k3, v3);
405 map.put(k4, v4);
406 map.put(k5, v5);
407 map.put(k6, v6);
408 map.put(k7, v7);
409 map.put(k8, v8);
410 return new ImmutableLinkedHashMap<>(map, Immutable.WRAP);
411 }
412
413 /**
414 * A map entry (key-value pair). The <code>Map.entrySet</code> method returns a collection-view of the map, whose elements
415 * are of this class. The <i>only</i> way to obtain a reference to a map entry is from the iterator of this collection-view.
416 * These <code>ImmutableMap.ImmutableEntry</code> objects are valid <i>only</i> for the duration of the iteration; more
417 * formally, the behavior of a map entry is undefined if the backing map has been modified after the entry was returned by
418 * the iterator, except through the <code>setValue</code> operation on the map entry.
419 * @param <K> key
420 * @param <V> value
421 */
422 class ImmutableEntry<K, V>
423 {
424 /** the wrapped entry. */
425 private final Entry<K, V> wrappedEntry;
426
427 /**
428 * @param wrappedEntry the wrapped entry
429 */
430 public ImmutableEntry(final Entry<K, V> wrappedEntry)
431 {
432 this.wrappedEntry = wrappedEntry;
433 }
434
435 /**
436 * Returns the key corresponding to this entry.
437 * @return the key corresponding to this entry
438 * @throws IllegalStateException implementations may, but are not required to, throw this exception if the entry has
439 * been removed from the backing map.
440 */
441 public K getKey()
442 {
443 return this.wrappedEntry.getKey();
444 }
445
446 /**
447 * Returns the value corresponding to this entry. If the mapping has been removed from the backing map (by the
448 * iterator's <code>remove</code> operation), the results of this call are undefined.
449 * @return the value corresponding to this entry
450 * @throws IllegalStateException implementations may, but are not required to, throw this exception if the entry has
451 * been removed from the backing map.
452 */
453 public V getValue()
454 {
455 return this.wrappedEntry.getValue();
456 }
457
458 /** {@inheritDoc} */
459 @Override
460 public int hashCode()
461 {
462 final int prime = 31;
463 int result = 1;
464 result = prime * result + ((this.wrappedEntry == null) ? 0 : this.wrappedEntry.hashCode());
465 return result;
466 }
467
468 /** {@inheritDoc} */
469 @Override
470 @SuppressWarnings("checkstyle:needbraces")
471 public boolean equals(final Object obj)
472 {
473 if (this == obj)
474 return true;
475 if (obj == null)
476 return false;
477 if (getClass() != obj.getClass())
478 return false;
479 ImmutableEntry<?, ?> other = (ImmutableEntry<?, ?>) obj;
480 if (this.wrappedEntry == null)
481 {
482 if (other.wrappedEntry != null)
483 return false;
484 }
485 else if (!this.wrappedEntry.equals(other.wrappedEntry))
486 return false;
487 return true;
488 }
489
490 /**
491 * Returns a comparator that compares {@link ImmutableMap.ImmutableEntry} in natural order on key.
492 * <p>
493 * The returned comparator is serializable and throws {@link NullPointerException} when comparing an entry with a null
494 * key.
495 * @param <K> the {@link Comparable} type of then map keys
496 * @param <V> the type of the map values
497 * @return a comparator that compares {@link ImmutableMap.ImmutableEntry} in natural order on key.
498 * @see Comparable
499 * @since 1.8
500 */
501 @SuppressWarnings("unchecked")
502 public static <K extends Comparable<? super K>, V> Comparator<ImmutableMap.ImmutableEntry<K, V>> comparingByKey()
503 {
504 return (Comparator<ImmutableMap.ImmutableEntry<K, V>> & Serializable) (c1, c2) -> c1.getKey()
505 .compareTo(c2.getKey());
506 }
507
508 /**
509 * Returns a comparator that compares {@link ImmutableMap.ImmutableEntry} in natural order on value.
510 * <p>
511 * The returned comparator is serializable and throws {@link NullPointerException} when comparing an entry with null
512 * values.
513 * @param <K> the type of the map keys
514 * @param <V> the {@link Comparable} type of the map values
515 * @return a comparator that compares {@link ImmutableMap.ImmutableEntry} in natural order on value.
516 * @see Comparable
517 * @since 1.8
518 */
519 @SuppressWarnings("unchecked")
520 public static <K, V extends Comparable<? super V>> Comparator<ImmutableMap.ImmutableEntry<K, V>> comparingByValue()
521 {
522 return (Comparator<ImmutableMap.ImmutableEntry<K, V>> & Serializable) (c1, c2) -> c1.getValue()
523 .compareTo(c2.getValue());
524 }
525
526 /**
527 * Returns a comparator that compares {@link ImmutableMap.ImmutableEntry} by key using the given {@link Comparator}.
528 * <p>
529 * The returned comparator is serializable if the specified comparator is also serializable.
530 * @param <K> the type of the map keys
531 * @param <V> the type of the map values
532 * @param cmp the key {@link Comparator}
533 * @return a comparator that compares {@link ImmutableMap.ImmutableEntry} by the key.
534 * @since 1.8
535 */
536 @SuppressWarnings("unchecked")
537 public static <K, V> Comparator<ImmutableMap.ImmutableEntry<K, V>> comparingByKey(final Comparator<? super K> cmp)
538 {
539 Objects.requireNonNull(cmp);
540 return (Comparator<ImmutableMap.ImmutableEntry<K, V>> & Serializable) (c1, c2) -> cmp.compare(c1.getKey(),
541 c2.getKey());
542 }
543
544 /**
545 * Returns a comparator that compares {@link ImmutableMap.ImmutableEntry} by value using the given {@link Comparator}.
546 * <p>
547 * The returned comparator is serializable if the specified comparator is also serializable.
548 * @param <K> the type of the map keys
549 * @param <V> the type of the map values
550 * @param cmp the value {@link Comparator}
551 * @return a comparator that compares {@link ImmutableMap.ImmutableEntry} by the value.
552 * @since 1.8
553 */
554 @SuppressWarnings("unchecked")
555 public static <K, V> Comparator<ImmutableMap.ImmutableEntry<K, V>> comparingByValue(final Comparator<? super V> cmp)
556 {
557 Objects.requireNonNull(cmp);
558 return (Comparator<ImmutableMap.ImmutableEntry<K, V>> & Serializable) (c1, c2) -> cmp.compare(c1.getValue(),
559 c2.getValue());
560 }
561
562 @Override
563 public String toString()
564 {
565 return "ImmutableEntry [wrappedEntry=" + this.wrappedEntry + "]";
566 }
567
568 }
569
570 /**
571 * Force to redefine toString.
572 * @return String; a description of this immutable map
573 */
574 @Override
575 String toString();
576
577 }