001/* 002 * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. Oracle designates this 008 * particular file as subject to the "Classpath" exception as provided 009 * by Oracle in the LICENSE file that accompanied this code. 010 * 011 * This code is distributed in the hope that it will be useful, but WITHOUT 012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 014 * version 2 for more details (a copy is included in the LICENSE file that 015 * accompanied this code). 016 * 017 * You should have received a copy of the GNU General Public License version 018 * 2 along with this work; if not, write to the Free Software Foundation, 019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 020 * 021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 022 * or visit www.oracle.com if you need additional information or have any 023 * questions. 024 */ 025package Torello.Java.ReadOnly; 026 027import java.io.Serializable; 028 029import java.util.function.BiConsumer; 030import java.util.function.Consumer; 031import java.util.function.Predicate; 032import java.util.function.Function; 033 034import Torello.Java.Additional.Tuple2; 035 036import java.util.*; 037 038/** 039 * Immutable Wrapper for <CODE>java.util.HashMap</CODE>, found in the "Java Collections 040 * Framework". 041 * 042 * <EMBED CLASS=globalDefs DATA-JDK=HashMap DATA-ENDING=Map> 043 * <EMBED CLASS='external-html' DATA-FILE-ID=DATA_CLASS> 044 * 045 * @param <K> the type of keys maintained by this map 046 * @param <V> the type of mapped values 047 */ 048@Torello.JavaDoc.JDHeaderBackgroundImg(EmbedTagFileID="JDHBI_MAIN") 049@SuppressWarnings("unchecked") 050public class ReadOnlyHashMap<K, V> implements ReadOnlyMap<K, V>, Serializable 051{ 052 // ******************************************************************************************** 053 // ******************************************************************************************** 054 // Protected & Private Fields, Methods, Statics 055 // ******************************************************************************************** 056 // ******************************************************************************************** 057 058 059 /** <EMBED CLASS='external-html' DATA-FILE-ID=SVUID> */ 060 protected static final long serialVersionUID = 1; 061 062 // Minor Optimization where new HashMap's that have no contents always re-use this static 063 // instance. Since this instance is completely empty, the Raw-Types things is irrelevant. 064 065 @SuppressWarnings("rawtypes") 066 private static final HashMap EMPTY_HASH_MAP = new HashMap(0, 0.75f); 067 068 // Singleton & Empty ReadOnlyHashMap, Uses the "Supplier Constructor" 069 @SuppressWarnings("rawtypes") 070 private static final ReadOnlyHashMap EMPTY_READONLY_HASH_MAP = 071 new ReadOnlyHashMap(EMPTY_HASH_MAP); 072 073 // The actual HashMap used by this instance. 074 private final HashMap<K, V> hashMap; 075 076 // TRUE => This was built using the class ROHashMapBuilder 077 // FALSE => This was built using the clone() of a standard java.util.HashMap constructor 078 079 private final boolean fromBuilderOrHashMap; 080 081 // Mimics the C++ Keyword/Concept of "Friend Class". Is "Friends With" ROHashMapBuilder 082 static class AccessBadge { private AccessBadge() { } } 083 private static final AccessBadge friendClassBadge = new AccessBadge(); 084 085 086 // ******************************************************************************************** 087 // ******************************************************************************************** 088 // Builder, Acess-Badge Constructor - and Static "Empty" getter (which is used by the builder) 089 // ******************************************************************************************** 090 // ******************************************************************************************** 091 092 093 /** 094 * Returns an empty, <B STYLE='color: red;'>singleton</B>, instance of {@code ReadOnlyHashMap}. 095 * @param <X> Returned {@link ReadOnlyMap}'s Key-Type. 096 * @param <Y> Returned {@link ReadOnlyMap}'s Value-Type. 097 * 098 * @return An empty map. Since this map is both empty & read-only, a raw-type singleton 099 * will suffice for all operations offered by this clas. 100 */ 101 public static <X, Y> ReadOnlyHashMap<X, Y> emptyROHM() 102 { return (ReadOnlyHashMap<X, Y>) EMPTY_READONLY_HASH_MAP; } 103 104 // To all the readers out there following along: The "AccessBadge" thing is just a slightly 105 // wimpier substitute for the C++ keyword / concept 'friend' or "Friend Class". It means this 106 // constructor is (for all intents and purposes) a private-constructor, except for the class 107 // ROHashMapBuilder 108 // 109 // This is the Constructor used by the Builder. It has a "Zero-Size" Optimization 110 111 ReadOnlyHashMap(ROHashMapBuilder<K, V> rohmb, ROHashMapBuilder.AccessBadge badge) 112 { 113 Objects.requireNonNull(badge, "Access Badge is null. Requires Friend-Class Badge"); 114 115 fromBuilderOrHashMap = true; 116 this.hashMap = rohmb; 117 } 118 119 120 // ******************************************************************************************** 121 // ******************************************************************************************** 122 // Modified-Original Constructors 123 // ******************************************************************************************** 124 // ******************************************************************************************** 125 126 127 /** 128 * Copies the contents of parameter {@code 'map'}, and saves saves it, thereby guaranteeing 129 * {@code 'this'} instance is Read-Only and fully-shielded from outside modification. 130 * 131 * @param map The {@code map} to be copied into {@code 'this'} instance internal and private 132 * {@code 'hashMap'} field. 133 */ 134 public ReadOnlyHashMap(Map<K, V> map) 135 { 136 fromBuilderOrHashMap = false; 137 138 // Empty Optimization (throw away, completely, the reference, use static-constant) 139 this.hashMap = (map.size() == 0) ? ((HashMap<K, V>) EMPTY_HASH_MAP) : new HashMap<>(map); 140 } 141 142 /** 143 * If only a small amount of processing needs to be done on the contents of some Java 144 * Map, and using an entire Builder-Class seems disproportionately complex - <I>this 145 * constructor can convert any Java {@code Map} into a {@code ReadOnlyHashMap}, using 146 * a simple {@code 'mapTranslator'}</I>. 147 * 148 * @param <X> The Key-Type of the User-Provided {@code Map}. 149 * @param <Y> The Value-Type of the User-Provided {@code Map}. 150 * 151 * @param refHolder This must a non-null instance of {@link Tuple2}. The provided 152 * {@code Consumer} is just that, a {@code 'Consumer'} rather than a {@code 'Function'}, since 153 * the results of each translation must be assigned to the values inside this tuple in order 154 * for them to be inserted into this {@code ReadOnlyHashMap}. 155 * 156 * @param map Any Java {@code Map}. 157 * 158 * @param mapTranslator A function for mapping the iterated elements of Map-Types {@code 'X'} 159 * and {@code 'Y'}, into the actual {@code HashMap's} Key-Type {@code 'K'}, and Value-Type 160 * {@code 'V'}. The results of this translation must be placed into the fields inside 161 * {@code 'refHolder'}. 162 * 163 * <BR /><BR />If this parameter is passed null, this method will throw a 164 * {@code NullPointerException}. 165 * 166 * @param filter An optional filter that can be used to prevent & prohibit any chosen 167 * elements from input {@code 'map'} from being inserted into {@code 'this'} 168 * {@code ReadOnlyTreeMap}. 169 * 170 * <BR /><BR />This parameter may be passed null, and if it is, it will be silently ignored, 171 * and all entries present inside {@code 'map'} will be processed and inserted into 172 * {@code 'this'} 173 * 174 * @param loadFactor <EMBED CLASS='external-html' DATA-FILE-ID=HASH_LOAD_FACTOR> 175 * 176 * @throws NullPointerException if either parameter {@code 'i'} or parameter 177 * {@code 'mapTranslator'} is passed null. 178 * 179 * @throws IllegalArgumentException if the load factor is nonpositive 180 */ 181 public <X, Y> ReadOnlyHashMap( 182 Tuple2<K, V> refHolder, 183 Map<X, Y> map, 184 Consumer<Map.Entry<X, Y>> mapTranslator, 185 Predicate<Map.Entry<X, Y>> filter, 186 Float loadFactor 187 ) 188 { 189 Objects.requireNonNull(refHolder, ROHelpers.NULL_MSG + "'refHolder'"); 190 Objects.requireNonNull(mapTranslator, ROHelpers.NULL_MSG + "'mapTranslator'"); 191 192 fromBuilderOrHashMap = false; 193 194 HashMap<K, V> hashMap = (loadFactor != null) 195 ? new HashMap<>(map.size(), loadFactor) 196 : new HashMap<>(map.size()); 197 198 if (filter != null) 199 200 for (Map.Entry<X, Y> entry : map.entrySet()) 201 { 202 if (! filter.test(entry)) continue; 203 mapTranslator.accept(entry); 204 hashMap.put(refHolder.a, refHolder.b); 205 } 206 207 else 208 209 for (Map.Entry<X, Y> entry : map.entrySet()) 210 { 211 mapTranslator.accept(entry); 212 hashMap.put(refHolder.a, refHolder.b); 213 } 214 215 // Empty Optimization (throw away, completely, the reference, use static-constant) 216 this.hashMap = (hashMap.size() == 0) ? ((HashMap<K, V>) EMPTY_HASH_MAP) : hashMap; 217 } 218 219 220 // ******************************************************************************************** 221 // ******************************************************************************************** 222 // New Constructors, January 2024 223 // ******************************************************************************************** 224 // ******************************************************************************************** 225 226 227 /** 228 * Constructs an instance of {@code ReadOnlyHashMap} that contains the keys present in 229 * parameter {@code 'keys'}, and values generated by {@code 'valueMapper'} - using each of the 230 * {@code 'keys'} as input. 231 * 232 * <EMBED CLASS='external-html' DATA-FILE-ID=LOOK_AT_IT> 233 * 234 * @param keys Any Java {@code Iterable} instance. 235 * @param valueMapper A user provided function to compute a map value, based on a map key. 236 * @param filter <EMBED CLASS='external-html' DATA-FILE-ID=MAP_ITERABLE_FILTER> 237 * @param loadFactor <EMBED CLASS='external-html' DATA-FILE-ID=HASH_LOAD_FACTOR> 238 * @param sizeIfKnown <EMBED CLASS='external-html' DATA-FILE-ID=MAP_TABLE_ICAPACITY> 239 * 240 * @throws NullPointerException if either {@code 'keys'} or {@code 'valueMapper'} are passed 241 * null. 242 */ 243 public ReadOnlyHashMap( 244 Iterable<? extends K> keys, 245 Function<? super K, ? extends V> valueMapper, 246 Predicate<? super K> filter, 247 Float loadFactor, 248 Integer sizeIfKnown 249 ) 250 { 251 Objects.requireNonNull(keys, ROHelpers.NULL_MSG + "'keys'"); 252 Objects.requireNonNull(valueMapper, ROHelpers.NULL_MSG + "'valueMapper'"); 253 254 fromBuilderOrHashMap = false; 255 256 HashMap<K, V> hashMap = new HashMap<>( 257 ((sizeIfKnown == null) ? 16 : sizeIfKnown), 258 ((loadFactor == null) ? 0.75f : loadFactor) 259 ); 260 261 if (filter == null) 262 for (K key : keys) 263 hashMap.put(key, valueMapper.apply(key)); 264 265 else 266 for (K key : keys) 267 if (filter.test(key)) 268 hashMap.put(key, valueMapper.apply(key)); 269 270 // Empty Optimization (throw away, completely, the reference, use static-constant) 271 this.hashMap = (hashMap.size() == 0) ? ((HashMap<K, V>) EMPTY_HASH_MAP) : hashMap; 272 } 273 274 /** 275 * Constructs an instance of {@code ReadOnlyHashMap} that has been populated by the Key-Value 276 * Pairs left in {@code 'refHolder'} by each invocation of the {@code Runnable} parameter 277 * {@code 'computeNextEntry'}. Key-Value Pairs are inserted until an invocation of the 278 * {@code Runnable} leaves null in {@code refHolder.a} and {@code refHolder.b} 279 * 280 * <EMBED CLASS='external-html' DATA-FILE-ID=LOOK_AT_IT> 281 * 282 * @param refHolder <EMBED CLASS='external-html' DATA-FILE-ID=REF_HOLDER> 283 * @param computeNextEntry <EMBED CLASS='external-html' DATA-FILE-ID=COMPUTE_NEXT_RUN> 284 * @param loadFactor <EMBED CLASS='external-html' DATA-FILE-ID=HASH_LOAD_FACTOR> 285 * @param sizeIfKnown <EMBED CLASS='external-html' DATA-FILE-ID=MAP_TABLE_ICAPACITY> 286 * 287 * @throws NullPointerException if either {@code 'refHolder'} or {@code 'computeNextEntry'} are 288 * passed null. 289 */ 290 public ReadOnlyHashMap( 291 Tuple2<K, V> refHolder, 292 Runnable computeNextEntry, 293 Float loadFactor, 294 Integer sizeIfKnown 295 ) 296 { 297 Objects.requireNonNull(refHolder, ROHelpers.NULL_MSG + "'refHolder'"); 298 Objects.requireNonNull(computeNextEntry, ROHelpers.NULL_MSG + "'computeNextEntry'"); 299 300 fromBuilderOrHashMap = false; 301 302 HashMap<K, V> hashMap = new HashMap<>( 303 ((sizeIfKnown == null) ? 16 : sizeIfKnown), 304 ((loadFactor == null) ? 0.75f : loadFactor) 305 ); 306 307 do 308 { 309 computeNextEntry.run(); 310 if ((refHolder.a == null) && (refHolder.b == null)) break; 311 hashMap.put(refHolder.a, refHolder.b); 312 } 313 while (true); 314 315 // Empty Optimization (throw away, completely, the reference, use static-constant) 316 this.hashMap = (hashMap.size() == 0) ? ((HashMap<K, V>) EMPTY_HASH_MAP) : hashMap; 317 } 318 319 /** 320 * Populates an instance of {@code ReadOnlyHashMap} by iterating the input {@code 'source'} 321 * iterable, and passing each value returned by that {@code Iterator} to the 322 * {@code 'computeNextEntry'} Java {@code Consumer}. 323 * 324 * <BR /><BR />It is the programmer's responsibility to properly place each Key-Value Pair 325 * that is intending to be inserted into the final {@code Map} instance into the 326 * {@code 'refHolder'} instance. After each invocation of {@code 'computeNextEntry'}, this 327 * constructor's logic will retrieve the values within {@code 'refHolder.a'} and 328 * {@code 'refHolder.b'} and insert them into this instance internal {@code HashMap}. 329 * 330 * <EMBED CLASS='external-html' DATA-FILE-ID=LOOK_AT_IT> 331 * 332 * @param <X> The type of the elements inside {@code 'source'} 333 * @param source Any Java {@code Iterable} instance. 334 * @param refHolder <EMBED CLASS='external-html' DATA-FILE-ID=REF_HOLDER> 335 * @param computeNextEntry <EMBED CLASS='external-html' DATA-FILE-ID=COMPUTE_NEXT_CONS> 336 * @param filter <EMBED CLASS='external-html' DATA-FILE-ID=MAP_ITERABLE_FILTER> 337 * @param loadFactor <EMBED CLASS='external-html' DATA-FILE-ID=HASH_LOAD_FACTOR> 338 * @param sizeIfKnown <EMBED CLASS='external-html' DATA-FILE-ID=MAP_TABLE_ICAPACITY> 339 * 340 * @throws NullPointerException if either {@code 'refHolder'}, {@code 'computeNextEntry'} or 341 * {@code 'source'} are passed null. 342 */ 343 public <X> ReadOnlyHashMap( 344 Iterable<X> source, 345 Tuple2<K, V> refHolder, 346 Consumer<? super X> computeNextEntry, 347 Predicate<? super X> filter, 348 Float loadFactor, 349 Integer sizeIfKnown 350 ) 351 { 352 Objects.requireNonNull(refHolder, ROHelpers.NULL_MSG + "'refHolder'"); 353 Objects.requireNonNull(computeNextEntry, ROHelpers.NULL_MSG + "'computeNextEntry'"); 354 355 fromBuilderOrHashMap = false; 356 357 HashMap<K, V> hashMap = new HashMap<>( 358 ((sizeIfKnown == null) ? 16 : sizeIfKnown), 359 ((loadFactor == null) ? 0.75f : loadFactor) 360 ); 361 362 X x; // temp var 363 Iterator<X> iter = source.iterator(); 364 365 if (filter == null) 366 367 while (iter.hasNext()) 368 { 369 computeNextEntry.accept(iter.next()); 370 hashMap.put(refHolder.a, refHolder.b); 371 } 372 373 else 374 375 while (iter.hasNext()) 376 if (filter.test(x = iter.next())) 377 { 378 computeNextEntry.accept(x); 379 hashMap.put(refHolder.a, refHolder.b); 380 } 381 382 // Empty Optimization (throw away, completely, the reference, use static-constant) 383 this.hashMap = (hashMap.size() == 0) ? ((HashMap<K, V>) EMPTY_HASH_MAP) : hashMap; 384 } 385 386 387 // ******************************************************************************************** 388 // ******************************************************************************************** 389 // Array Constructors, March 2024 390 // ******************************************************************************************** 391 // ******************************************************************************************** 392 393 394 /** 395 * Retrieves elements from the VarArgs Generic-Array parameter {@code 'elements'}, and 396 * subsequently invokes the {@code 'computeNextEntry'} processor to populate this 397 * {@code ReadOnlyHashMap}. 398 * 399 * @param <X> The type of array parameter {@code 'elements'} 400 * @param refHolder <EMBED CLASS='external-html' DATA-FILE-ID=REF_HOLDER> 401 * @param computeNextEntry <EMBED CLASS='external-html' DATA-FILE-ID=ARR_COMPUTE_NEXT_CONS> 402 * @param filter <EMBED CLASS='external-html' DATA-FILE-ID=MAP_ARRAY_FILTER> 403 * @param loadFactor <EMBED CLASS='external-html' DATA-FILE-ID=HASH_LOAD_FACTOR> 404 * @param elements Any Generic VarArgs-Array 405 * @throws ClassCastException <EMBED CLASS='external-html' DATA-FILE-ID=PRIM_ARR_CCEX> 406 * 407 * @throws NullPointerException if either {@code 'refHolder'} or {@code 'computeNextEntry'} 408 * are passed null 409 */ 410 public <X> ReadOnlyHashMap( 411 Tuple2<K, V> refHolder, 412 Consumer<? super X> computeNextEntry, 413 Predicate<? super X> filter, 414 Float loadFactor, 415 X... elements 416 ) 417 { 418 Objects.requireNonNull(refHolder, ROHelpers.NULL_MSG + "'refHolder'"); 419 Objects.requireNonNull(computeNextEntry, ROHelpers.NULL_MSG + "'computeNextEntry'"); 420 421 this.fromBuilderOrHashMap = false; 422 423 HashMap<K, V> hashMap = 424 new HashMap<>(elements.length, ((loadFactor == null) ? 0.75f : loadFactor)); 425 426 if (filter == null) for (X e : elements) 427 { 428 computeNextEntry.accept(e); 429 hashMap.put(refHolder.a, refHolder.b); 430 } 431 432 else for (X x : elements) if (filter.test(x)) 433 { 434 computeNextEntry.accept(x); 435 hashMap.put(refHolder.a, refHolder.b); 436 } 437 438 // Empty Optimization (throw away, completely, the reference, use static-constant) 439 this.hashMap = (hashMap.size() == 0) ? ((HashMap<K, V>) EMPTY_HASH_MAP) : hashMap; 440 } 441 442 /** 443 * Retrieves elements from the Java Primitive-Array parameter {@code 'primitiveArray'}, and 444 * subsequently invokes the {@code 'computeNextEntry'} processor to populate this 445 * {@code ReadOnlyHashMap}. 446 * 447 * @param filter <EMBED CLASS='external-html' DATA-FILE-ID=PRED_FILT_PRIM> 448 * @param computeNextEntry <EMBED CLASS='external-html' DATA-FILE-ID=ARR_COMPUTE_NEXT_CONS> 449 * @param refHolder <EMBED CLASS='external-html' DATA-FILE-ID=REF_HOLDER> 450 * @param loadFactor <EMBED CLASS='external-html' DATA-FILE-ID=HASH_LOAD_FACTOR> 451 * @param primitiveArray Any Java Primitive-Array 452 * @throws ClassCastException <EMBED CLASS='external-html' DATA-FILE-ID=PRIM_ARR_CCEX> 453 * 454 * @throws NullPointerException if either {@code 'refHolder'} or {@code 'computeNextEntry'} 455 * are passed null 456 * 457 */ 458 public <X> ReadOnlyHashMap( 459 Predicate<?> filter, 460 Consumer<?> computeNextEntry, 461 Tuple2<K, V> refHolder, 462 Float loadFactor, 463 Object primitiveArray 464 ) 465 { 466 Objects.requireNonNull(refHolder, ROHelpers.NULL_MSG + "'refHolder'"); 467 Objects.requireNonNull(computeNextEntry, ROHelpers.NULL_MSG + "'computeNextEntry'"); 468 469 this.fromBuilderOrHashMap = false; 470 471 HashMap<K, V> hashMap = ROHelpers.buildROMap( 472 primitiveArray, 473 (int arrayLen) -> new HashMap<>(arrayLen, ((loadFactor == null) ? 0.75f : loadFactor)), 474 filter, 475 refHolder, 476 computeNextEntry 477 ); 478 479 // Empty Optimization (throw away, completely, the reference, use static-constant) 480 this.hashMap = (hashMap.size() == 0) ? ((HashMap<K, V>) EMPTY_HASH_MAP) : hashMap; 481 } 482 483 484 // ******************************************************************************************** 485 // ******************************************************************************************** 486 // Convert to java.util Types 487 // ******************************************************************************************** 488 // ******************************************************************************************** 489 490 491 /** 492 * Clone's {@code 'this'} instance internal {@code HashMap<K, V>} field, and returns it. 493 * <EMBED CLASS='external-html' DATA-TYPE=HashMap DATA-FILE-ID=CLONE_TO> 494 * 495 * @return An independent, mutable copy of {@code 'this'} instance internal 496 * {@code HashMap<K, V>} data-structure. 497 */ 498 public HashMap<K, V> cloneToHashMap() 499 { 500 if (! fromBuilderOrHashMap) return (HashMap<K, V>) this.hashMap.clone(); 501 502 HashMap<K, V> ret = new HashMap<K, V>(); 503 504 for (Map.Entry<K, V> e : 505 ((ROHashMapBuilder<K, V>) this.hashMap)._entrySet(friendClassBadge)) 506 507 ret.put(e.getKey(), e.getValue()); 508 509 return ret; 510 } 511 512 /** 513 * Invokes {@code java.util.Collections.unmodifiableMap} on the internal {@code HashMap}. 514 * <EMBED CLASS='external-html' DATA-RET_TYPE=Map DATA-FILE-ID=WRAP_TO_IMMUTABLE> 515 * 516 * @return A {@code Map} which adheres to the JDK interface {@code java.util.Map}, but throws 517 * an {@code UnsupportedOperationException} if a user attempts to invoke a Mutator-Method on 518 * the returned instance. 519 */ 520 public Map<K, V> wrapToImmutableMap() 521 { return Collections.unmodifiableMap(this.hashMap); } 522 523 524 // ******************************************************************************************** 525 // ******************************************************************************************** 526 // Original JDK Methods, java.util.HashMap 527 // ******************************************************************************************** 528 // ******************************************************************************************** 529 530 531 /** 532 * Returns the number of key-value mappings in this map. 533 * @return the number of key-value mappings in this map 534 */ 535 public int size() 536 { return this.hashMap.size(); } 537 538 /** 539 * Returns {@code TRUE} if this map contains no key-value mappings. 540 * @return {@code TRUE} if this map contains no key-value mappings 541 */ 542 public boolean isEmpty() 543 { return this.hashMap.isEmpty(); } 544 545 /** 546 * Returns the value to which the specified key is mapped, or {@code null} if this map contains 547 * no mapping for the key. 548 * 549 * <BR /><BR />More formally, if this map contains a mapping from a key {@code k} to a value 550 * {@code v} such that {@code (key==null ? k==null : key.equals(k))}, then this method returns 551 * {@code v}; otherwise it returns {@code null}. (There can be at most one such mapping.) 552 * 553 * <BR /><BR />A return value of {@code null} does not <i>necessarily</i> indicate that the map 554 * contains no mapping for the key; it's also possible that the map explicitly maps the key to 555 * {@code null}. The {@link #containsKey containsKey} operation may be used to distinguish 556 * these two cases. 557 */ 558 public V get(Object key) 559 { return this.hashMap.get(key); } 560 561 /** 562 * Returns {@code TRUE} if this map contains a mapping for the specified key. 563 * @param key The key whose presence in this map is to be tested 564 * @return {@code TRUE} if this map contains a mapping for the specified key. 565 */ 566 public boolean containsKey(Object key) 567 { return this.hashMap.containsKey(key); } 568 569 /** 570 * Returns {@code TRUE} if this map maps one or more keys to the specified value. 571 * @param value value whose presence in this map is to be tested 572 * @return {@code TRUE} if this map maps one or more keys to the specified value 573 */ 574 public boolean containsValue(Object value) 575 { return this.hashMap.containsValue(value); } 576 577 578 // ******************************************************************************************** 579 // ******************************************************************************************** 580 // The Builder AccessDenied Issue 581 // ******************************************************************************************** 582 // ******************************************************************************************** 583 584 585 /** 586 * Returns a {@link ReadOnlySet} view of the keys contained in this map. 587 * @return a set view of the keys contained in this map 588 */ 589 public ReadOnlySet<K> keySet() 590 { 591 return InterfaceBuilder.toReadOnlySet( 592 fromBuilderOrHashMap 593 ? ((ROHashMapBuilder<K, V>) this.hashMap)._keySet(friendClassBadge) 594 : this.hashMap.keySet() 595 ); 596 } 597 598 /** 599 * Returns a {@link ReadOnlyCollection} view of the values contained in this map. 600 * @return a view of the values contained in this map 601 */ 602 public ReadOnlyCollection<V> values() 603 { 604 return InterfaceBuilder.toReadOnlyCollection( 605 fromBuilderOrHashMap 606 ? ((ROHashMapBuilder<K, V>) this.hashMap)._values(friendClassBadge) 607 : this.hashMap.values() 608 ); 609 } 610 611 /** 612 * Returns a {@link ReadOnlySet} view of the mappings contained in this map. 613 * @return a set view of the mappings contained in this map 614 */ 615 public ReadOnlySet<ReadOnlyMap.Entry<K,V>> entrySet() 616 { 617 return ROHelpers.toReadOnlyEntrySet( 618 fromBuilderOrHashMap 619 ? ((ROHashMapBuilder<K, V>) this.hashMap)._entrySet(friendClassBadge) 620 : this.hashMap.entrySet() 621 ); 622 } 623 624 625 // ******************************************************************************************** 626 // ******************************************************************************************** 627 // Overrides of JDK8 Map extension methods / ReadOnlyMap 628 // ******************************************************************************************** 629 // ******************************************************************************************** 630 631 632 @Override 633 public V getOrDefault(Object key, V defaultValue) 634 { return this.hashMap.getOrDefault(key, defaultValue); } 635 636 @Override 637 public void forEach(BiConsumer<? super K, ? super V> action) 638 { this.hashMap.forEach(action); } 639 640 641 // ******************************************************************************************** 642 // ******************************************************************************************** 643 // java.lang.Object 644 // ******************************************************************************************** 645 // ******************************************************************************************** 646 647 648 /** 649 * Returns a {@code String} representation of this map. The {@code String} representation 650 * consists of a list of key-value mappings in the order returned by the map's entrySet view's 651 * iterator, enclosed in braces ({@code "{}"}). Adjacent mappings are separated by the 652 * characters {@code ", "} (comma and space). Each key-value mapping is rendered as the key 653 * followed by an equals sign ({@code "="}) followed by the associated value. Keys and values 654 * are converted to {@code String's} as by {@code String.valueOf(Object)}. 655 * 656 * @return a {@code String} representation of this {@code HashMap} 657 */ 658 public String toString() 659 { 660 return ROHelpers.toString( 661 this.hashMap, // if the map contains itself, it is needed for printing purposes 662 fromBuilderOrHashMap 663 ? ((ROHashMapBuilder<K, V>) this.hashMap)._entrySet(friendClassBadge) 664 : this.hashMap.entrySet() 665 ); 666 } 667 668 /** 669 * Compares the specified Object with this Map for equality, as per the definition in the 670 * class {@code java.util.HashMap}. 671 * 672 * @param o object to be compared for equality with this {@code ReadOnlyHashMap}. 673 * @return TRUE if the specified Object is equal to this Map 674 */ 675 public boolean equals(Object o) 676 { return ROHelpers.roMapEq(this, o); } 677 678 /** 679 * Returns the hash code value for this Map as per the definition in the class 680 * {@code java.util.HashMap}. 681 */ 682 public int hashCode() 683 { return this.hashMap.hashCode(); } 684}