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