diff --git a/src/simplestream/SimpleStream.java b/src/simplestream/SimpleStream.java new file mode 100644 index 0000000..8b7c751 --- /dev/null +++ b/src/simplestream/SimpleStream.java @@ -0,0 +1,517 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 Madhavan Lakshminarayanan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package simplestream; + +import java.lang.reflect.Array; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ConcurrentMap; +import java.util.function.BinaryOperator; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * A simplified interface to {@link Stream}. + * + * @param the type of stream elements + * + * @author Madhavan Lakshminarayanan + * @version 1.0 + */ +public final class SimpleStream { + private Stream stream; + + private SimpleStream(Stream stream) { + this.stream = stream; + } + + /** + * Creates a {@link SimpleStream} wrapping the specified {@link Stream}. + * + * @param stream the {@link Stream} to wrap + */ + public static SimpleStream stream(Stream stream) { + return new SimpleStream<>(stream); + } + + /** + * Creates a {@link SimpleStream} backed by the specified {@link Collection}. + * + * @param collection the source {@link Collection} + */ + public static SimpleStream stream(Collection collection) { + return new SimpleStream<>(collection.stream()); + } + + /** + * Creates a {@link SimpleStream} containing the specified values. + * + * @param the type of stream elements + * @param values the values to create the stream from + */ + @SafeVarargs + public static SimpleStream stream(T... values) { + return new SimpleStream<>(Stream.of(values)); + } + + /** + * Returns whether all elements in this stream match the given predicate. + * + * @param predicate the predicate to evaluate + * @see java.util.stream.Stream#allMatch(java.util.function.Predicate) + */ + public boolean allMatch(Predicate predicate) { + return stream.allMatch(predicate); + } + + /** + * Returns whether at least one element in this stream matches the given predicate. + * + * @param predicate the predicate to evaluate + * @see java.util.stream.Stream#anyMatch(java.util.function.Predicate) + */ + public boolean anyMatch(Predicate predicate) { + return stream.anyMatch(predicate); + } + + /** + * Returns the arithmetic mean of the values in this stream. + * + * @throws ClassCastException if the stream contains elements that cannot be cast to a {@link Number} + * @see java.util.stream.Collectors#averagingDouble(java.util.function.ToDoubleFunction) + */ + public double average() { + return stream.collect(Collectors.averagingDouble(t -> ((Number) t).doubleValue())); + } + + /** + * Casts the elements in this stream to the specified type. + *

+ * Because of the lazy nature of streams, this may cause a {@link ClassCastException} at a later time + * if an element cannot be cast to the specified type. + * + * @param type the type to cast to + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + public SimpleStream cast(Class type) { + return new SimpleStream<>((Stream) stream); + } + + /** + * Concatenates the specified stream to the end of this stream. + * + * @param stream the stream to be concatenated to the end of this stream + */ + public SimpleStream concat(SimpleStream stream) { + this.stream = Stream.concat(this.stream, stream.stream); + return this; + } + + /** + * Returns whether this stream contains the specified element. + * + * @param value the element to search for + */ + public boolean contains(T value) { + return stream.anyMatch(t -> Objects.equals(t, value)); + } + + /** + * Returns the count of elements in this stream. + * + * @see java.util.stream.Stream#count() + */ + public long count() { + return stream.count(); + } + + /** + * Filters this stream to include only distinct elements. + * + * @see java.util.stream.Stream#distinct() + */ + public SimpleStream distinct() { + this.stream = stream.distinct(); + return this; + } + + /** + * Returns an {@link Optional} value containing some element of this stream, if any. + * + * @see java.util.stream.Stream#findAny() + */ + public Optional findAny() { + return stream.findAny(); + } + + /** + * Returns an {@link Optional} value containing the first element of this stream, if any. + * + * @see java.util.stream.Stream#findFirst() + */ + public Optional findFirst() { + return stream.findFirst(); + } + + /** + * Applies the given one-to-many mapping function to the elements in this stream and flattens the result. + * + * @param mapper the one-to-many mapping function to apply + * @see java.util.stream.Stream#flatMap(java.util.function.Function) + */ + public SimpleStream flatMap(Function> mapper) { + return new SimpleStream<>(stream.flatMap(t -> mapper.apply(t).toStream())); + } + + /** + * Performs an action on each element in this stream. + * + * @param action the action to perform + * @see java.util.stream.Stream#forEach(java.util.function.Consumer) + */ + public void forEach(Consumer action) { + stream.forEach(action); + } + + /** + * Groups the elements in this stream according to the given classification function. + * + * @param classifier the classifier function that maps elements to keys + * @see java.util.stream.Collectors#groupingBy(java.util.function.Function) + */ + public Map> groupBy(Function classifier) { + return stream.collect(Collectors.groupingBy(classifier)); + } + + /** + * Concatenates the elements of this stream into a string. + * + * @return the concatenated string + * @see java.util.stream.Collectors#joining() + */ + public String join() { + return stream.map(Object::toString).collect(Collectors.joining()); + } + + /** + * Concatenates the elements of this stream into a string using the specified delimiter. + * + * @param delimiter the delimiter to be used between each element + * @return the concatenated string + * @see java.util.stream.Collectors#joining(CharSequence) + */ + public String join(String delimiter) { + return stream.map(Object::toString).collect(Collectors.joining(delimiter)); + } + + /** + * Limits the maximum number of elements in this stream. + * + * @param n the number of elements that the stream should be limited to + * @see java.util.stream.Stream#limit(long) + */ + public SimpleStream limit(long n) { + this.stream = stream.limit(n); + return this; + } + + /** + * Applies the given mapping function to the elements in this stream. + * + * @param mapper the mapping function to apply + * @see java.util.stream.Stream#map(java.util.function.Function) + */ + public SimpleStream map(Function mapper) { + return new SimpleStream<>(stream.map(mapper)); + } + + /** + * Returns the maximum element of this stream according to their natural order. + *

+ * Because of the lazy nature of streams, this may cause a {@link ClassCastException} at a later time + * if the elements are not {@link Comparable}. + * + * @see java.util.stream.Stream#max(java.util.Comparator) + */ + @SuppressWarnings("unchecked") + public Optional max() { + return stream.max(defaultComparator()); + } + + /** + * Returns the maximum element of this stream according to the given {@link Comparator}. + * + * @param comparator the {@link Comparator} to use when comparing elements + * @see java.util.stream.Stream#max(java.util.Comparator) + */ + public Optional max(Comparator comparator) { + return stream.max(comparator); + } + + /** + * Returns the minimum element of this stream according to their natural order. + *

+ * Because of the lazy nature of streams, this may cause a {@link ClassCastException} at a later time + * if the elements are not {@link Comparable}. + + * @see java.util.stream.Stream#min(java.util.Comparator) + */ + @SuppressWarnings("unchecked") + public Optional min() { + return stream.min(defaultComparator()); + } + + /** + * Returns the minimum element of this stream according to the given {@link Comparator}. + * + * @param comparator the {@link Comparator} to use when comparing elements + * @see java.util.stream.Stream#min(java.util.Comparator) + */ + public Optional min(Comparator comparator) { + return stream.min(comparator); + } + + /** + * Returns whether no elements in this stream match the given predicate. + * + * @param predicate the predicate to evaluate + * @see java.util.stream.Stream#noneMatch(java.util.function.Predicate) + */ + public boolean noneMatch(Predicate predicate) { + return stream.noneMatch(predicate); + } + + /** + * Sets the stream to parallel mode. + * + * @see java.util.stream.Stream#parallel() + */ + public SimpleStream parallel() { + this.stream = stream.parallel(); + return this; + } + + /** + * Performs a reduction on the elements of this stream, using the given accumulation function. + * If the stream could be empty, use {@link #reduce(T, BinaryOperator)} instead. + * + * @param accumulator the accumulation function to be used to combine two elements + * @throws NoSuchElementException if the stream is empty + * @see java.util.stream.Stream#reduce(java.util.function.BinaryOperator) + */ + public T reduce(BinaryOperator accumulator) { + return stream.reduce(accumulator).get(); + } + + /** + * Performs a reduction on the elements of this stream, using the given identity value + * and accumulation function. + * + * @param identity the identity value for the accumulation function + * @param accumulator the accumulation function to be used to combine two elements + * @see java.util.stream.Stream#reduce(T, java.util.function.BinaryOperator) + */ + public T reduce(T identity, BinaryOperator accumulator) { + return stream.reduce(identity, accumulator); + } + + /** + * Filters this stream to remove elements that match the given predicate. + * + * @param predicate the predicate to evaluate + * @see java.util.stream.Stream#filter(java.util.function.Predicate) + */ + public SimpleStream remove(Predicate predicate) { + return select(predicate.negate()); + } + + /** + * Filters this stream to retain only elements of the specified type. + * + * @param type the type of elements to retain + */ + public SimpleStream select(Class type) { + return select(t -> type.isInstance(t)).cast(type); + } + + /** + * Filters this stream to retain only elements that match the given predicate. + * + * @param predicate the predicate to evaluate + * @see java.util.stream.Stream#filter(java.util.function.Predicate) + */ + public SimpleStream select(Predicate predicate) { + this.stream = stream.filter(predicate); + return this; + } + + /** + * Sets the stream to sequential mode. + * + * @see java.util.stream.Stream#sequential() + */ + public SimpleStream sequential() { + this.stream = stream.sequential(); + return this; + } + + /** + * Skips the specified number of elements from this stream. + * + * @param n the number of elements to skip + * @see java.util.stream.Stream#skip(long) + */ + public SimpleStream skip(long n) { + this.stream = stream.skip(n); + return this; + } + + /** + * Sorts the elements in this stream according to their natural order. + *

+ * Because of the lazy nature of streams, this may cause a {@link ClassCastException} at a later time + * if the elements are not {@link Comparable}. + * + * @see java.util.stream.Stream#sorted() + */ + public SimpleStream sort() { + this.stream = stream.sorted(); + return this; + } + + /** + * Sorts the elements in this stream according to the given {@link Comparator}. + * + * @param comparator the {@link Comparator} to use when comparing elements + * @see java.util.stream.Stream#sorted(java.util.Comparator) + */ + public SimpleStream sort(Comparator comparator) { + this.stream = stream.sorted(comparator); + return this; + } + + /** + * Returns the sum of values in this stream as a double. + * + * @throws ClassCastException if the stream contains elements that cannot be cast to a {@link Number} + * @see java.util.stream.Collectors#summingDouble(java.util.function.ToDoubleFunction) + */ + public double sumAsDouble() { + return stream.collect(Collectors.summingDouble(t -> ((Number) t).doubleValue())); + } + + /** + * Returns the sum of values in this stream as an integer. + * + * @throws ClassCastException if the stream contains elements that cannot be cast to a {@link Number} + * @see java.util.stream.Collectors#summingInt(java.util.function.ToIntFunction) + */ + public int sumAsInt() { + return stream.collect(Collectors.summingInt(t -> ((Number) t).intValue())); + } + + /** + * Returns the sum of values in this stream as a long. + * + * @throws ClassCastException if the stream contains elements that cannot be cast to a {@link Number} + * @see java.util.stream.Collectors#summingLong(java.util.function.ToLongFunction) + */ + public long sumAsLong() { + return stream.collect(Collectors.summingLong(t -> ((Number) t).longValue())); + } + + /** + * Returns an array containing the elements in this stream. + * + * @param type the element type of the resulting array + * @throws ArrayStoreException if the specified element type is not a supertype of + * the runtime type of every element in this stream + */ + @SuppressWarnings("unchecked") + public R[] toArray(Class type) { + return stream.toArray(n -> (R[]) Array.newInstance(type, n)); + } + + /** + * Returns a {@link ConcurrentMap} containing key-value pairs derived from the elements in this stream. + * + * @param keyMapper the key mapping function + * @param valueMapper the value mapping function + * @see java.util.stream.Collectors#toConcurrentMap(java.util.function.Function, java.util.function.Function) + */ + public ConcurrentMap toConcurrentMap(Function keyMapper, Function valueMapper) { + return stream.collect(Collectors.toConcurrentMap(keyMapper, valueMapper)); + } + + /** + * Returns a {@link List} containing the elements in this stream. + * + * @see java.util.stream.Collectors#toList() + */ + public List toList() { + return stream.collect(Collectors.toList()); + } + + /** + * Returns a {@link Map} containing key-value pairs derived from the elements in this stream. + * + * @param keyMapper the key mapping function + * @param valueMapper the value mapping function + * @see java.util.stream.Collectors#toMap(java.util.function.Function, java.util.function.Function) + */ + public Map toMap(Function keyMapper, Function valueMapper) { + return stream.collect(Collectors.toMap(keyMapper, valueMapper)); + } + + /** + * Returns a {@link Set} containing the elements in this stream. + * + * @see java.util.stream.Collectors#toSet() + */ + public Set toSet() { + return stream.collect(Collectors.toSet()); + } + + /** + * Returns the underlying {@link Stream}. + */ + public Stream toStream() { + return stream; + } + + @SuppressWarnings("rawtypes") + private Comparator defaultComparator() { + return Comparator.naturalOrder(); + } +}