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<? super K,? super V>; 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 }