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