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