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