001/* 002 * Copyright (c) 1994, 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.util.*; 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 036/** 037 * Immutable Wrapper for <CODE>java.util.Hashtable</CODE>, found in the "Java Collections 038 * Framework". 039 * 040 * <EMBED CLASS=globalDefs DATA-JDK=Hashtable DATA-ENDING=table> 041 * <EMBED CLASS='external-html' DATA-FILE-ID=DATA_CLASS> 042 * <EMBED CLASS='external-html' DATA-FILE-ID=RO_SYNCHRONIZED> 043 * 044 * @param <K> the type of keys maintained by this map 045 * @param <V> the type of mapped values 046 */ 047@Torello.JavaDoc.JDHeaderBackgroundImg(EmbedTagFileID="JDHBI_MAIN") 048@SuppressWarnings("unchecked") 049public class ReadOnlyHashtable<K, V> 050 implements ReadOnlyDictionary<K, V>, ReadOnlyMap<K, V>, Cloneable, java.io.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 Hashtable'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 Hashtable EMPTY_HASH_TABLE = new Hashtable(0, 0.75f); 067 068 // Singleton & Empty ReadOnlyHashtable, Uses the "Supplier Constructor" 069 @SuppressWarnings("rawtypes") 070 private static final ReadOnlyHashtable EMPTY_READONLY_HASH_TABLE = 071 new ReadOnlyHashtable(EMPTY_HASH_TABLE); 072 073 // The actual Hashtable used by this instance. 074 private final Hashtable<K, V> hashTable; 075 076 // TRUE => This was built using the class ROHashtableBuilder 077 // FALSE => This was built using the clone() of a standard java.util.Hashtable constructor 078 079 private final boolean fromBuilderOrHashtable; 080 081 // Mimics the C++ Keyword/Concept of "Friend Class". Is "Friends With" ROHashtableBuilder 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 095 * {@code ReadOnlyHashtable}. 096 * 097 * @param <X> Returned {@link ReadOnlyMap}'s Key-Type. 098 * @param <Y> Returned {@link ReadOnlyMap}'s Value-Type. 099 * 100 * @return An empty map. Since this map is both empty & read-only, a raw-type singleton 101 * will suffice for all operations offered by this clas. 102 */ 103 public static <X, Y> ReadOnlyHashtable<X, Y> emptyROHT() 104 { return (ReadOnlyHashtable<X, Y>) EMPTY_READONLY_HASH_TABLE; } 105 106 // Used by the **Builder** 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 // ROHashtableBuilder 111 // 112 // This is the Constructor used by the Builder. It has a "Zero-Size" Optimization 113 114 ReadOnlyHashtable(ROHashtableBuilder<K, V> rohtb, ROHashtableBuilder.AccessBadge badge) 115 { 116 Objects.requireNonNull(badge, "Access Badge is null. Requires Friend-Class Badge"); 117 118 this.fromBuilderOrHashtable = true; 119 this.hashTable = rohtb; 120 } 121 122 // SPECIAL CASE: ReadOnlyProperties inherits ReadOnlyHashtable 123 // NOTE: ROPropertiesBuilder **DOES NOT** inherit ROHashtableBuilder 124 // 125 // The parent class of ReadOnlyProperties (a.k.a. this class) is completely unused, and the 126 // private fields are not important. For inheritance purposes, though, this is the parent 127 // class, and as such, it has to be initialized with something! 128 129 ReadOnlyHashtable(ReadOnlyProperties.AccessBadge badge) 130 { 131 this.fromBuilderOrHashtable = true; // This is unused 132 this.hashTable = null; // This is unused 133 } 134 135 136 // ******************************************************************************************** 137 // ******************************************************************************************** 138 // Modified-Original Constructors 139 // ******************************************************************************************** 140 // ******************************************************************************************** 141 142 143 /** 144 * Copies the contents of parameter {@code 'map'}, and saves saves it, thereby guaranteeing 145 * {@code 'this'} instance is Read-Only and fully-shielded from outside modification. 146 * 147 * @param map The {@code map} to be copied into {@code 'this'} instance internal and private 148 * {@code 'hashTable'} field. 149 */ 150 public ReadOnlyHashtable(Map<K, V> map) 151 { 152 this.fromBuilderOrHashtable = false; 153 154 this.hashTable = (map.size() == 0) 155 ? ((Hashtable<K, V>) EMPTY_HASH_TABLE) 156 : new Hashtable<>(map); 157 } 158 159 /** 160 * If only a small amount of processing needs to be done on the contents of some Java 161 * Map, and using an entire Builder-Class seems disproportionately complex - <I>this 162 * constructor can convert any Java {@code Map} into a {@code ReadOnlyHashtable}, using 163 * a simple {@code 'mapTranslator'}</I>. 164 * 165 * @param <X> The Key-Type of the User-Provided {@code Map}. 166 * @param <Y> The Value-Type of the User-Provided {@code Map}. 167 * 168 * @param refHolder This must a non-null instance of {@link Tuple2}. The provided 169 * {@code Consumer} is just that, a {@code 'Consumer'} rather than a {@code 'Function'}, since 170 * the results of each translation must be assigned to the values inside this tuple in order 171 * for them to be inserted into this {@code ReadOnlyHashtable}. 172 * 173 * @param map Any Java {@code Map}. 174 * 175 * @param mapTranslator A consumer for mapping the iterated elements of Map-Types {@code 'X'} 176 * and {@code 'Y'}, into the actual {@code Hashtable's} Key-Type {@code 'K'}, and Value-Type 177 * {@code 'V'}. The results of this translation must be placed into the fields inside 178 * {@code 'refHolder'}. 179 * 180 * <BR /><BR />If this parameter is passed null, this method will throw a 181 * {@code NullPointerException}. 182 * 183 * @param filter An optional filter that can be used to prevent & prohibit any chosen 184 * elements from input {@code 'map'} from being inserted into {@code 'this'} 185 * {@code ReadOnlyTreeMap}. 186 * 187 * <BR /><BR />This parameter may be passed null, and if it is, it will be silently ignored, 188 * and all entries present inside {@code 'map'} will be processed and inserted into 189 * {@code 'this'} 190 * 191 * @param loadFactor <EMBED CLASS='external-html' DATA-FILE-ID=HASH_LOAD_FACTOR> 192 * 193 * @throws NullPointerException if either parameter {@code 'i'} or parameter 194 * {@code 'mapTranslator'} is passed null. 195 */ 196 public <X, Y> ReadOnlyHashtable( 197 Tuple2<K, V> refHolder, 198 Map<X, Y> map, 199 Consumer<Map.Entry<X, Y>> mapTranslator, 200 Predicate<Map.Entry<X, Y>> filter, 201 Float loadFactor 202 ) 203 { 204 Objects.requireNonNull(refHolder, ROHelpers.NULL_MSG + "'refHolder'"); 205 Objects.requireNonNull(mapTranslator, ROHelpers.NULL_MSG + "'mapTranslator'"); 206 207 fromBuilderOrHashtable = false; 208 209 Hashtable<K, V> hashTable = (loadFactor != null) 210 ? new Hashtable<>(map.size(), loadFactor) 211 : new Hashtable<>(map.size()); 212 213 if (filter == null) 214 215 for (Map.Entry<X, Y> entry : map.entrySet()) 216 { 217 mapTranslator.accept(entry); 218 hashTable.put(refHolder.a, refHolder.b); 219 } 220 221 else for (Map.Entry<X, Y> entry : map.entrySet()) 222 { 223 if (! filter.test(entry)) continue; 224 mapTranslator.accept(entry); 225 hashTable.put(refHolder.a, refHolder.b); 226 } 227 228 // Empty Optimization (throw away, completely, the reference, use static-constant) 229 this.hashTable = (hashTable.size() == 0) 230 ? ((Hashtable<K, V>) EMPTY_HASH_TABLE) 231 : hashTable; 232 } 233 234 235 // ******************************************************************************************** 236 // ******************************************************************************************** 237 // New Stuff, January 2024 238 // ******************************************************************************************** 239 // ******************************************************************************************** 240 241 242 /** 243 * Constructs an instance of {@code ReadOnlyHashtable} that contains the keys present in 244 * parameter {@code 'keys'}, and values generated by {@code 'valueMapper'} - using each of the 245 * {@code 'keys'} as input. 246 * 247 * <EMBED CLASS='external-html' DATA-FILE-ID=LOOK_AT_IT> 248 * 249 * @param keys Any Java {@code Iterable} instance. 250 * @param valueMapper A user provided function to compute a map value, based on a map key. 251 * @param filter <EMBED CLASS='external-html' DATA-FILE-ID=MAP_ITERABLE_FILTER> 252 * @param loadFactor <EMBED CLASS='external-html' DATA-FILE-ID=HASH_LOAD_FACTOR> 253 * @param sizeIfKnown <EMBED CLASS='external-html' DATA-FILE-ID=MAP_TABLE_ICAPACITY> 254 * 255 * @throws NullPointerException if either {@code 'keys'} or {@code 'valueMapper'} are passed 256 * null. 257 */ 258 public ReadOnlyHashtable( 259 Iterable<? extends K> keys, 260 Function<? super K, ? extends V> valueMapper, 261 Predicate<? super K> filter, 262 Float loadFactor, 263 Integer sizeIfKnown 264 ) 265 { 266 Objects.requireNonNull(keys, ROHelpers.NULL_MSG + "'keys'"); 267 Objects.requireNonNull(valueMapper, ROHelpers.NULL_MSG + "'valueMapper'"); 268 269 fromBuilderOrHashtable = false; 270 271 Hashtable<K, V> hashTable = new Hashtable<>( 272 ((sizeIfKnown == null) ? 16 : sizeIfKnown), 273 ((loadFactor == null) ? 0.75f : loadFactor) 274 ); 275 276 if (filter == null) 277 for (K key : keys) 278 hashTable.put(key, valueMapper.apply(key)); 279 280 else 281 for (K key : keys) 282 if (filter.test(key)) 283 hashTable.put(key, valueMapper.apply(key)); 284 285 // Empty Optimization (throw away, completely, the reference, use static-constant) 286 this.hashTable = (hashTable.size() == 0) 287 ? ((Hashtable<K, V>) EMPTY_HASH_TABLE) 288 : hashTable; 289 } 290 291 /** 292 * Constructs an instance of {@code ReadOnlyHashtable} that has been populated by the Key-Value 293 * Pairs left in {@code 'refHolder'} by each invocation of the {@code Runnable} parameter 294 * {@code 'computeNextEntry'}. Key-Value Pairs are inserted until an invocation of the 295 * {@code Runnable} leaves null in {@code refHolder.a} and {@code refHolder.b} 296 * 297 * <EMBED CLASS='external-html' DATA-FILE-ID=LOOK_AT_IT> 298 * 299 * @param refHolder <EMBED CLASS='external-html' DATA-FILE-ID=REF_HOLDER> 300 * @param computeNextEntry <EMBED CLASS='external-html' DATA-FILE-ID=COMPUTE_NEXT_RUN> 301 * @param loadFactor <EMBED CLASS='external-html' DATA-FILE-ID=HASH_LOAD_FACTOR> 302 * @param sizeIfKnown <EMBED CLASS='external-html' DATA-FILE-ID=MAP_TABLE_ICAPACITY> 303 * 304 * @throws NullPointerException if either {@code 'refHolder'} or {@code 'computeNextEntry'} are 305 * passed null. 306 */ 307 public ReadOnlyHashtable( 308 Tuple2<K, V> refHolder, 309 Runnable computeNextEntry, 310 Float loadFactor, 311 Integer sizeIfKnown 312 ) 313 { 314 Objects.requireNonNull(refHolder, ROHelpers.NULL_MSG + "'refHolder'"); 315 Objects.requireNonNull(computeNextEntry, ROHelpers.NULL_MSG + "'computeNextEntry'"); 316 317 fromBuilderOrHashtable = false; 318 319 Hashtable<K, V> hashTable = new Hashtable<>( 320 ((sizeIfKnown == null) ? 16 : sizeIfKnown), 321 ((loadFactor == null) ? 0.75f : loadFactor) 322 ); 323 324 do 325 { 326 computeNextEntry.run(); 327 if ((refHolder.a == null) && (refHolder.b == null)) break; 328 hashTable.put(refHolder.a, refHolder.b); 329 } 330 while (true); 331 332 // Empty Optimization (throw away, completely, the reference, use static-constant) 333 this.hashTable = (hashTable.size() == 0) 334 ? ((Hashtable<K, V>) EMPTY_HASH_TABLE) 335 : hashTable; 336 } 337 338 /** 339 * Populates an instance of {@code ReadOnlyHashtable} by iterating the input {@code 'source'} 340 * iterable, and passing each value returned by that {@code Iterator} to the 341 * {@code 'computeNextEntry'} Java {@code Consumer}. 342 * 343 * <BR /><BR />It is the programmer's responsibility to properly place each Key-Value Pair 344 * that is intending to be inserted into the final {@code Map} instance into the 345 * {@code 'refHolder'} instance. After each invocation of {@code 'computeNextEntry'}, this 346 * constructor's logic will retrieve the values within {@code 'refHolder.a'} and 347 * {@code 'refHolder.b'} and insert them into this instance internal {@code Hashtable}. 348 * 349 * <EMBED CLASS='external-html' DATA-FILE-ID=LOOK_AT_IT> 350 * 351 * @param <X> The type of the elements inside {@code 'source'} 352 * @param source Any Java {@code Iterable} instance. 353 * @param refHolder <EMBED CLASS='external-html' DATA-FILE-ID=REF_HOLDER> 354 * @param computeNextEntry <EMBED CLASS='external-html' DATA-FILE-ID=COMPUTE_NEXT_CONS> 355 * @param filter May be used to filter out some of the elements of {@code 'source'} 356 * @param loadFactor <EMBED CLASS='external-html' DATA-FILE-ID=HASH_LOAD_FACTOR> 357 * @param sizeIfKnown <EMBED CLASS='external-html' DATA-FILE-ID=MAP_TABLE_ICAPACITY> 358 * 359 * @throws NullPointerException if either {@code 'refHolder'}, {@code 'computeNextEntry'} or 360 * {@code 'source'} are passed null. 361 */ 362 public <X> ReadOnlyHashtable( 363 Iterable<X> source, 364 Tuple2<K, V> refHolder, 365 Consumer<? super X> computeNextEntry, 366 Predicate<? super X> filter, 367 Float loadFactor, 368 Integer sizeIfKnown 369 ) 370 { 371 Objects.requireNonNull(refHolder, ROHelpers.NULL_MSG + "'refHolder'"); 372 Objects.requireNonNull(computeNextEntry, ROHelpers.NULL_MSG + "'computeNextEntry'"); 373 374 fromBuilderOrHashtable = false; 375 376 Hashtable<K, V> hashTable = new Hashtable<>( 377 ((sizeIfKnown == null) ? 16 : sizeIfKnown), 378 ((loadFactor == null) ? 0.75f : loadFactor) 379 ); 380 381 X x; // temp var 382 Iterator<X> iter = source.iterator(); 383 384 if (filter == null) 385 386 while (iter.hasNext()) 387 { 388 computeNextEntry.accept(iter.next()); 389 hashTable.put(refHolder.a, refHolder.b); 390 } 391 392 else 393 394 while (iter.hasNext()) 395 if (filter.test(x = iter.next())) 396 { 397 computeNextEntry.accept(x); 398 hashTable.put(refHolder.a, refHolder.b); 399 } 400 401 // Empty Optimization (throw away, completely, the reference, use static-constant) 402 this.hashTable = (hashTable.size() == 0) 403 ? ((Hashtable<K, V>) EMPTY_HASH_TABLE) 404 : hashTable; 405 } 406 407 408 // ******************************************************************************************** 409 // ******************************************************************************************** 410 // Array Constructors, March 2024 411 // ******************************************************************************************** 412 // ******************************************************************************************** 413 414 415 /** 416 * Retrieves elements from the VarArgs Generic-Array parameter {@code 'elements'}, and 417 * subsequently invokes the {@code 'computeNextEntry'} processor to populate this 418 * {@code ReadOnlyHashtable}. 419 * 420 * @param <X> The type of array parameter {@code 'elements'} 421 * @param refHolder <EMBED CLASS='external-html' DATA-FILE-ID=REF_HOLDER> 422 * @param computeNextEntry <EMBED CLASS='external-html' DATA-FILE-ID=ARR_COMPUTE_NEXT_CONS> 423 * @param filter <EMBED CLASS='external-html' DATA-FILE-ID=MAP_ARRAY_FILTER> 424 * @param loadFactor <EMBED CLASS='external-html' DATA-FILE-ID=HASH_LOAD_FACTOR> 425 * @param elements Any Generic VarArgs-Array 426 * @throws ClassCastException <EMBED CLASS='external-html' DATA-FILE-ID=PRIM_ARR_CCEX> 427 * 428 * @throws NullPointerException if either {@code 'refHolder'} or {@code 'computeNextEntry'} 429 * are passed null 430 */ 431 public <X> ReadOnlyHashtable( 432 Tuple2<K, V> refHolder, 433 Consumer<? super X> computeNextEntry, 434 Predicate<? super X> filter, 435 Float loadFactor, 436 X... elements 437 ) 438 { 439 Objects.requireNonNull(refHolder, ROHelpers.NULL_MSG + "'refHolder'"); 440 Objects.requireNonNull(computeNextEntry, ROHelpers.NULL_MSG + "'computeNextEntry'"); 441 442 this.fromBuilderOrHashtable = false; 443 444 Hashtable<K, V> hashTable = 445 new Hashtable<>(elements.length, ((loadFactor == null) ? 0.75f : loadFactor)); 446 447 if (filter == null) for (X e : elements) 448 { 449 computeNextEntry.accept(e); 450 hashTable.put(refHolder.a, refHolder.b); 451 } 452 453 else for (X x : elements) if (filter.test(x)) 454 { 455 computeNextEntry.accept(x); 456 hashTable.put(refHolder.a, refHolder.b); 457 } 458 459 // Empty Optimization (throw away, completely, the reference, use static-constant) 460 this.hashTable = (hashTable.size() == 0) 461 ? ((Hashtable<K, V>) EMPTY_HASH_TABLE) 462 : hashTable; 463 } 464 465 /** 466 * Retrieves elements from the Java Primitive-Array parameter {@code 'primitiveArray'}, and 467 * subsequently invokes the {@code 'computeNextEntry'} processor to populate this 468 * {@code ReadOnlyHashtable}. 469 * 470 * @param filter <EMBED CLASS='external-html' DATA-FILE-ID=PRED_FILT_PRIM> 471 * @param computeNextEntry <EMBED CLASS='external-html' DATA-FILE-ID=ARR_COMPUTE_NEXT_CONS> 472 * @param refHolder <EMBED CLASS='external-html' DATA-FILE-ID=REF_HOLDER> 473 * @param loadFactor <EMBED CLASS='external-html' DATA-FILE-ID=HASH_LOAD_FACTOR> 474 * @param primitiveArray Any Java Primitive-Array 475 * @throws ClassCastException <EMBED CLASS='external-html' DATA-FILE-ID=PRIM_ARR_CCEX> 476 * 477 * @throws NullPointerException if either {@code 'refHolder'} or {@code 'computeNextEntry'} 478 * are passed null 479 * 480 */ 481 public <X> ReadOnlyHashtable( 482 Predicate<?> filter, 483 Consumer<?> computeNextEntry, 484 Tuple2<K, V> refHolder, 485 Float loadFactor, 486 Object primitiveArray 487 ) 488 { 489 Objects.requireNonNull(refHolder, ROHelpers.NULL_MSG + "'refHolder'"); 490 Objects.requireNonNull(computeNextEntry, ROHelpers.NULL_MSG + "'computeNextEntry'"); 491 492 this.fromBuilderOrHashtable = false; 493 494 Hashtable<K, V> hashTable = ROHelpers.buildROMap( 495 primitiveArray, 496 (int arrayLen) -> new Hashtable<>(arrayLen, ((loadFactor == null) ? 0.75f : loadFactor)), 497 filter, 498 refHolder, 499 computeNextEntry 500 ); 501 502 // Empty Optimization (throw away, completely, the reference, use static-constant) 503 this.hashTable = (hashTable.size() == 0) 504 ? ((Hashtable<K, V>) EMPTY_HASH_TABLE) 505 : hashTable; 506 } 507 508 509 // ******************************************************************************************** 510 // ******************************************************************************************** 511 // Convert to java.util Types 512 // ******************************************************************************************** 513 // ******************************************************************************************** 514 515 516 /** 517 * Clone's {@code 'this'} instance internal {@code Hashtable<K, V>} field, and returns it. 518 * <EMBED CLASS='external-html' DATA-TYPE=Hashtable DATA-FILE-ID=CLONE_TO> 519 * 520 * @return An independent, mutable copy of {@code 'this'} instance internal 521 * {@code Hashtable<K, V>} data-structure. 522 */ 523 public Hashtable<K, V> cloneToHashtable() 524 { 525 if (! fromBuilderOrHashtable) return (Hashtable<K, V>) this.hashTable.clone(); 526 527 Hashtable<K, V> ret = new Hashtable<K, V>(); 528 529 for (Map.Entry<K, V> e : 530 ((ROHashtableBuilder<K, V>) this.hashTable)._entrySet(friendClassBadge)) 531 532 ret.put(e.getKey(), e.getValue()); 533 534 return ret; 535 } 536 537 /** 538 * Invokes {@code java.util.Collections.unmodifiableMap} on the internal {@code Hashtable}. 539 * <EMBED CLASS='external-html' DATA-RET_TYPE=Map DATA-FILE-ID=WRAP_TO_IMMUTABLE> 540 * 541 * @return A {@code Map} which adheres to the JDK interface {@code java.util.Map}, but throws 542 * an {@code UnsupportedOperationException} if a user attempts to invoke a Mutator-Method on 543 * the returned instance. 544 */ 545 public Map<K, V> wrapToImmutableMap() 546 { return Collections.unmodifiableMap(this.hashTable); } 547 548 549 // ******************************************************************************************** 550 // ******************************************************************************************** 551 // Original JDK Methods, java.util.Hashtable 552 // ******************************************************************************************** 553 // ******************************************************************************************** 554 555 556 /** 557 * Returns the number of keys in this {@code Hashtable}. 558 * @return the number of keys in this {@code Hashtable}. 559 */ 560 public int size() 561 { return this.hashTable.size(); } 562 563 /** 564 * Tests if this {@code Hashtable} maps no keys to values. 565 * 566 * @return {@code TRUE} if this {@code Hashtable} maps no keys to values; {@code FALSE} 567 * otherwise. 568 */ 569 public boolean isEmpty() 570 { return this.hashTable.isEmpty(); } 571 572 /** 573 * Returns an enumeration of the keys in this {@code Hashtable}. Use the Enumeration methods 574 * on the returned object to fetch the keys sequentially. 575 * 576 * @return an enumeration of the keys in this {@code Hashtable}. 577 * 578 * @see #elements() 579 * @see #keySet() 580 * @see ReadOnlyMap 581 */ 582 public Enumeration<K> keys() 583 { return this.hashTable.keys(); } 584 585 /** 586 * Returns an enumeration of the values in this {@code Hashtable}. Use the Enumeration methods 587 * on the returned object to fetch the elements sequentially. 588 * 589 * @return an enumeration of the values in this {@code Hashtable}. 590 * 591 * @see #keys() 592 * @see #values() 593 * @see ReadOnlyMap 594 */ 595 public Enumeration<V> elements() 596 { return this.hashTable.elements(); } 597 598 /** 599 * Tests if some key maps into the specified value in this {@code Hashtable}. This operation 600 * is more expensive than the {@link #containsKey containsKey} method. 601 * 602 * <BR /><BR />Note that this method is identical in functionality to 603 * {@link #containsValue containsValue}, (which is part of the {@link ReadOnlyMap} interface. 604 * 605 * @param value a value to search for 606 * 607 * @return {@code TRUE} if and only if some key maps to the {@code value} argument in this 608 * {@code Hashtable} as determined by the {@code equals} method; {@code false} otherwise. 609 * 610 * @throws NullPointerException if the value is {@code null} 611 */ 612 public boolean contains(Object value) 613 { return this.hashTable.contains(value); } 614 615 /** 616 * Returns {@code TRUE} if this {@code Hashtable} maps one or more keys to this value. 617 * 618 * <BR /><BR />Note that this method is identical in functionality to {@link #contains}. 619 * 620 * @param value value whose presence in this {@code Hashtable} is to be tested 621 * @return {@code TRUE} if this map maps one or more keys to the specified value 622 * @throws NullPointerException if the value is {@code null} 623 */ 624 public boolean containsValue(Object value) 625 { return this.hashTable.contains(value); } 626 627 /** 628 * Tests if the specified object is a key in this {@code Hashtable}. 629 * 630 * @param key possible key 631 * 632 * @return {@code TRUE} if and only if the specified object is a key in this {@code Hashtable}, 633 * as determined by the {@code equals} method; {@code false} otherwise. 634 * 635 * @throws NullPointerException if the key is {@code null} 636 * 637 * @see #contains(Object) 638 */ 639 public boolean containsKey(Object key) 640 { return this.hashTable.containsKey(key); } 641 642 /** 643 * Returns the value to which the specified key is mapped, or {@code null} if this map contains 644 * no mapping for the key. 645 * 646 * <BR /><BR />More formally, if this map contains a mapping from a key {@code k} to a value 647 * {@code v} such that {@code (key.equals(k))}, then this method returns {@code v}; otherwise 648 * it returns {@code null}. (There can be at most one such mapping.) 649 * 650 * @param key the key whose associated value is to be returned 651 * 652 * @return the value to which the specified key is mapped, or {@code null} if this map contains 653 * no mapping for the key 654 * 655 * @throws NullPointerException if the specified key is null 656 */ 657 public V get(Object key) 658 { return this.hashTable.get(key); } 659 660 661 // ******************************************************************************************** 662 // ******************************************************************************************** 663 // Views 664 // ******************************************************************************************** 665 // ******************************************************************************************** 666 667 668 /** 669 * Returns a {@link ReadOnlySet} view of the keys contained in this map. The set is backed by 670 * the map, so changes to the map are reflected in the set, and vice-versa. 671 */ 672 public ReadOnlySet<K> keySet() 673 { 674 return InterfaceBuilder.toReadOnlySet( 675 fromBuilderOrHashtable 676 ? ((ROHashtableBuilder<K, V>) this.hashTable)._keySet(friendClassBadge) 677 : this.hashTable.keySet() 678 ); 679 } 680 681 /** Returns a {@link ReadOnlySet} view of the mappings contained in this map. */ 682 public ReadOnlySet<ReadOnlyMap.Entry<K, V>> entrySet() 683 { 684 return ROHelpers.toReadOnlyEntrySet( 685 fromBuilderOrHashtable 686 ? ((ROHashtableBuilder<K, V>) this.hashTable)._entrySet(friendClassBadge) 687 : this.hashTable.entrySet() 688 ); 689 } 690 691 /** Returns a {@link ReadOnlyCollection} view of the values contained in this map. */ 692 public ReadOnlyCollection<V> values() 693 { 694 return InterfaceBuilder.toReadOnlyCollection( 695 fromBuilderOrHashtable 696 ? ((ROHashtableBuilder<K, V>) this.hashTable)._values(friendClassBadge) 697 : this.hashTable.values() 698 ); 699 } 700 701 702 // ******************************************************************************************** 703 // ******************************************************************************************** 704 // Comparison and hashing 705 // ******************************************************************************************** 706 // ******************************************************************************************** 707 708 709 public V getOrDefault(Object key, V defaultValue) 710 { return this.hashTable.getOrDefault(key, defaultValue); } 711 712 public void forEach(BiConsumer<? super K, ? super V> action) 713 { this.hashTable.forEach(action); } 714 715 716 // ******************************************************************************************** 717 // ******************************************************************************************** 718 // java.lang.Object 719 // ******************************************************************************************** 720 // ******************************************************************************************** 721 722 723 /** 724 * Returns a string representation of this {@code Hashtable} object in the form of a set of 725 * entries, enclosed in braces and separated by the ASCII characters {@code " , "} (comma and 726 * space). Each entry is rendered as the key, an equals sign {@code '='}, and the associated 727 * element, where the {@code toString} method is used to convert the key and element to 728 * {@code String's}. 729 * 730 * @return a {@code String} representation of this {@code Hashtable} 731 */ 732 public String toString() 733 { 734 return ROHelpers.toString( 735 this.hashTable, // if the map contains itself, it is needed for printing purposes 736 fromBuilderOrHashtable 737 ? ((ROHashtableBuilder<K, V>) this.hashTable)._entrySet(friendClassBadge) 738 : this.hashTable.entrySet() 739 ); 740 } 741 742 /** 743 * Compares the specified Object with this Map for equality, as per the definition in the 744 * class {@code java.util.Hashtable}. 745 * 746 * @param o object to be compared for equality with this {@code ReadOnlyHashtable}. 747 * @return {@code TRUE} if the specified Object is equal to this Map 748 */ 749 public boolean equals(Object o) 750 { return ROHelpers.roMapEq(this, o); } 751 752 /** 753 * Returns the hash code value for this Map as per the definition in the class 754 * {@code java.util.Hashtable}. 755 */ 756 public int hashCode() 757 { return this.hashTable.hashCode(); } 758}