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&#46;util&#46;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 &amp; 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}