javaslang.collection.HashSet Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of javaslang Show documentation
Show all versions of javaslang Show documentation
Javaslang is a Java standard library extension built for Java 8 and above.
/* / \____ _ _ ____ ______ / \ ____ __ _ _____
* / / \/ \ / \/ \ / /\__\/ // \/ \ / / _ \ 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 super Integer, ? extends T> 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 extends T> 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 extends T> 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 extends T> 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 extends T> 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 super T> comparator) {
Objects.requireNonNull(comparator, "comparator is null");
return HashSet.ofAll(iterator().distinctBy(comparator));
}
@Override
public HashSet distinctBy(Function super T, ? extends U> 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 super T> predicate) {
Objects.requireNonNull(predicate, "predicate is null");
return dropWhile(predicate.negate());
}
@Override
public HashSet dropWhile(Predicate super T> 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 super T> 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 super T, ? extends Iterable extends U>> 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 super T, ? super U, ? extends U> f) {
return foldLeft(zero, (u, t) -> f.apply(t, u));
}
@Override
public Map> groupBy(Function super T, ? extends C> 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 extends T> 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 super T, ? extends U> 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 super T> 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 super T> 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 extends T> 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 extends T> 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 super T, ? super T, ? extends T> operation) {
return scanLeft(zero, operation);
}
@Override
public HashSet scanLeft(U zero, BiFunction super U, ? super T, ? extends U> 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 super T, ? super U, ? extends U> 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 super T> 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 super T> predicate) {
Objects.requireNonNull(predicate, "predicate is null");
return takeWhile(predicate.negate());
}
@Override
public HashSet takeWhile(Predicate super T> 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 extends T> 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 super T, Tuple2 extends T1, ? extends T2>> 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 super T, Tuple3 extends T1, ? extends T2, ? extends T3>> 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