All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javaslang.collection.HashSet Maven / Gradle / Ivy

There is a newer version: 2.0.0-RC4
Show newest version
/*     / \____  _    _  ____   ______  / \ ____  __    _ _____
 *    /  /    \/ \  / \/    \ /  /\__\/  //    \/  \  / /  _  \   Javaslang
 *  _/  /  /\  \  \/  /  /\  \\__\\  \  //  /\  \ /\\/  \__/  /   Copyright 2014-now Daniel Dietrich
 * /___/\_/  \_/\____/\_/  \_/\__\/__/___\_/  \_//  \__/_____/    Licensed under the Apache License, Version 2.0
 */
package javaslang.collection;

import javaslang.Lazy;
import javaslang.Tuple;
import javaslang.Tuple2;
import javaslang.Tuple3;
import javaslang.control.Match;
import javaslang.control.Option;

import java.io.*;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.function.*;
import java.util.stream.Collector;

/**
 * An immutable {@code HashSet} implementation.
 *
 * @param  Component type
 * @author Ruslan Sennov, Patryk Najda, Daniel Dietrich
 * @since 2.0.0
 */
public final class HashSet implements Set, Serializable {

    private static final long serialVersionUID = 1L;

    private static final HashSet EMPTY = new HashSet<>(HashArrayMappedTrie.empty());

    private final HashArrayMappedTrie tree;
    private final transient Lazy hash;

    private HashSet(HashArrayMappedTrie tree) {
        this.tree = tree;
        this.hash = Lazy.of(() -> Traversable.hash(tree::iterator));
    }

    @SuppressWarnings("unchecked")
    public static  HashSet empty() {
        return (HashSet) EMPTY;
    }

    /**
     * Returns a {@link java.util.stream.Collector} which may be used in conjunction with
     * {@link java.util.stream.Stream#collect(java.util.stream.Collector)} to obtain a {@link javaslang.collection.HashSet}.
     *
     * @param  Component type of the HashSet.
     * @return A javaslang.collection.HashSet Collector.
     */
    public static  Collector, HashSet> collector() {
        final Supplier> supplier = ArrayList::new;
        final BiConsumer, T> accumulator = ArrayList::add;
        final BinaryOperator> combiner = (left, right) -> {
            left.addAll(right);
            return left;
        };
        final Function, HashSet> finisher = HashSet::ofAll;
        return Collector.of(supplier, accumulator, combiner, finisher);
    }

    /**
     * Returns a singleton {@code HashSet}, i.e. a {@code HashSet} of one element.
     *
     * @param element An element.
     * @param      The component type
     * @return A new HashSet instance containing the given element
     */
    public static  HashSet of(T element) {
        return HashSet. empty().add(element);
    }

