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