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 Torello.Java.Additional.EffectivelyFinal; 028import Torello.Java.Additional.RemoveUnsupportedIterator; 029 030import Torello.JavaDoc.LinkJavaSource; 031import Torello.JavaDoc.IntoHTMLTable; 032 033import java.util.*; 034 035import java.util.stream.Collector; 036 037import java.util.function.Function; 038import java.util.function.Supplier; 039import java.util.function.Predicate; 040import java.util.function.Consumer; 041import java.util.function.IntFunction; 042 043/** 044 * Immutable Wrapper for <CODE>java.util.HashSet</CODE>, found in the "Java Collections 045 * Framework". 046 * 047 * <EMBED CLASS=globalDefs DATA-JDK=HashSet DATA-ENDING=Set> 048 * <EMBED CLASS='external-html' DATA-FILE-ID=MUCHOS_CONSTRUCTORS> 049 * <EMBED CLASS='external-html' DATA-FILE-ID=DATA_CLASS> 050 * 051 * @param <E> the type of elements maintained by this set 052 */ 053@Torello.JavaDoc.JDHeaderBackgroundImg(EmbedTagFileID="JDHBI_MAIN") 054@SuppressWarnings("unchecked") 055public class ReadOnlyHashSet<E> 056 // extends AbstractSet<E> 057 implements ReadOnlySet<E>, Cloneable, java.io.Serializable 058{ 059 // ******************************************************************************************** 060 // ******************************************************************************************** 061 // Protected & Private Fields, Methods, Statics 062 // ******************************************************************************************** 063 // ******************************************************************************************** 064 065 066 /** <EMBED CLASS='external-html' DATA-FILE-ID=SVUID> */ 067 protected static final long serialVersionUID = 1; 068 069 // Minor Optimization where new HashSet's that have no contents always re-use this static 070 // instance. Since this instance is completely empty, the Raw-Types things is irrelevant. 071 072 @SuppressWarnings("rawtypes") 073 private static final HashSet EMPTY_HASH_SET = new HashSet(0, 0.75f); 074 075 // Singleton & Empty ReadOnlyHashSet, Uses the "Supplier Constructor" 076 @SuppressWarnings("rawtypes") 077 private static final ReadOnlyHashSet EMPTY_READONLY_HASH_SET = 078 new ReadOnlyHashSet(0, 0.75f, () -> null); 079 080 // The actual HashSet used by this instance. 081 private final HashSet<E> hashSet; 082 083 // TRUE => This was built using the class ROHashSetBuilder 084 // FALSE => This was built using the clone() of a standard java.util.HashSet constructor 085 086 private final boolean fromBuilderOrHashSet; 087 088 // Mimics the C++ Keyword/Concept of "Friend Class". Is "Friends With" ROHashSetBuilder 089 static class AccessBadge { private AccessBadge() { } } 090 private static final AccessBadge friendClassBadge = new AccessBadge(); 091 092 /** 093 * Returns an empty, <B STYLE='color: red;'>singleton</B>, instance of {@code ReadOnlHashSet}. 094 * @param <T> Returned {@link ReadOnlySet}'s Data-Type. 095 * 096 * @return An empty set. Since this set is both empty & read-only, a raw-type singleton 097 * will suffice for all operations offered by this clas. 098 * 099 * <EMBED CLASS='external-html' DATA-FILE-ID=EMPTY_SYNCHRONIZED> 100 */ 101 public static <T> ReadOnlyHashSet<T> emptyROHS() 102 { return (ReadOnlyHashSet<T>) EMPTY_READONLY_HASH_SET; } 103 104 105 // ******************************************************************************************** 106 // ******************************************************************************************** 107 // Basic Constructors 108 // ******************************************************************************************** 109 // ******************************************************************************************** 110 111 112 // To all the readers out there following along: The "AccessBadge" thing is just a slightly 113 // wimpier substitute for the C++ keyword / concept 'friend' or "Friend Class". It means this 114 // constructor is (for all intents and purposes) a private-constructor, except for the class 115 // ROHashSetBuilder 116 // 117 // This is the Constructor used by the Builder. It has a "Zero-Size" Optimization 118 119 ReadOnlyHashSet(ROHashSetBuilder<E> rohsb, ROHashSetBuilder.AccessBadge badge) 120 { 121 Objects.requireNonNull(badge, "Access Badge is null. Requires Friend-Class Badge"); 122 123 this.fromBuilderOrHashSet = true; 124 this.hashSet = rohsb; 125 } 126 127 /** 128 * Copies parameter {@code 'c'} (and saves it) in order to guarantee that {@code 'this'} 129 * instance is Read-Only, and shielded from outside modification. 130 * 131 * @param c The {@code Collection} to be copied and saved into this instance internal 132 * and private {@code 'hashSet'} field. 133 */ 134 public ReadOnlyHashSet(Collection<E> c) 135 { 136 this.fromBuilderOrHashSet = false; 137 this.hashSet = (c.size() == 0) ? ((HashSet<E>) EMPTY_HASH_SET) : new HashSet<>(c); 138 } 139 140 /** 141 * Use a {@code Supplier<E>} to provide an arbitrary number of elements of type {@code 'E'} 142 * directly to this constructor. This constructor will request elements from the 143 * {@code Supplier} provided to parameter {@code 's'} until {@code 's'} returns null. 144 * 145 * <EMBED CLASS=defs DATA-SOURCE=Supplier> 146 * 147 * @param quantityIfKnown <EMBED CLASS='external-html' DATA-FILE-ID=HASH_INIT_CAPACITY> 148 * @param loadFactor <EMBED CLASS='external-html' DATA-FILE-ID=HASH_LOAD_FACTOR> 149 * @param s Any Java {@code Supplier<E>} 150 * 151 * @throws IllegalArgumentException if the specified quantity / capacity is negative 152 */ 153 public ReadOnlyHashSet(Integer quantityIfKnown, Float loadFactor, Supplier<E> s) 154 { 155 fromBuilderOrHashSet = false; 156 157 final HashSet<E> hashSet = new HashSet<E>( 158 (quantityIfKnown != null) ? quantityIfKnown : 16, 159 (loadFactor != null) ? loadFactor : 0.75f 160 ); 161 162 E e; 163 while ((e = s.get()) != null) hashSet.add(e); 164 165 // Empty Optimization (throw away, completely, the reference, use static-constant) 166 this.hashSet = (hashSet.size() == 0) ? ((HashSet<E>) EMPTY_HASH_SET) : hashSet; 167 } 168 169 170 // ******************************************************************************************** 171 // ******************************************************************************************** 172 // Iterable & Array Based Constructors - **NO FILTER** 173 // ******************************************************************************************** 174 // ******************************************************************************************** 175 176 177 /** 178 * If only a small amount of processing needs to be done on the contents of some Java 179 * Data-Type, and using an entire Builder-Class seems disproportionately complex - <I>this 180 * constructor can convert any Java {@code Iterable} into a {@code ReadOnlyHashSet}, using 181 * a simple {@code 'mapper'}</I>. 182 * 183 * <EMBED CLASS=defs DATA-SOURCE=Iterable> 184 * 185 * @param <T> <EMBED CLASS='external-html' DATA-FILE-ID=ITERABLE_TYPE_PARAM> 186 * @param i Any Java {@code Iterable<T>} 187 * @param mapper <EMBED CLASS='external-html' DATA-FILE-ID=ITERABLE_MAPPER> 188 * @param sizeIfKnown <EMBED CLASS='external-html' DATA-FILE-ID=HASH_INIT_CAPACITY> 189 * @param loadFactor <EMBED CLASS='external-html' DATA-FILE-ID=HASH_LOAD_FACTOR> 190 * 191 * @throws NullPointerException if either {@code 'i'} or {@code 'mapper'} are passed null. 192 * 193 * @throws IllegalArgumentException if the initial capacity ({@code 'sizeIfKnown'}) is less 194 * than zero, or if the load factor is nonpositive 195 */ 196 public <T> ReadOnlyHashSet( 197 Iterable<T> i, 198 Function<? super T, ? extends E> mapper, 199 Integer sizeIfKnown, 200 Float loadFactor 201 ) 202 { 203 Objects.requireNonNull(mapper, ROHelpers.NULL_MSG + "'mapper'"); 204 205 fromBuilderOrHashSet = false; 206 207 final HashSet<E> hashSet = new HashSet<E>( 208 (sizeIfKnown != null) ? sizeIfKnown : 16, 209 (loadFactor != null) ? loadFactor : 0.75f 210 ); 211 212 for (T t : i) hashSet.add(mapper.apply(t)); 213 214 // Empty Optimization (throw away, completely, the reference, use static-constant) 215 this.hashSet = (hashSet.size() == 0) ? ((HashSet<E>) EMPTY_HASH_SET) : hashSet; 216 } 217 218 /** 219 * If a Standard Java {@code Iterable} can be directly mapped into a {@code HashSet} 220 * (and, ergo, an entire Builder-Instance would be a bit excessive) - <I>this constructor will 221 * seamlessly convert any Java {@code Iterable<E>} directly into a {@code ReadOnlyHashSet<E>} 222 * with just this single invocation</I>. 223 * 224 * <EMBED CLASS=defs DATA-SOURCE=Iterable> 225 * 226 * @param i Any Java {@code Iterator<E>} 227 * @param sizeIfKnown <EMBED CLASS='external-html' DATA-FILE-ID=HASH_INIT_CAPACITY> 228 * @param loadFactor <EMBED CLASS='external-html' DATA-FILE-ID=HASH_LOAD_FACTOR> 229 * 230 * @throws NullPointerException if {@code 'i'} is passed null. 231 * 232 * @throws IllegalArgumentException if the initial capacity ({@code 'sizeIfKnown'}) is less 233 * than zero, or if the load factor is nonpositive 234 */ 235 public ReadOnlyHashSet(Iterable<E> i, Integer sizeIfKnown, Float loadFactor) 236 { 237 fromBuilderOrHashSet = false; 238 239 final HashSet<E> hashSet = new HashSet<E>( 240 (sizeIfKnown != null) ? sizeIfKnown : 16, 241 (loadFactor != null) ? loadFactor : 0.75f 242 ); 243 244 for (E element : i) hashSet.add(element); 245 246 // Empty Optimization (throw away, completely, the reference, use static-constant) 247 this.hashSet = (hashSet.size() == 0) ? ((HashSet<E>) EMPTY_HASH_SET) : hashSet; 248 } 249 250 /** 251 * Builds {@code ReadOnlyHashSet<E>} instance having Generic-Type {@code 'E'}, and contents 252 * {@code 'elements'}. 253 * 254 * @param elementsType <EMBED CLASS='external-html' DATA-FILE-ID=ELEMENTS_TYPE> 255 * @param loadFactor <EMBED CLASS='external-html' DATA-FILE-ID=HASH_LOAD_FACTOR> 256 * @param elements <EMBED CLASS='external-html' DATA-FILE-ID=ELEMENTS_VAR_ARGS> 257 * 258 * @throws ClassCastException <EMBED CLASS='external-html' DATA-FILE-ID=CCEX> 259 * @throws NullPointerException If {@code 'elementsType'} is passed null. 260 */ 261 public ReadOnlyHashSet(Class<E> elementsType, Float loadFactor, Object... elements) 262 { 263 Objects.requireNonNull(elementsType, ROHelpers.NULL_MSG + "'elementsType'"); 264 265 fromBuilderOrHashSet = false; 266 267 if (elements.length == 0) 268 this.hashSet = (HashSet<E>) EMPTY_HASH_SET; 269 270 else 271 { 272 this.hashSet = (loadFactor != null) 273 ? new HashSet<>(elements.length, loadFactor) 274 : new HashSet<>(elements.length, 0.75f); 275 276 for (Object element : elements) this.hashSet.add(elementsType.cast(element)); 277 } 278 } 279 280 /** 281 * Builds {@code ReadOnlyHashSet<E>} instance having Generic-Type {@code 'E'}, and contents 282 * {@code 'elements'}. 283 * 284 * @param mapper <EMBED CLASS='external-html' DATA-FILE-ID=OBJ_E_MAPPER> 285 * @param loadFactor <EMBED CLASS='external-html' DATA-FILE-ID=HASH_LOAD_FACTOR> 286 * @param elements <EMBED CLASS='external-html' DATA-FILE-ID=ELEMENTS_VAR_ARGS> 287 * 288 * @throws ClassCastException <EMBED CLASS='external-html' DATA-FILE-ID=CCEX> 289 * @throws NullPointerException If {@code 'mapper'} is passed null. 290 */ 291 public ReadOnlyHashSet 292 (Function<Object, ? extends E> mapper, Float loadFactor, Object... elements) 293 { 294 Objects.requireNonNull(mapper, ROHelpers.NULL_MSG + "'mapper'"); 295 296 fromBuilderOrHashSet = false; 297 298 if (elements.length == 0) 299 this.hashSet = (HashSet<E>) EMPTY_HASH_SET; 300 301 else 302 { 303 this.hashSet = (loadFactor != null) 304 ? new HashSet<>(elements.length, loadFactor) 305 : new HashSet<>(elements.length, 0.75f); 306 307 for (Object element : elements) this.hashSet.add(mapper.apply(element)); 308 } 309 } 310 311 312 // ******************************************************************************************** 313 // ******************************************************************************************** 314 // Iterable & Array Based Constructors - **INCLUDES ELEMENT FILTER** 315 // ******************************************************************************************** 316 // ******************************************************************************************** 317 318 319 /** 320 * If only a small amount of processing needs to be done on the contents of some Java 321 * Data-Type, and using an entire Builder-Class seems disproportionately complex - <I>this 322 * constructor can convert any Java {@code Iterable} into a {@code ReadOnlyHashSet}, using 323 * a simple {@code 'mapper'}</I>. 324 * 325 * <EMBED CLASS=defs DATA-SOURCE=Iterable> 326 * 327 * @param <T> <EMBED CLASS='external-html' DATA-FILE-ID=ITERABLE_TYPE_PARAM> 328 * @param i Any Java {@code Iterable<T>} 329 * @param mapper <EMBED CLASS='external-html' DATA-FILE-ID=ITERABLE_MAPPER> 330 * @param filter <EMBED CLASS='external-html' DATA-FTP=T DATA-FILE-ID=PREDICATE_FILTER> 331 * @param sizeIfKnown <EMBED CLASS='external-html' DATA-FILE-ID=HASH_INIT_CAPACITY> 332 * @param loadFactor <EMBED CLASS='external-html' DATA-FILE-ID=HASH_LOAD_FACTOR> 333 * 334 * @throws NullPointerException if either {@code 'mapper'} or {@code 'filter'} are passed null 335 * 336 * @throws IllegalArgumentException if the initial capacity ({@code 'sizeIfKnown'}) is less 337 * than zero, or if the load factor is nonpositive 338 */ 339 public <T> ReadOnlyHashSet( 340 Iterable<T> i, Function<T, E> mapper, 341 Predicate<? super T> filter, 342 Integer sizeIfKnown, 343 Float loadFactor 344 ) 345 { 346 Objects.requireNonNull(mapper, ROHelpers.NULL_MSG + "'mapper'"); 347 Objects.requireNonNull(filter, ROHelpers.NULL_MSG + "'filter'"); 348 349 fromBuilderOrHashSet = false; 350 351 final HashSet<E> hashSet = new HashSet<E>( 352 (sizeIfKnown != null) ? sizeIfKnown : 16, 353 (loadFactor != null) ? loadFactor : 0.75f 354 ); 355 356 for (T t : i) if (filter.test(t)) hashSet.add(mapper.apply(t)); 357 358 // Empty Optimization (throw away, completely, the reference, use static-constant) 359 this.hashSet = (hashSet.size() == 0) ? ((HashSet<E>) EMPTY_HASH_SET) : hashSet; 360 } 361 362 /** 363 * If a Standard Java {@code Iterable} can be directly mapped into a {@code HashSet} 364 * (and, ergo, an entire Builder-Instance would be a bit excessive) - <I>this constructor will 365 * seamlessly convert any Java {@code Iterable<E>} directly into a {@code ReadOnlyHashSet<E>} 366 * with just this single invocation</I>. 367 * 368 * <EMBED CLASS=defs DATA-SOURCE=Iterable> 369 * 370 * @param i Any Java {@code Iterator<E>} 371 * @param filter <EMBED CLASS='external-html' DATA-FTP=E DATA-FILE-ID=PREDICATE_FILTER> 372 * @param sizeIfKnown <EMBED CLASS='external-html' DATA-FILE-ID=HASH_INIT_CAPACITY> 373 * @param loadFactor <EMBED CLASS='external-html' DATA-FILE-ID=HASH_LOAD_FACTOR> 374 * 375 * @throws NullPointerException if either {@code 'i'} or {@code 'filter'} are passed null 376 * 377 * @throws IllegalArgumentException if the initial capacity ({@code 'sizeIfKnown'}) is less 378 * than zero, or if the load factor is nonpositive 379 */ 380 public ReadOnlyHashSet 381 (Iterable<E> i, Predicate<? super E> filter, Integer sizeIfKnown, Float loadFactor) 382 { 383 Objects.requireNonNull(filter, ROHelpers.NULL_MSG + "'filter'"); 384 385 fromBuilderOrHashSet = false; 386 387 final HashSet<E> hashSet = new HashSet<E>( 388 (sizeIfKnown != null) ? sizeIfKnown : 16, 389 (loadFactor != null) ? loadFactor : 0.75f 390 ); 391 392 for (E element : i) if (filter.test(element)) hashSet.add(element); 393 394 // Empty Optimization (throw away, completely, the reference, use static-constant) 395 this.hashSet = (hashSet.size() == 0) ? ((HashSet<E>) EMPTY_HASH_SET) : hashSet; 396 } 397 398 /** 399 * Builds {@code ReadOnlyHashSet<E>} instance having Generic-Type {@code 'E'}, and contents 400 * {@code 'elements'}. 401 * 402 * @param elementsType <EMBED CLASS='external-html' DATA-FILE-ID=ELEMENTS_TYPE> 403 * @param filter <EMBED CLASS='external-html' DATA-FTP=E DATA-FILE-ID=PREDICATE_FILTER> 404 * @param loadFactor <EMBED CLASS='external-html' DATA-FILE-ID=HASH_LOAD_FACTOR> 405 * @param elements <EMBED CLASS='external-html' DATA-FILE-ID=ELEMENTS_VAR_ARGS> 406 * 407 * @throws ClassCastException <EMBED CLASS='external-html' DATA-FILE-ID=CCEX> 408 * 409 * @throws NullPointerException If either {@code 'elementsType'} or {@code 'filter'} is passed 410 * null 411 */ 412 public ReadOnlyHashSet( 413 Class<E> elementsType, 414 Predicate<? super E> filter, 415 Float loadFactor, 416 Object... elements 417 ) 418 { 419 Objects.requireNonNull(filter, ROHelpers.NULL_MSG + "'filter'"); 420 Objects.requireNonNull(elementsType, ROHelpers.NULL_MSG + "'elementsType'"); 421 422 fromBuilderOrHashSet = false; 423 424 final HashSet<E> hashSet = (loadFactor != null) 425 ? new HashSet<>(elements.length, loadFactor) 426 : new HashSet<>(elements.length, 0.75f); 427 428 E e; 429 for (Object element : elements) 430 if (filter.test(e = elementsType.cast(element))) 431 hashSet.add(e); 432 433 // Empty Optimization (throw away, completely, the reference, use static-constant) 434 this.hashSet = (hashSet.size() == 0) ? ((HashSet<E>) EMPTY_HASH_SET) : hashSet; 435 } 436 437 /** 438 * Builds {@code ReadOnlyHashSet<E>} instance having Generic-Type {@code 'E'}, and contents 439 * {@code 'elements'}. 440 * 441 * @param mapper <EMBED CLASS='external-html' DATA-FILE-ID=OBJ_E_MAPPER> 442 * @param filter <EMBED CLASS='external-html' DATA-FTP=Object DATA-FILE-ID=PREDICATE_FILTER> 443 * 444 * @param loadFactor <EMBED CLASS='external-html' DATA-FILE-ID=HASH_LOAD_FACTOR> 445 * @param elements <EMBED CLASS='external-html' DATA-FILE-ID=ELEMENTS_VAR_ARGS> 446 * 447 * @throws ClassCastException <EMBED CLASS='external-html' DATA-FILE-ID=CCEX> 448 * @throws NullPointerException If either {@code 'mapper'} or {@code 'filter'} is passed null 449 */ 450 public ReadOnlyHashSet( 451 Function<Object, ? extends E> mapper, 452 Predicate<Object> filter, 453 Float loadFactor, 454 Object... elements 455 ) 456 { 457 Objects.requireNonNull(filter, ROHelpers.NULL_MSG + "'filter'"); 458 Objects.requireNonNull(mapper, ROHelpers.NULL_MSG + "'mapper'"); 459 460 fromBuilderOrHashSet = false; 461 462 final HashSet<E> hashSet = (loadFactor != null) 463 ? new HashSet<>(elements.length, loadFactor) 464 : new HashSet<>(elements.length, 0.75f); 465 466 for (Object element : elements) 467 if (filter.test(element)) 468 hashSet.add(mapper.apply(element)); 469 470 // Empty Optimization (throw away, completely, the reference, use static-constant) 471 this.hashSet = (hashSet.size() == 0) ? ((HashSet<E>) EMPTY_HASH_SET) : hashSet; 472 } 473 474 475 // ******************************************************************************************** 476 // ******************************************************************************************** 477 // @SafeVarargs / Variable-Arity / VarArgs: with Parameterized / Generic Type's 478 // ******************************************************************************************** 479 // ******************************************************************************************** 480 481 482 /** 483 * Builds {@code ReadOnlyHashSet<E>} instance having Generic-Type {@code 'E'}, and contents 484 * {@code 'elements'}. 485 * 486 * @param loadFactor <EMBED CLASS='external-html' DATA-FILE-ID=HASH_LOAD_FACTOR> 487 * @param elements <EMBED CLASS='external-html' DATA-FILE-ID=ELEMENTS_VAR_ARGS> 488 */ 489 @SafeVarargs 490 public ReadOnlyHashSet(Float loadFactor, E... elements) 491 { 492 fromBuilderOrHashSet = false; 493 494 if (elements.length == 0) 495 this.hashSet = (HashSet<E>) EMPTY_HASH_SET; 496 497 else 498 { 499 this.hashSet = (loadFactor != null) 500 ? new HashSet<>(elements.length, loadFactor) 501 : new HashSet<>(elements.length, 0.75f); 502 503 for (E e : elements) this.hashSet.add(e); 504 } 505 } 506 507 /** 508 * Builds {@code ReadOnlyHashSet<E>} instance having Generic-Type {@code 'E'}, and contents 509 * {@code 'elements'}. 510 * 511 * @param <T> <EMBED CLASS='external-html' DATA-FILE-ID=VARARGS_TYPE_PARAM> 512 * @param loadFactor <EMBED CLASS='external-html' DATA-FILE-ID=HASH_LOAD_FACTOR> 513 * @param mapper <EMBED CLASS='external-html' DATA-FILE-ID=T_ARR_E_MAPPER> 514 * @param elements <EMBED CLASS='external-html' DATA-FILE-ID=ELEMENTS_VAR_ARGS> 515 * 516 * @throws NullPointerException If {@code 'mapper'} is passed null. 517 */ 518 @SafeVarargs 519 public <T> ReadOnlyHashSet( 520 Float loadFactor, 521 Function<? super T, ? extends E> mapper, 522 T... elements 523 ) 524 { 525 Objects.requireNonNull(mapper, ROHelpers.NULL_MSG + "'mapper'"); 526 527 fromBuilderOrHashSet = false; 528 529 if (elements.length == 0) 530 this.hashSet = (HashSet<E>) EMPTY_HASH_SET; 531 532 else 533 { 534 this.hashSet = (loadFactor != null) 535 ? new HashSet<>(elements.length, loadFactor) 536 : new HashSet<>(elements.length, 0.75f); 537 538 for (T t : elements) this.hashSet.add(mapper.apply(t)); 539 } 540 } 541 542 /** 543 * Builds {@code ReadOnlyHashSet<E>} instance having Generic-Type {@code 'E'}, and contents 544 * {@code 'elements'}. 545 * 546 * @param filter <EMBED CLASS='external-html' DATA-FTP=E DATA-FILE-ID=PREDICATE_FILTER> 547 * @param loadFactor <EMBED CLASS='external-html' DATA-FILE-ID=HASH_LOAD_FACTOR> 548 * @param elements <EMBED CLASS='external-html' DATA-FILE-ID=ELEMENTS_VAR_ARGS> 549 * 550 * @throws NullPointerException If {@code 'filter'} is passed null 551 */ 552 @SafeVarargs 553 public ReadOnlyHashSet( 554 Predicate<? super E> filter, 555 Float loadFactor, 556 E... elements 557 ) 558 { 559 Objects.requireNonNull(filter, ROHelpers.NULL_MSG + "'filter'"); 560 561 fromBuilderOrHashSet = false; 562 563 final HashSet<E> hashSet = (loadFactor != null) 564 ? new HashSet<>(elements.length, loadFactor) 565 : new HashSet<>(elements.length, 0.75f); 566 567 for (E e : elements) if (filter.test(e)) hashSet.add(e); 568 569 // Empty Optimization (throw away, completely, the reference, use static-constant) 570 this.hashSet = (hashSet.size() == 0) ? ((HashSet<E>) EMPTY_HASH_SET) : hashSet; 571 } 572 573 /** 574 * Builds {@code ReadOnlyHashSet<E>} instance having Generic-Type {@code 'E'}, and contents 575 * {@code 'elements'}. 576 * 577 * @param <T> <EMBED CLASS='external-html' DATA-FILE-ID=VARARGS_TYPE_PARAM> 578 * @param filter <EMBED CLASS='external-html' DATA-FTP=T DATA-FILE-ID=PREDICATE_FILTER> 579 * @param mapper <EMBED CLASS='external-html' DATA-FILE-ID=T_ARR_E_MAPPER> 580 * @param loadFactor <EMBED CLASS='external-html' DATA-FILE-ID=HASH_LOAD_FACTOR> 581 * @param elements <EMBED CLASS='external-html' DATA-FILE-ID=ELEMENTS_VAR_ARGS> 582 * 583 * @throws NullPointerException If either {@code 'mapper'} or {@code 'filter'} is passed null 584 */ 585 @SafeVarargs 586 public <T> ReadOnlyHashSet( 587 Predicate<? super T> filter, 588 Function<? super T, ? extends E> mapper, 589 Float loadFactor, 590 T... elements 591 ) 592 { 593 Objects.requireNonNull(filter, ROHelpers.NULL_MSG + "'filter'"); 594 Objects.requireNonNull(mapper, ROHelpers.NULL_MSG + "'mapper'"); 595 596 fromBuilderOrHashSet = false; 597 598 final HashSet<E> hashSet = (loadFactor != null) 599 ? new HashSet<>(elements.length, loadFactor) 600 : new HashSet<>(elements.length, 0.75f); 601 602 for (T t : elements) if (filter.test(t)) hashSet.add(mapper.apply(t)); 603 604 // Empty Optimization (throw away, completely, the reference, use static-constant) 605 this.hashSet = (hashSet.size() == 0) ? ((HashSet<E>) EMPTY_HASH_SET) : hashSet; 606 } 607 608 609 // ******************************************************************************************** 610 // ******************************************************************************************** 611 // PRIMITIVE-ARRAY INPUT 612 // ******************************************************************************************** 613 // ******************************************************************************************** 614 615 616 /** 617 * Converts a Java Primitive-Array to a {@code ReadOnlyHashSet<E>}, where {@code 'E'} is the 618 * Java Boxed-Type which corresponds to the Primitive-Array's Type. 619 * 620 * <EMBED CLASS='external-html' DATA-FILE-ID=PRIM_ARR_CTOR_DESC> 621 * @param primitiveArray <EMBED CLASS='external-html' DATA-FILE-ID=PRIMITIVE_ARRAY> 622 * @throws ClassCastException <EMBED CLASS='external-html' DATA-FILE-ID=PRIM_ARR_CCEX> 623 * @throws NullPointerException If {@code 'primitiveArray'} is passed null; 624 */ 625 @LinkJavaSource(handle="ROHelperPrimitiveArrays", name="buildROListOrSet") 626 public ReadOnlyHashSet( 627 Object primitiveArray, 628 Float loadFactor 629 ) 630 { 631 fromBuilderOrHashSet = false; 632 633 final HashSet<E> hs = ROHelperPrimitiveArrays.buildROListOrSet( 634 primitiveArray, 635 (int arrayLen) -> new HashSet<E>(arrayLen, (loadFactor != null) ? loadFactor : 0.75f), 636 null 637 ); 638 639 // Empty Optimization (throw away, completely, the reference, use static-constant) 640 this.hashSet = (hs.size() == 0) ? ((HashSet<E>) EMPTY_HASH_SET) : hs; 641 } 642 643 /** 644 * Converts a Java Primitive-Array to a {@code ReadOnlyHashSet<E>}, where {@code 'E'} is the 645 * Java Boxed-Type which corresponds to the Primitive-Array's Type - <I>but also accepts a 646 * {@code 'filter'} that can remove any array-entries that need to be removed</I>. 647 * 648 * <EMBED CLASS='external-html' DATA-FILE-ID=PRIM_ARR_CTOR_DESC> 649 * @param primitiveArray <EMBED CLASS='external-html' DATA-FILE-ID=PRIMITIVE_ARRAY> 650 * @param filter <EMBED CLASS='external-html' DATA-FILE-ID=PRED_FILT_PRIM> 651 * @throws ClassCastException <EMBED CLASS='external-html' DATA-FILE-ID=PRIM_ARR_CCEX> 652 * @throws NullPointerException If {@code 'primitiveArray'} is passed null; 653 */ 654 @LinkJavaSource(handle="ROHelperPrimitiveArrays", name="buildROListOrSet") 655 public ReadOnlyHashSet( 656 Object primitiveArray, 657 Float loadFactor, 658 Predicate<?> filter 659 ) 660 { 661 Objects.requireNonNull(filter, ROHelpers.NULL_MSG + "'filter'"); 662 663 fromBuilderOrHashSet = false; 664 665 final HashSet<E> hs = ROHelperPrimitiveArrays.buildROListOrSet( 666 primitiveArray, 667 (int arrayLen) -> new HashSet<E>(arrayLen, (loadFactor != null) ? loadFactor : 0.75f), 668 filter 669 ); 670 671 // Empty Optimization (throw away, completely, the reference, use static-constant) 672 this.hashSet = (hs.size() == 0) ? ((HashSet<E>) EMPTY_HASH_SET) : hs; 673 } 674 675 676 // ******************************************************************************************** 677 // ******************************************************************************************** 678 // java.util.stream.Stream HELPER 679 // ******************************************************************************************** 680 // ******************************************************************************************** 681 682 683 /** 684 * For use with a the Java Stream method {@code 'collect(Collector c)'}. 685 * 686 * <EMBED CLASS='external-html' DATA-ABBREV=hs DATA-FILE-ID=STREAM_COLLECTOR> 687 * 688 * @param <T> This is the Generic-Type of the Input-Stream. It will also be the Generic-Type 689 * of the {@code ReadOnlyHashSet} that's returned from the stream's {@code collect} method. 690 * 691 * @param characteristics Optional Characteristics List. See Java Stream-API Documentation on 692 * {@code Collector.Characteristics} inner-class for more details. 693 * 694 * @return This returns a collector that may be piped into a stream's {@code 'collect'} method, 695 * as in the example, above. 696 */ 697 public static <T> java.util.stream.Collector 698 <T, ROHashSetBuilder<T>, ReadOnlyHashSet<T>> 699 streamCollector(Collector.Characteristics... characteristics) 700 { 701 return Collector.of( 702 ROHashSetBuilder<T>::new, // The "Supplier" (builds a new ROHashSetBuilder) 703 ROHashSetBuilder<T>::add, // The "Accumulator" (adds elements to the builder) 704 705 // Oracle Making Life Difficult - It should be the line below, but, alas, it is not! 706 // ROHashSetBuilder<T>::addAll, // The "Combiner" (combines multiple ROHashSetBuilders) 707 // 708 // In Stream.collect(), the 3rd parameter - the "combiner" - is a "BiConsumer<R, R>" 709 // NOTE: A "BiConsumer" is a FunctionalInterface that does not return anything - it is 710 // (obviously) a "void" return method! 711 // 712 // **BUT** 713 // 714 // In Collector.of, the 3rd parameter - the "combiner" - is a "BinaryOperation<R>" 715 716 (ROHashSetBuilder<T> rohsb1, ROHashSetBuilder<T> rohsb2) -> 717 { 718 rohsb1.addAll(rohsb2); 719 return rohsb1; 720 }, 721 722 ROHashSetBuilder<T>::build, // The "Finisher" (Converts Builder to ReadOnlyHashSet) 723 characteristics 724 ); 725 } 726 727 728 // ******************************************************************************************** 729 // ******************************************************************************************** 730 // Convert to java.util Types 731 // ******************************************************************************************** 732 // ******************************************************************************************** 733 734 735 /** 736 * Clone's {@code 'this'} instance internal {@code HashSet<E>} field, and returns it. 737 * <EMBED CLASS='external-html' DATA-TYPE=HashSet DATA-FILE-ID=CLONE_TO> 738 * 739 * @return An independent, mutable copy of {@code 'this'} instance' internal {@code HashSet<E>} 740 * data-structure. 741 */ 742 @SuppressWarnings("unchecked") // The clone() cast 743 public HashSet<E> cloneToHashSet() 744 { 745 return fromBuilderOrHashSet 746 ? new HashSet<E>(this.hashSet) 747 : (HashSet<E>) this.hashSet.clone(); 748 } 749 750 /** 751 * <BR>Same: Identical to the method {@link #cloneToHashSet()}. 752 * <BR>Returns: Has Return-Type {@code Set<E>}, instead. 753 * <BR>Implements: Parent-Class {@link ReadOnlySet#cloneToSet} 754 */ 755 @IntoHTMLTable(title="Converts this ReadOnlyHashSet to a java.util.HashSet") 756 public Set<E> cloneToSet() { return cloneToHashSet(); } 757 758 // Documented in the Implemented-Interface ReadOnlySet 759 public Set<E> wrapToImmutableSet() 760 { return Collections.unmodifiableSet(this.hashSet); } 761 762 763 // ******************************************************************************************** 764 // ******************************************************************************************** 765 // Original JDK Methods, java.util.HashSet 766 // ******************************************************************************************** 767 // ******************************************************************************************** 768 769 770 /** 771 * Returns an iterator over the elements in this set. The elements are returned in no 772 * particular order. 773 * 774 * @return an {@code Iterator} over the elements in this set 775 * @see ConcurrentModificationException 776 */ 777 public RemoveUnsupportedIterator<E> iterator() 778 { 779 return fromBuilderOrHashSet 780 ? ((ROHashSetBuilder<E>) this.hashSet).iterator() 781 : new RemoveUnsupportedIterator<>(this.hashSet.iterator()); 782 } 783 784 /** 785 * Returns the number of elements in this set (its cardinality). 786 * @return the number of elements in this set (its cardinality) 787 */ 788 public int size() 789 { return this.hashSet.size(); } 790 791 /** 792 * Returns {@code TRUE} if this set contains no elements. 793 * @return {@code TRUE} if this set contains no elements 794 */ 795 public boolean isEmpty() 796 { return this.hashSet.isEmpty(); } 797 798 /** 799 * Returns {@code TRUE} if this set contains the specified element. More formally, returns 800 * {@code TRUE} if and only if this set contains an element {@code e} such that 801 * {@code Objects.equals(o, e)}. 802 * 803 * @param o element whose presence in this set is to be tested 804 * @return {@code TRUE} if this set contains the specified element 805 */ 806 public boolean contains(Object o) 807 { return this.hashSet.contains(o); } 808 809 /** 810 * Creates a <em><a href="Spliterator.html#binding">late-binding</a></em> and 811 * <em>fail-fast</em> {@code Spliterator} over the elements in this set. 812 * 813 * <BR /><BR />The {@code Spliterator} reports {@code Spliterator.SIZED} and 814 * {@code Spliterator.DISTINCT}. Overriding implementations should document the reporting of 815 * additional characteristic values. 816 * 817 * @return a {@code Spliterator} over the elements in this set 818 */ 819 public Spliterator<E> spliterator() 820 { return this.hashSet.spliterator(); } 821 822 823 // ******************************************************************************************** 824 // ******************************************************************************************** 825 // Misc Stuff that is defined in a super-class - for java.util, but not for Java.ReadOnly 826 // ******************************************************************************************** 827 // ******************************************************************************************** 828 829 830 public boolean containsAll(Collection<?> c) 831 { return this.hashSet.containsAll(c); } 832 833 @Override 834 public Object[] toArray() 835 { return this.hashSet.toArray(); } 836 837 @Override 838 public <T> T[] toArray(T[] a) 839 { return this.hashSet.toArray(a); } 840 841 842 // ******************************************************************************************** 843 // ******************************************************************************************** 844 // java.lang.Object 845 // ******************************************************************************************** 846 // ******************************************************************************************** 847 848 849 /** 850 * Returns a {@code String} representation of this {@code HashSet}. The {@code String} 851 * representation consists of a list of the collection's elements in the order they are 852 * returned by its iterator, enclosed in square brackets ({@code "[]"}). Adjacent elements are 853 * separated by the characters {@code ", "} (comma and space). Elements are converted to 854 * {@code String's} as by {@code String.valueOf(Object)}. 855 * 856 * @return a {@code String} representation of this {@code HashSet} 857 */ 858 public String toString() 859 { return this.hashSet.toString(); } 860 861 /** 862 * Compares the specified Object with this Set for equality, as per the definition in the 863 * class {@code java.util.HashSet}. 864 * 865 * @param o object to be compared for equality with this {@code ReadOnlyHashSet}. 866 * @return {@code TRUE} if the specified Object is equal to this set 867 */ 868 @LinkJavaSource(handle="ROHelperEquals", name="roSetEq") 869 public boolean equals(Object o) 870 { return ROHelperEquals.roSetEq(this, o); } 871 872 /** 873 * Returns the hash code value for this Set as per the definition in the class 874 * {@code java.util.HashSet}. 875 */ 876 public int hashCode() 877 { return this.hashSet.hashCode(); } 878}