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-2019 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       * <tt>Integer.MAX_VALUE</tt> elements, returns <tt>Integer.MAX_VALUE</tt>.
31       * @return the number of elements in this immutable collection
32       */
33      int size();
34  
35      /**
36       * Returns <tt>true</tt> if this immutable collection contains no elements.
37       * @return <tt>true</tt> if this immutable collection contains no elements
38       */
39      boolean isEmpty();
40  
41      /**
42       * Returns <tt>true</tt> if this map contains a mapping for the specified key. More formally, returns <tt>true</tt> if and
43       * only if this map contains a mapping for a key <tt>k</tt> such that <tt>(key==null ? k==null : key.equals(k))</tt>. (There
44       * can be at most one such mapping.)
45       * @param key Object; key whose presence in this map is to be tested
46       * @return <tt>true</tt> 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 <tt>true</tt> if this map maps one or more keys to the specified value. More formally, returns <tt>true</tt> if
54       * and only if this map contains at least one mapping to a value <tt>v</tt> such that
55       * <tt>(value==null ? v==null : value.equals(v))</tt>. This operation will probably require time linear in the map size for
56       * most implementations of the <tt>Map</tt> interface.
57       * @param value Object; value whose presence in this map is to be tested
58       * @return <tt>true</tt> 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 <tt>Map.entrySet</tt> method returns a collection-view of the map, whose elements are
189      * 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 <tt>ImmutableMap.ImmutableEntry</tt> 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 <tt>setValue</tt> 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 <tt>remove</tt> 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         public boolean equals(Object obj)
245         {
246             if (this == obj)
247                 return true;
248             if (obj == null)
249                 return false;
250             if (getClass() != obj.getClass())
251                 return false;
252             ImmutableEntry<?, ?> other = (ImmutableEntry<?, ?>) obj;
253             if (this.wrappedEntry == null)
254             {
255                 if (other.wrappedEntry != null)
256                     return false;
257             }
258             else if (!this.wrappedEntry.equals(other.wrappedEntry))
259                 return false;
260             return true;
261         }
262 
263         /**
264          * Returns a comparator that compares {@link ImmutableMap.ImmutableEntry} in natural order on key.
265          * <p>
266          * The returned comparator is serializable and throws {@link NullPointerException} when comparing an entry with a null
267          * key.
268          * @param <K> the {@link Comparable} type of then map keys
269          * @param <V> the type of the map values
270          * @return a comparator that compares {@link ImmutableMap.ImmutableEntry} in natural order on key.
271          * @see Comparable
272          * @since 1.8
273          */
274         @SuppressWarnings("unchecked")
275         public static <K extends Comparable<? super K>, V> Comparator<ImmutableMap.ImmutableEntry<K, V>> comparingByKey()
276         {
277             return (Comparator<ImmutableMap.ImmutableEntry<K, V>> & Serializable) (c1, c2) -> c1.getKey()
278                     .compareTo(c2.getKey());
279         }
280 
281         /**
282          * Returns a comparator that compares {@link ImmutableMap.ImmutableEntry} in natural order on value.
283          * <p>
284          * The returned comparator is serializable and throws {@link NullPointerException} when comparing an entry with null
285          * values.
286          * @param <K> the type of the map keys
287          * @param <V> the {@link Comparable} type of the map values
288          * @return a comparator that compares {@link ImmutableMap.ImmutableEntry} in natural order on value.
289          * @see Comparable
290          * @since 1.8
291          */
292         @SuppressWarnings("unchecked")
293         public static <K, V extends Comparable<? super V>> Comparator<ImmutableMap.ImmutableEntry<K, V>> comparingByValue()
294         {
295             return (Comparator<ImmutableMap.ImmutableEntry<K, V>> & Serializable) (c1, c2) -> c1.getValue()
296                     .compareTo(c2.getValue());
297         }
298 
299         /**
300          * Returns a comparator that compares {@link ImmutableMap.ImmutableEntry} by key using the given {@link Comparator}.
301          * <p>
302          * The returned comparator is serializable if the specified comparator is also serializable.
303          * @param <K> the type of the map keys
304          * @param <V> the type of the map values
305          * @param cmp the key {@link Comparator}
306          * @return a comparator that compares {@link ImmutableMap.ImmutableEntry} by the key.
307          * @since 1.8
308          */
309         @SuppressWarnings("unchecked")
310         public static <K, V> Comparator<ImmutableMap.ImmutableEntry<K, V>> comparingByKey(Comparator<? super K> cmp)
311         {
312             Objects.requireNonNull(cmp);
313             return (Comparator<ImmutableMap.ImmutableEntry<K, V>> & Serializable) (c1, c2) -> cmp.compare(c1.getKey(),
314                     c2.getKey());
315         }
316 
317         /**
318          * Returns a comparator that compares {@link ImmutableMap.ImmutableEntry} by value using the given {@link Comparator}.
319          * <p>
320          * The returned comparator is serializable if the specified comparator is also serializable.
321          * @param <K> the type of the map keys
322          * @param <V> the type of the map values
323          * @param cmp the value {@link Comparator}
324          * @return a comparator that compares {@link ImmutableMap.ImmutableEntry} by the value.
325          * @since 1.8
326          */
327         @SuppressWarnings("unchecked")
328         public static <K, V> Comparator<ImmutableMap.ImmutableEntry<K, V>> comparingByValue(Comparator<? super V> cmp)
329         {
330             Objects.requireNonNull(cmp);
331             return (Comparator<ImmutableMap.ImmutableEntry<K, V>> & Serializable) (c1, c2) -> cmp.compare(c1.getValue(),
332                     c2.getValue());
333         }
334     }
335 
336 }