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.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-2020 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   * @author <a href="https://www.transport.citg.tudelft.nl">Wouter Schakel</a>
23   * @param <K> the key type of content of this Map
24   * @param <V> the value type of content of this Map
25   */
26  public interface ImmutableMap<K, V> extends Serializable
27  {
28      /**
29       * Returns the number of elements in this immutable collection. If this immutable collection contains more than
30       * <code>Integer.MAX_VALUE</code> elements, returns <code>Integer.MAX_VALUE</code>.
31       * @return the number of elements in this immutable collection
32       */
33      int size();
34  
35      /**
36       * Returns <code>true</code> if this immutable collection contains no elements.
37       * @return <code>true</code> if this immutable collection contains no elements
38       */
39      boolean isEmpty();
40  
41      /**
42       * Returns <code>true</code> if this map contains a mapping for the specified key. More formally, returns <code>true</code>
43       * if and only if this map contains a mapping for a key <code>k</code> such that
44       * <code>(key==null ? k==null : key.equals(k))</code>. (There can be at most one such mapping.)
45       * @param key Object; key whose presence in this map is to be tested
46       * @return <code>true</code> if this map contains a mapping for the specified key
47       * @throws ClassCastException if the key is of an inappropriate type for this map
48       * @throws NullPointerException if the specified key is null and this map does not permit null keys
49       */
50      boolean containsKey(Object key);
51  
52      /**
53       * Returns <code>true</code> if this map maps one or more keys to the specified value. More formally, returns
54       * <code>true</code> if and only if this map contains at least one mapping to a value <code>v</code> such that
55       * <code>(value==null ? v==null : value.equals(v))</code>. This operation will probably require time linear in the map size
56       * for most implementations of the <code>Map</code> interface.
57       * @param value Object; value whose presence in this map is to be tested
58       * @return <code>true</code> if this map maps one or more keys to the specified value
59       * @throws ClassCastException if the value is of an inappropriate type for this map
60       * @throws NullPointerException if the specified value is null and this map does not permit null values
61       */
62      boolean containsValue(Object value);
63  
64      /**
65       * Returns the value to which the specified key is mapped, or {@code null} if this map contains no mapping for the key.
66       * <p>
67       * More formally, if this map contains a mapping from a key {@code k} to a value {@code v} such that
68       * {@code (key==null ? k==null : key.equals(k))}, then this method returns {@code v}; otherwise it returns {@code null}.
69       * (There can be at most one such mapping.)
70       * <p>
71       * If this map permits null values, then a return value of {@code null} does not <i>necessarily</i> indicate that the map
72       * contains no mapping for the key; it's also possible that the map explicitly maps the key to {@code null}. The
73       * {@link #containsKey containsKey} operation may be used to distinguish these two cases.
74       * @param key Object; the key whose associated value is to be returned
75       * @return the value to which the specified key is mapped, or {@code null} if this map contains no mapping for the key
76       * @throws ClassCastException if the key is of an inappropriate type for this map
77       * @throws NullPointerException if the specified key is null and this map does not permit null keys
78       */
79      V get(Object key);
80  
81      /**
82       * Returns a {@link Set} view of the keys contained in this map.
83       * @return an immutable set of the keys contained in this map
84       */
85      ImmutableSet<K> keySet();
86  
87      /**
88       * Returns a {@link Set} view of the entries contained in this map.
89       * @return an immutable set of the entries contained in this map
90       */
91      ImmutableSet<ImmutableEntry<K, V>> entrySet();
92  
93      /**
94       * Returns a {@link ImmutableCollection} view of the values contained in this map.
95       * @return an immutable collection view of the values contained in this map
96       */
97      ImmutableCollection<V> values();
98  
99      /**
100      * Returns the value to which the specified key is mapped, or {@code defaultValue} if this map contains no mapping for the
101      * key. The default implementation makes no guarantees about synchronization or atomicity properties of this method. Any
102      * implementation providing atomicity guarantees must override this method and document its concurrency properties.
103      * @param key Object; the key whose associated value is to be returned
104      * @param defaultValue V; the default mapping of the key
105      * @return the value to which the specified key is mapped, or {@code defaultValue} if this map contains no mapping for the
106      *         key
107      * @throws ClassCastException if the key is of an inappropriate type for this map
108      * @throws NullPointerException if the specified key is null and this map does not permit null keys
109      */
110     default V getOrDefault(Object key, V defaultValue)
111     {
112         V v = get(key);
113         return ((v != null) || containsKey(key)) ? v : defaultValue;
114     }
115 
116     /**
117      * Performs the given action for each entry in this map until all entries have been processed or the action throws an
118      * exception. Unless otherwise specified by the implementing class, actions are performed in the order of entry set
119      * iteration (if an iteration order is specified.) Exceptions thrown by the action are relayed to the caller. The default
120      * implementation makes no guarantees about synchronization or atomicity properties of this method. Any implementation
121      * providing atomicity guarantees must override this method and document its concurrency properties.
122      * @param action BiConsumer&lt;? super K,? super V&gt;; The action to be performed for each entry
123      * @throws NullPointerException if the specified action is null
124      * @throws ConcurrentModificationException if an entry is found to be removed during iteration
125      */
126     default void forEach(BiConsumer<? super K, ? super V> action)
127     {
128         Objects.requireNonNull(action);
129         for (ImmutableEntry<K, V> entry : entrySet())
130         {
131             K k;
132             V v;
133             try
134             {
135                 k = entry.getKey();
136                 v = entry.getValue();
137             }
138             catch (IllegalStateException ise)
139             {
140                 // this usually means the entry is no longer in the map.
141                 throw new ConcurrentModificationException(ise);
142             }
143             action.accept(k, v);
144         }
145     }
146 
147     /**
148      * Returns a modifiable copy of this immutable list.
149      * @return a modifiable copy of this immutable list.
150      */
151     Map<K, V> toMap();
152 
153     /**
154      * Force to redefine equals for the implementations of immutable collection classes.
155      * @param obj Object; the object to compare this collection with
156      * @return whether the objects are equal
157      */
158     @Override
159     boolean equals(Object obj);
160 
161     /**
162      * Force to redefine hashCode for the implementations of immutable collection classes.
163      * @return the calculated hashCode
164      */
165     @Override
166     int hashCode();
167 
168     /**
169      * Return whether the internal storage is a wrapped pointer to the original map. If true, this means that anyone holding a
170      * pointer to this data structure can still change it. The users of the ImmutableMap itself can, however, not make any
171      * changes.
172      * @return boolean; whether the internal storage is a wrapped pointer to the original map
173      */
174     boolean isWrap();
175 
176     /**
177      * Return whether the internal storage is a (shallow) copy of the original map. If true, this means that anyone holding a
178      * pointer to the original of the data structure can not change it anymore. Nor can the users of the ImmutableMap itself
179      * make any changes.
180      * @return boolean; whether the internal storage is a safe copy of the original map
181      */
182     default boolean isCopy()
183     {
184         return !isWrap();
185     }
186 
187     /**
188      * A map entry (key-value pair). The <code>Map.entrySet</code> method returns a collection-view of the map, whose elements
189      * 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.
190      * These <code>ImmutableMap.ImmutableEntry</code> objects are valid <i>only</i> for the duration of the iteration; more
191      * formally, the behavior of a map entry is undefined if the backing map has been modified after the entry was returned by
192      * the iterator, except through the <code>setValue</code> operation on the map entry.
193      * @param <K> key
194      * @param <V> value
195      */
196     class ImmutableEntry<K, V>
197     {
198         /** the wrapped entry. */
199         private final Entry<K, V> wrappedEntry;
200 
201         /**
202          * @param wrappedEntry the wrapped entry
203          */
204         public ImmutableEntry(final Entry<K, V> wrappedEntry)
205         {
206             this.wrappedEntry = wrappedEntry;
207         }
208 
209         /**
210          * Returns the key corresponding to this entry.
211          * @return the key corresponding to this entry
212          * @throws IllegalStateException implementations may, but are not required to, throw this exception if the entry has
213          *             been removed from the backing map.
214          */
215         public K getKey()
216         {
217             return this.wrappedEntry.getKey();
218         }
219 
220         /**
221          * Returns the value corresponding to this entry. If the mapping has been removed from the backing map (by the
222          * iterator's <code>remove</code> operation), the results of this call are undefined.
223          * @return the value corresponding to this entry
224          * @throws IllegalStateException implementations may, but are not required to, throw this exception if the entry has
225          *             been removed from the backing map.
226          */
227         public V getValue()
228         {
229             return this.wrappedEntry.getValue();
230         }
231 
232         /** {@inheritDoc} */
233         @Override
234         public int hashCode()
235         {
236             final int prime = 31;
237             int result = 1;
238             result = prime * result + ((this.wrappedEntry == null) ? 0 : this.wrappedEntry.hashCode());
239             return result;
240         }
241 
242         /** {@inheritDoc} */
243         @Override
244         @SuppressWarnings("checkstyle:needbraces")
245         public boolean equals(final Object obj)
246         {
247             if (this == obj)
248                 return true;
249             if (obj == null)
250                 return false;
251             if (getClass() != obj.getClass())
252                 return false;
253             ImmutableEntry<?, ?> other = (ImmutableEntry<?, ?>) obj;
254             if (this.wrappedEntry == null)
255             {
256                 if (other.wrappedEntry != null)
257                     return false;
258             }
259             else if (!this.wrappedEntry.equals(other.wrappedEntry))
260                 return false;
261             return true;
262         }
263 
264         /**
265          * Returns a comparator that compares {@link ImmutableMap.ImmutableEntry} in natural order on key.
266          * <p>
267          * The returned comparator is serializable and throws {@link NullPointerException} when comparing an entry with a null
268          * key.
269          * @param <K> the {@link Comparable} type of then map keys
270          * @param <V> the type of the map values
271          * @return a comparator that compares {@link ImmutableMap.ImmutableEntry} in natural order on key.
272          * @see Comparable
273          * @since 1.8
274          */
275         @SuppressWarnings("unchecked")
276         public static <K extends Comparable<? super K>, V> Comparator<ImmutableMap.ImmutableEntry<K, V>> comparingByKey()
277         {
278             return (Comparator<ImmutableMap.ImmutableEntry<K, V>> & Serializable) (c1, c2) -> c1.getKey()
279                     .compareTo(c2.getKey());
280         }
281 
282         /**
283          * Returns a comparator that compares {@link ImmutableMap.ImmutableEntry} in natural order on value.
284          * <p>
285          * The returned comparator is serializable and throws {@link NullPointerException} when comparing an entry with null
286          * values.
287          * @param <K> the type of the map keys
288          * @param <V> the {@link Comparable} type of the map values
289          * @return a comparator that compares {@link ImmutableMap.ImmutableEntry} in natural order on value.
290          * @see Comparable
291          * @since 1.8
292          */
293         @SuppressWarnings("unchecked")
294         public static <K, V extends Comparable<? super V>> Comparator<ImmutableMap.ImmutableEntry<K, V>> comparingByValue()
295         {
296             return (Comparator<ImmutableMap.ImmutableEntry<K, V>> & Serializable) (c1, c2) -> c1.getValue()
297                     .compareTo(c2.getValue());
298         }
299 
300         /**
301          * Returns a comparator that compares {@link ImmutableMap.ImmutableEntry} by key using the given {@link Comparator}.
302          * <p>
303          * The returned comparator is serializable if the specified comparator is also serializable.
304          * @param <K> the type of the map keys
305          * @param <V> the type of the map values
306          * @param cmp the key {@link Comparator}
307          * @return a comparator that compares {@link ImmutableMap.ImmutableEntry} by the key.
308          * @since 1.8
309          */
310         @SuppressWarnings("unchecked")
311         public static <K, V> Comparator<ImmutableMap.ImmutableEntry<K, V>> comparingByKey(final Comparator<? super K> cmp)
312         {
313             Objects.requireNonNull(cmp);
314             return (Comparator<ImmutableMap.ImmutableEntry<K, V>> & Serializable) (c1, c2) -> cmp.compare(c1.getKey(),
315                     c2.getKey());
316         }
317 
318         /**
319          * Returns a comparator that compares {@link ImmutableMap.ImmutableEntry} by value using the given {@link Comparator}.
320          * <p>
321          * The returned comparator is serializable if the specified comparator is also serializable.
322          * @param <K> the type of the map keys
323          * @param <V> the type of the map values
324          * @param cmp the value {@link Comparator}
325          * @return a comparator that compares {@link ImmutableMap.ImmutableEntry} by the value.
326          * @since 1.8
327          */
328         @SuppressWarnings("unchecked")
329         public static <K, V> Comparator<ImmutableMap.ImmutableEntry<K, V>> comparingByValue(final Comparator<? super V> cmp)
330         {
331             Objects.requireNonNull(cmp);
332             return (Comparator<ImmutableMap.ImmutableEntry<K, V>> & Serializable) (c1, c2) -> cmp.compare(c1.getValue(),
333                     c2.getValue());
334         }
335 
336         @Override
337         public String toString()
338         {
339             return "ImmutableEntry [wrappedEntry=" + this.wrappedEntry + "]";
340         }
341         
342     }
343 
344     /**
345      * Force to redefine toString.
346      * @return String; a description of this immutable list
347      */
348     @Override
349     String toString();
350 
351 }