View Javadoc
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&lt;? super K,? super V&gt;; 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&lt;K, V&gt;; 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&lt;K, V&gt;; 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&lt;K, V&gt;; 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&lt;K, V&gt;; 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&lt;K, V&gt;; 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&lt;K, V&gt;; 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&lt;K, V&gt;; 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&lt;K, V&gt;; 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&lt;K, V&gt;; 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 }