    /**
     * Creates a HashSet of the given elements.
     *
     * 
HashSet.of(1, 2, 3, 4)
* * @param Component type of the HashSet. * @param elements Zero or more elements. * @return A set containing the given elements. * @throws NullPointerException if {@code elements} is null */ @SafeVarargs public static HashSet of(T... elements) { Objects.requireNonNull(elements, "elements is null"); HashArrayMappedTrie tree = HashArrayMappedTrie.empty(); for (T element : elements) { tree = tree.put(element, element); } return tree.isEmpty() ? empty() : new HashSet<>(tree); } /** * Returns an HashSet containing {@code n} values of a given Function {@code f} * over a range of integer values from 0 to {@code n - 1}. * * @param Component type of the HashSet * @param n The number of elements in the HashSet * @param f The Function computing element values * @return An HashSet consisting of elements {@code f(0),f(1), ..., f(n - 1)} * @throws NullPointerException if {@code f} is null */ public static HashSet tabulate(int n, Function f) { Objects.requireNonNull(f, "f is null"); return Collections.tabulate(n, f, HashSet.empty(), HashSet::of); } /** * Returns an HashSet containing {@code n} values supplied by a given Supplier {@code s}. * * @param Component type of the HashSet * @param n The number of elements in the HashSet * @param s The Supplier computing element values * @return An HashSet of size {@code n}, where each element contains the result supplied by {@code s}. * @throws NullPointerException if {@code s} is null */ public static HashSet fill(int n, Supplier s) { Objects.requireNonNull(s, "s is null"); return Collections.fill(n, s, HashSet.empty(), HashSet::of); } /** * Creates a HashSet of the given elements. * * @param elements Set elements * @param The value type * @return A new HashSet containing the given entries */ @SuppressWarnings("unchecked") public static HashSet ofAll(Iterable elements) { Objects.requireNonNull(elements, "elements is null"); if (elements instanceof HashSet) { return (HashSet) elements; } else { final HashArrayMappedTrie tree = addAll(HashArrayMappedTrie.empty(), elements); return tree.isEmpty() ? empty() : new HashSet<>(tree); } } /** * Creates a HashSet based on the elements of a boolean array. * * @param array a boolean array * @return A new HashSet of Boolean values */ public static HashSet ofAll(boolean[] array) { Objects.requireNonNull(array, "array is null"); return HashSet.ofAll(Iterator.ofAll(array)); } /** * Creates a HashSet based on the elements of a byte array. * * @param array a byte array * @return A new HashSet of Byte values */ public static HashSet ofAll(byte[] array) { Objects.requireNonNull(array, "array is null"); return HashSet.ofAll(Iterator.ofAll(array)); } /** * Creates a HashSet based on the elements of a char array. * * @param array a char array * @return A new HashSet of Character values */ public static HashSet ofAll(char[] array) { Objects.requireNonNull(array, "array is null"); return HashSet.ofAll(Iterator.ofAll(array)); } /** * Creates a HashSet based on the elements of a double array. * * @param array a double array * @return A new HashSet of Double values */ public static HashSet ofAll(double[] array) { Objects.requireNonNull(array, "array is null"); return HashSet.ofAll(Iterator.ofAll(array)); } /** * Creates a HashSet based on the elements of a float array. * * @param array a float array * @return A new HashSet of Float values */ public static HashSet ofAll(float[] array) { Objects.requireNonNull(array, "array is null"); return HashSet.ofAll(Iterator.ofAll(array)); } /** * Creates a HashSet based on the elements of an int array. * * @param array an int array * @return A new HashSet of Integer values */ public static HashSet ofAll(int[] array) { Objects.requireNonNull(array, "array is null"); return HashSet.ofAll(Iterator.ofAll(array)); } /** * Creates a HashSet based on the elements of a long array. * * @param array a long array * @return A new HashSet of Long values */ public static HashSet ofAll(long[] array) { Objects.requireNonNull(array, "array is null"); return HashSet.ofAll(Iterator.ofAll(array)); } /** * Creates a HashSet based on the elements of a short array. * * @param array a short array * @return A new HashSet of Short values */ public static HashSet ofAll(short[] array) { Objects.requireNonNull(array, "array is null"); return HashSet.ofAll(Iterator.ofAll(array)); } /** * Creates a HashSet of int numbers starting from {@code from}, extending to {@code toExclusive - 1}. *

* Examples: *

     * 
     * HashSet.range(0, 0)  // = HashSet()
     * HashSet.range(2, 0)  // = HashSet()
     * HashSet.range(-2, 2) // = HashSet(-2, -1, 0, 1)
     * 
     * 
* * @param from the first number * @param toExclusive the last number + 1 * @return a range of int values as specified or the empty range if {@code from >= toExclusive} */ public static HashSet range(int from, int toExclusive) { return HashSet.ofAll(Iterator.range(from, toExclusive)); } public static HashSet range(char from, char toExclusive) { return HashSet.ofAll(Iterator.range(from, toExclusive)); } /** * Creates a HashSet of int numbers starting from {@code from}, extending to {@code toExclusive - 1}, * with {@code step}. *

* Examples: *

     * 
     * HashSet.rangeBy(1, 3, 1)  // = HashSet(1, 2)
     * HashSet.rangeBy(1, 4, 2)  // = HashSet(1, 3)
     * HashSet.rangeBy(4, 1, -2) // = HashSet(4, 2)
     * HashSet.rangeBy(4, 1, 2)  // = HashSet()
     * 
     * 
* * @param from the first number * @param toExclusive the last number + 1 * @param step the step * @return a range of long values as specified or the empty range if
* {@code from >= toInclusive} and {@code step > 0} or
* {@code from <= toInclusive} and {@code step < 0} * @throws IllegalArgumentException if {@code step} is zero */ public static HashSet rangeBy(int from, int toExclusive, int step) { return HashSet.ofAll(Iterator.rangeBy(from, toExclusive, step)); } public static HashSet rangeBy(char from, char toExclusive, int step) { return HashSet.ofAll(Iterator.rangeBy(from, toExclusive, step)); } public static HashSet rangeBy(double from, double toExclusive, double step) { return HashSet.ofAll(Iterator.rangeBy(from, toExclusive, step)); } /** * Creates a HashSet of long numbers starting from {@code from}, extending to {@code toExclusive - 1}. *

* Examples: *

     * 
     * HashSet.range(0L, 0L)  // = HashSet()
     * HashSet.range(2L, 0L)  // = HashSet()
     * HashSet.range(-2L, 2L) // = HashSet(-2L, -1L, 0L, 1L)
     * 
     * 
* * @param from the first number * @param toExclusive the last number + 1 * @return a range of long values as specified or the empty range if {@code from >= toExclusive} */ public static HashSet range(long from, long toExclusive) { return HashSet.ofAll(Iterator.range(from, toExclusive)); } /** * Creates a HashSet of long numbers starting from {@code from}, extending to {@code toExclusive - 1}, * with {@code step}. *

* Examples: *

     * 
     * HashSet.rangeBy(1L, 3L, 1L)  // = HashSet(1L, 2L)
     * HashSet.rangeBy(1L, 4L, 2L)  // = HashSet(1L, 3L)
     * HashSet.rangeBy(4L, 1L, -2L) // = HashSet(4L, 2L)
     * HashSet.rangeBy(4L, 1L, 2L)  // = HashSet()
     * 
     * 
* * @param from the first number * @param toExclusive the last number + 1 * @param step the step * @return a range of long values as specified or the empty range if
* {@code from >= toInclusive} and {@code step > 0} or
* {@code from <= toInclusive} and {@code step < 0} * @throws IllegalArgumentException if {@code step} is zero */ public static HashSet rangeBy(long from, long toExclusive, long step) { return HashSet.ofAll(Iterator.rangeBy(from, toExclusive, step)); } /** * Creates a HashSet of int numbers starting from {@code from}, extending to {@code toInclusive}. *

* Examples: *

     * 
     * HashSet.rangeClosed(0, 0)  // = HashSet(0)
     * HashSet.rangeClosed(2, 0)  // = HashSet()
     * HashSet.rangeClosed(-2, 2) // = HashSet(-2, -1, 0, 1, 2)
     * 
     * 
* * @param from the first number * @param toInclusive the last number * @return a range of int values as specified or the empty range if {@code from > toInclusive} */ public static HashSet rangeClosed(int from, int toInclusive) { return HashSet.ofAll(Iterator.rangeClosed(from, toInclusive)); } public static HashSet rangeClosed(char from, char toInclusive) { return HashSet.ofAll(Iterator.rangeClosed(from, toInclusive)); } /** * Creates a HashSet of int numbers starting from {@code from}, extending to {@code toInclusive}, * with {@code step}. *

* Examples: *

     * 
     * HashSet.rangeClosedBy(1, 3, 1)  // = HashSet(1, 2, 3)
     * HashSet.rangeClosedBy(1, 4, 2)  // = HashSet(1, 3)
     * HashSet.rangeClosedBy(4, 1, -2) // = HashSet(4, 2)
     * HashSet.rangeClosedBy(4, 1, 2)  // = HashSet()
     * 
     * 
* * @param from the first number * @param toInclusive the last number * @param step the step * @return a range of int values as specified or the empty range if
* {@code from > toInclusive} and {@code step > 0} or
* {@code from < toInclusive} and {@code step < 0} * @throws IllegalArgumentException if {@code step} is zero */ public static HashSet rangeClosedBy(int from, int toInclusive, int step) { return HashSet.ofAll(Iterator.rangeClosedBy(from, toInclusive, step)); } public static HashSet rangeClosedBy(char from, char toInclusive, int step) { return HashSet.ofAll(Iterator.rangeClosedBy(from, toInclusive, step)); } public static HashSet rangeClosedBy(double from, double toInclusive, double step) { return HashSet.ofAll(Iterator.rangeClosedBy(from, toInclusive, step)); } /** * Creates a HashSet of long numbers starting from {@code from}, extending to {@code toInclusive}. *

* Examples: *

     * 
     * HashSet.rangeClosed(0L, 0L)  // = HashSet(0L)
     * HashSet.rangeClosed(2L, 0L)  // = HashSet()
     * HashSet.rangeClosed(-2L, 2L) // = HashSet(-2L, -1L, 0L, 1L, 2L)
     * 
     * 
* * @param from the first number * @param toInclusive the last number * @return a range of long values as specified or the empty range if {@code from > toInclusive} */ public static HashSet rangeClosed(long from, long toInclusive) { return HashSet.ofAll(Iterator.rangeClosed(from, toInclusive)); } /** * Creates a HashSet of long numbers starting from {@code from}, extending to {@code toInclusive}, * with {@code step}. *

* Examples: *

     * 
     * HashSet.rangeClosedBy(1L, 3L, 1L)  // = HashSet(1L, 2L, 3L)
     * HashSet.rangeClosedBy(1L, 4L, 2L)  // = HashSet(1L, 3L)
     * HashSet.rangeClosedBy(4L, 1L, -2L) // = HashSet(4L, 2L)
     * HashSet.rangeClosedBy(4L, 1L, 2L)  // = HashSet()
     * 
     * 
* * @param from the first number * @param toInclusive the last number * @param step the step * @return a range of int values as specified or the empty range if
* {@code from > toInclusive} and {@code step > 0} or
* {@code from < toInclusive} and {@code step < 0} * @throws IllegalArgumentException if {@code step} is zero */ public static HashSet rangeClosedBy(long from, long toInclusive, long step) { return HashSet.ofAll(Iterator.rangeClosedBy(from, toInclusive, step)); } @Override public HashSet add(T element) { return new HashSet<>(tree.put(element, element)); } @Override public HashSet addAll(Iterable elements) { Objects.requireNonNull(elements, "elements is null"); final HashArrayMappedTrie that = addAll(tree, elements); if (that.size() == tree.size()) { return this; } else { return new HashSet<>(that); } } @Override public HashSet clear() { return empty(); } @Override public boolean contains(T element) { return tree.get(element).isDefined(); } @Override public HashSet diff(Set elements) { Objects.requireNonNull(elements, "elements is null"); if (isEmpty() || elements.isEmpty()) { return this; } else { return removeAll(elements); } } @Override public HashSet distinct() { return this; } @Override public HashSet distinctBy(Comparator comparator) { Objects.requireNonNull(comparator, "comparator is null"); return HashSet.ofAll(iterator().distinctBy(comparator)); } @Override public HashSet distinctBy(Function keyExtractor) { Objects.requireNonNull(keyExtractor, "keyExtractor is null"); return HashSet.ofAll(iterator().distinctBy(keyExtractor)); } @Override public HashSet drop(long n) { if (n <= 0) { return this; } else { return HashSet.ofAll(iterator().drop(n)); } } @Override public HashSet dropRight(long n) { return drop(n); } @Override public HashSet dropUntil(Predicate predicate) { Objects.requireNonNull(predicate, "predicate is null"); return dropWhile(predicate.negate()); } @Override public HashSet dropWhile(Predicate predicate) { Objects.requireNonNull(predicate, "predicate is null"); final HashSet dropped = HashSet.ofAll(iterator().dropWhile(predicate)); return dropped.length() == length() ? this : dropped; } @Override public HashSet filter(Predicate predicate) { Objects.requireNonNull(predicate, "predicate is null"); final HashSet filtered = HashSet.ofAll(iterator().filter(predicate)); return filtered.length() == length() ? this : filtered; } @Override public HashSet flatMap(Function> mapper) { Objects.requireNonNull(mapper, "mapper is null"); if (isEmpty()) { return empty(); } else { final HashArrayMappedTrie that = foldLeft(HashArrayMappedTrie.empty(), (tree, t) -> addAll(tree, mapper.apply(t))); return new HashSet<>(that); } } @Override public U foldRight(U zero, BiFunction f) { return foldLeft(zero, (u, t) -> f.apply(t, u)); } @Override public Map> groupBy(Function classifier) { return foldLeft(HashMap.empty(), (map, t) -> { final C key = classifier.apply(t); final HashSet values = map.get(key).map(ts -> ts.add(t)).getOrElse(HashSet.of(t)); return map.put(key, values); }); } @Override public Iterator> grouped(long size) { return sliding(size, size); } @Override public boolean hasDefiniteSize() { return true; } @Override public T head() { if (tree.isEmpty()) { throw new NoSuchElementException("head of empty set"); } return iterator().next(); } @Override public Option headOption() { return iterator().headOption(); } @Override public HashSet init() { return tail(); } @Override public Option> initOption() { return tailOption(); } @Override public HashSet intersect(Set elements) { Objects.requireNonNull(elements, "elements is null"); if (isEmpty() || elements.isEmpty()) { return empty(); } else { return retainAll(elements); } } @Override public boolean isEmpty() { return tree.isEmpty(); } @Override public boolean isTraversableAgain() { return true; } @Override public Iterator iterator() { return tree.iterator().map(t -> t._1); } @Override public int length() { return tree.size(); } @Override public HashSet map(Function mapper) { Objects.requireNonNull(mapper, "mapper is null"); if (isEmpty()) { return empty(); } else { final HashArrayMappedTrie that = foldLeft(HashArrayMappedTrie.empty(), (tree, t) -> { final U u = mapper.apply(t); return tree.put(u, u); }); return new HashSet<>(that); } } @Override public Match.MatchValue.Of> match() { return Match.of(this); } @Override public String mkString(CharSequence prefix, CharSequence delimiter, CharSequence suffix) { return iterator().mkString(prefix, delimiter, suffix); } @Override public Tuple2, HashSet> partition(Predicate predicate) { Objects.requireNonNull(predicate, "predicate is null"); final Tuple2, Iterator> p = iterator().partition(predicate); return Tuple.of(HashSet.ofAll(p._1), HashSet.ofAll(p._2)); } @Override public HashSet peek(Consumer action) { Objects.requireNonNull(action, "action is null"); if (!isEmpty()) { action.accept(iterator().head()); } return this; } @Override public HashSet remove(T element) { final HashArrayMappedTrie newTree = tree.remove(element); return (newTree == tree) ? this : new HashSet<>(newTree); } @Override public HashSet removeAll(Iterable elements) { Objects.requireNonNull(elements, "elements is null"); HashArrayMappedTrie trie = tree; for (T element : elements) { trie = trie.remove(element); } return (trie == tree) ? this : new HashSet<>(trie); } @Override public HashSet replace(T currentElement, T newElement) { if (tree.containsKey(currentElement)) { return remove(currentElement).add(newElement); } else { return this; } } @Override public HashSet replaceAll(T currentElement, T newElement) { return replace(currentElement, newElement); } @Override public HashSet retainAll(Iterable elements) { Objects.requireNonNull(elements, "elements is null"); final HashArrayMappedTrie kept = addAll(HashArrayMappedTrie.empty(), elements); HashArrayMappedTrie that = HashArrayMappedTrie.empty(); for (T element : this) { if (kept.containsKey(element)) { that = that.put(element, element); } } return that.isEmpty() ? empty() : that.size() == size() ? this : new HashSet<>(that); } @Override public HashSet scan(T zero, BiFunction operation) { return scanLeft(zero, operation); } @Override public HashSet scanLeft(U zero, BiFunction operation) { Objects.requireNonNull(operation, "operation is null"); return Collections.scanLeft(this, zero, operation, new java.util.ArrayList<>(), (c, u) -> { c.add(u); return c; }, HashSet::ofAll); } @Override public HashSet scanRight(U zero, BiFunction operation) { Objects.requireNonNull(operation, "operation is null"); return Collections.scanRight(this, zero, operation, HashSet.empty(), HashSet::add, Function.identity()); } @Override public Iterator> sliding(long size) { return sliding(size, 1); } @Override public Iterator> sliding(long size, long step) { return iterator().sliding(size, step).map(HashSet::ofAll); } @Override public Tuple2, HashSet> span(Predicate predicate) { Objects.requireNonNull(predicate, "predicate is null"); final Tuple2, Iterator> t = iterator().span(predicate); return Tuple.of(HashSet.ofAll(t._1), HashSet.ofAll(t._2)); } @Override public HashSet tail() { if (tree.isEmpty()) { throw new UnsupportedOperationException("tail of empty set"); } return remove(head()); } @Override public Option> tailOption() { if (tree.isEmpty()) { return Option.none(); } else { return Option.some(tail()); } } @Override public HashSet take(long n) { if (tree.size() <= n) { return this; } return HashSet.ofAll(() -> iterator().take(n)); } @Override public HashSet takeRight(long n) { return take(n); } @Override public HashSet takeUntil(Predicate predicate) { Objects.requireNonNull(predicate, "predicate is null"); return takeWhile(predicate.negate()); } @Override public HashSet takeWhile(Predicate predicate) { Objects.requireNonNull(predicate, "predicate is null"); HashSet taken = HashSet.ofAll(iterator().takeWhile(predicate)); return taken.length() == length() ? this : taken; } @SuppressWarnings("unchecked") @Override public HashSet union(Set elements) { Objects.requireNonNull(elements, "elements is null"); if (isEmpty()) { if (elements instanceof HashSet) { return (HashSet) elements; } else { return HashSet.ofAll(elements); } } else if (elements.isEmpty()) { return this; } else { final HashArrayMappedTrie that = addAll(tree, elements); if (that.size() == tree.size()) { return this; } else { return new HashSet<>(that); } } } @Override public Tuple2, HashSet> unzip( Function> unzipper) { Objects.requireNonNull(unzipper, "unzipper is null"); Tuple2, Iterator> t = iterator().unzip(unzipper); return Tuple.of(HashSet.ofAll(t._1), HashSet.ofAll(t._2)); } @Override public Tuple3, HashSet, HashSet> unzip3( Function> unzipper) { Objects.requireNonNull(unzipper, "unzipper is null"); Tuple3, Iterator, Iterator> t = iterator().unzip3(unzipper); return Tuple.of(HashSet.ofAll(t._1), HashSet.ofAll(t._2), HashSet.ofAll(t._3)); } @Override public HashSet> zip(Iterable that) { Objects.requireNonNull(that, "that is null"); return HashSet.ofAll(iterator().zip(that)); } @Override public HashSet> zipAll(Iterable that, T thisElem, U thatElem) { Objects.requireNonNull(that, "that is null"); return HashSet.ofAll(iterator().zipAll(that, thisElem, thatElem)); } @Override public HashSet> zipWithIndex() { return HashSet.ofAll(iterator().zipWithIndex()); } // -- Object @Override public int hashCode() { return hash.get(); } @SuppressWarnings("unchecked") @Override public boolean equals(Object o) { if (o == this) { return true; } else if (o instanceof HashSet) { final HashSet that = (HashSet) o; return this.length() == that.length() && ((HashSet) this).containsAll(that); } else { return false; } } @Override public String stringPrefix() { return "HashSet"; } @Override public String toString() { return mkString(stringPrefix() + "(", ", ", ")"); } private static HashArrayMappedTrie addAll(HashArrayMappedTrie initial, Iterable additional) { HashArrayMappedTrie that = initial; for (T t : additional) { that = that.put(t, t); } return that; } // -- Serialization /** * {@code writeReplace} method for the serialization proxy pattern. *

* The presence of this method causes the serialization system to emit a SerializationProxy instance instead of * an instance of the enclosing class. * * @return A SerialiationProxy for this enclosing class. */ private Object writeReplace() { return new SerializationProxy<>(this.tree); } /** * {@code readObject} method for the serialization proxy pattern. *

* Guarantees that the serialization system will never generate a serialized instance of the enclosing class. * * @param stream An object serialization stream. * @throws java.io.InvalidObjectException This method will throw with the message "Proxy required". */ private void readObject(ObjectInputStream stream) throws InvalidObjectException { throw new InvalidObjectException("Proxy required"); } /** * A serialization proxy which, in this context, is used to deserialize immutable, linked Lists with final * instance fields. * * @param The component type of the underlying list. */ // DEV NOTE: The serialization proxy pattern is not compatible with non-final, i.e. extendable, // classes. Also, it may not be compatible with circular object graphs. private static final class SerializationProxy implements Serializable { private static final long serialVersionUID = 1L; // the instance to be serialized/deserialized private transient HashArrayMappedTrie tree; /** * Constructor for the case of serialization, called by {@link HashSet#writeReplace()}. *

* The constructor of a SerializationProxy takes an argument that concisely represents the logical state of * an instance of the enclosing class. * * @param tree a Cons */ SerializationProxy(HashArrayMappedTrie tree) { this.tree = tree; } /** * Write an object to a serialization stream. * * @param s An object serialization stream. * @throws java.io.IOException If an error occurs writing to the stream. */ private void writeObject(ObjectOutputStream s) throws IOException { s.defaultWriteObject(); s.writeInt(tree.size()); for (Tuple2 e : tree) { s.writeObject(e._1); } } /** * Read an object from a deserialization stream. * * @param s An object deserialization stream. * @throws ClassNotFoundException If the object's class read from the stream cannot be found. * @throws InvalidObjectException If the stream contains no list elements. * @throws IOException If an error occurs reading from the stream. */ private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException { s.defaultReadObject(); final int size = s.readInt(); if (size < 0) { throw new InvalidObjectException("No elements"); } HashArrayMappedTrie temp = HashArrayMappedTrie.empty(); for (int i = 0; i < size; i++) { @SuppressWarnings("unchecked") final T element = (T) s.readObject(); temp = temp.put(element, element); } tree = temp; } /** * {@code readResolve} method for the serialization proxy pattern. *

* Returns a logically equivalent instance of the enclosing class. The presence of this method causes the * serialization system to translate the serialization proxy back into an instance of the enclosing class * upon deserialization. * * @return A deserialized instance of the enclosing class. */ private Object readResolve() { return tree.isEmpty() ? HashSet.empty() : new HashSet<>(tree); } } }