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

solid.stream.Stream Maven / Gradle / Ivy

package solid.stream;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import solid.collections.Grouped;
import solid.collections.Indexed;
import solid.collectors.ToArrayList;
import solid.functions.Action1;
import solid.functions.Func1;
import solid.functions.Func2;
import solid.optional.Optional;

/**
 * This is a base stream class for implementation of iterable streams.
 * It provides shortcuts for calling operators in a chaining manner.
 *
 * @param  the type of object returned by the iterator.
 */
public abstract class Stream implements Iterable {

    /**
     * Converts a non-primitive array into a {@link Stream}.
     *
     * @param array array to convert.
     * @param    a type of array items.
     * @return a {@link Stream} that represents source array's elements.
     */
    public static  Stream stream(final T[] array) {
        return new FixedSizeStream<>(array.length, new Func1() {
            @Override
            public T call(Integer index) {
                return array[index];
            }
        });
    }

    /**
     * Converts a source {@link Iterable} into a {@link Stream}.
     *
     * @param source a source {@link Iterable} to convert.
     * @param     a type of stream items
     * @return a {@link Stream} that represents source {@link Iterable} elements
     */
    public static  Stream stream(final Iterable source) {
        return new Stream() {
            @Override
            public Iterator iterator() {
                return source.iterator();
            }
        };
    }

    /**
     * Returns a stream with just one given element.
     *
     * @param value the element value.
     * @param    the type of the stream.
     * @return a stream with just one given element.
     */
    public static  Stream of(final T value) {
        return new Stream() {
            @Override
            public Iterator iterator() {
                return new ReadOnlyIterator() {

                    boolean has = true;

                    @Override
                    public boolean hasNext() {
                        return has;
                    }

                    @Override
                    public T next() {
                        has = false;
                        return value;
                    }
                };
            }
        };
    }

    /**
     * Returns a stream of given values.
     *
     * @param values stream values.
     * @param     the type of the stream.
     * @return a stream of given values.
     */
    public static  Stream of(T... values) {
        return stream(values);
    }

    /**
     * Returns an empty stream.
     *
     * @param  the type of the stream.
     * @return an empty stream.
     */
    public static  Stream of() {
        return EMPTY;
    }

    /**
     * Converts the current stream into any value with a given method.
     *
     * @param collector a method that should be used to return value.
     * @param        a type of value to return.
     * @return a value that has been returned by the given collecting method.
     */
    public  R collect(Func1, R> collector) {
        return collector.call(this);
    }

    /**
     * Returns a value that has been received by applying an accumulating function to each item of the current stream.
     * An initial value should be provided.
     *
     * @param          a type of the returning and initial values.
     * @param accumulator a function to apply to each stream item.
     * @return a value that has been received by applying an accumulating function to each item of the current stream.
     */
    public  R reduce(R initial, Func2 accumulator) {
        R value = initial;
        for (T anIt : this)
            value = accumulator.call(value, anIt);
        return value;
    }

    /**
     * Returns a value that has been received by applying an accumulating function to each item of the current stream.
     * An initial value is taken from the first value.
     *
     * If the stream is empty an {@link UnsupportedOperationException} will be thrown.
     *
     * @param accumulator a function to apply to each (except the first one) stream item.
     * @return a value that has been received by applying an accumulating function to each item of the current stream.
     */
    public Optional reduce(Func2 accumulator) {
        Iterator iterator = iterator();
        if (!iterator.hasNext())
            return Optional.empty();

        T result = iterator.next();
        while (iterator.hasNext())
            result = accumulator.call(result, iterator.next());
        return Optional.of(result);
    }

    /**
     * Convert an iterable stream into one first item of the stream.
     *
     * @return the first item of the stream.
     */
    public Optional first() {
        Iterator iterator = iterator();
        return iterator.hasNext() ? Optional.of(iterator.next()) : Optional.empty();
    }

    /**
     * Convert an iterable stream into one last item of the stream.
     *
     * @return the last item of the stream.
     */
    public Optional last() {
        Iterator iterator = iterator();
        T value = null;
        while (iterator.hasNext())
            value = iterator.next();
        return Optional.of(value);
    }

    /**
     * Returns a new stream that is created by a given factory.
     * The factory accepts the current stream as an argument.
     *
     * @param factory a method that produces the new stream.
     * @param      a type of items new stream returns.
     * @return a constructed stream.
     */
    public  Stream compose(Func1, Stream> factory) {
        return factory.call(this);
    }

    /**
     * Returns a new stream that contains items that has been returned by a given function for each item in the current stream.
     *
     * @param func a function that takes an item of the current stream and returns a corresponding value for the new stream.
     * @param   a type of items new stream returns.
     * @return a new stream that contains items that has been returned by a given function for each item in the current stream.
     */
    public  Stream map(final Func1 func) {
        return new Stream() {
            @Override
            public Iterator iterator() {
                return new ReadOnlyIterator() {

                    Iterator iterator = Stream.this.iterator();

                    @Override
                    public boolean hasNext() {
                        return iterator.hasNext();
                    }

                    @Override
                    public R next() {
                        return func.call(iterator.next());
                    }
                };
            }
        };
    }

    /**
     * Returns a new stream that contains items that has been returned a given function for each item in the current stream.
     * The difference from {@link #map(Func1)} is that a given function can return more than one item for
     * each item of the current list.
     *
     * @param func a function that takes an item of the current stream and returns a stream of values for the new stream.
     * @param   a type of items new stream returns.
     * @return a new stream that contains items that has been returned by a given function for each item in the current stream.
     */
    public  Stream flatMap(final Func1> func) {
        return new Stream() {
            @Override
            public Iterator iterator() {
                return new ReadOnlyIterator() {

                    Iterator iterator = Stream.this.iterator();
                    Iterator next;

                    @Override
                    public boolean hasNext() {
                        while ((next == null || !next.hasNext()) && iterator.hasNext()) {
                            next = func.call(iterator.next()).iterator();
                        }

                        return next != null && next.hasNext();
                    }

                    @Override
                    public R next() {
                        return next.next();
                    }
                };
            }
        };
    }

    /**
     * Returns a new stream that contains all items of the current stream for which a given function returned {@link Boolean#TRUE}.
     *
     * @param func a function to call for each item.
     * @return a new stream that contains all items of the current stream for which a given function returned {@link Boolean#TRUE}.
     */
    public Stream filter(final Func1 func) {
        return new Stream() {
            @Override
            public Iterator iterator() {
                return new ReadOnlyIterator() {

                    Iterator iterator = Stream.this.iterator();
                    T next;
                    boolean hasNext;

                    private void process() {
                        while (!hasNext && iterator.hasNext()) {
                            final T n = iterator.next();
                            if (func.call(n)) {
                                next = n;
                                hasNext = true;
                            }
                        }
                    }

                    @Override
                    public boolean hasNext() {
                        process();
                        return hasNext;
                    }

                    @Override
                    public T next() {
                        process();
                        hasNext = false;
                        return next;
                    }
                };
            }
        };
    }

    /**
     * Returns a new stream that contains all items of the current stream with addition of a given item.
     *
     * @param value a value to add.
     * @return a new stream that contains all items of the current stream with addition of a given item.
     */
    public Stream merge(final T value) {
        return new Stream() {
            @Override
            public Iterator iterator() {
                return new ReadOnlyIterator() {

                    Iterator iterator = Stream.this.iterator();
                    boolean completed;

                    @Override
                    public boolean hasNext() {
                        return iterator.hasNext() || !completed;
                    }

                    @Override
                    public T next() {
                        if (iterator.hasNext())
                            return iterator.next();
                        completed = true;
                        return value;
                    }
                };
            }
        };
    }

    /**
     * Returns a new stream that contains all items of the current stream except of a given item.
     *
     * @param value a value to filter out.
     * @return a new stream that contains all items of the current stream except of a given item.
     */
    public Stream separate(final T value) {
        return filter(new Func1() {
            @Override
            public Boolean call(T it) {
                return ((it == null) ? (value != null) : !it.equals(value));
            }
        });
    }

    /**
     * Adds items from another stream to the end of the current stream.
     *
     * @param with an {@link Iterable} that should be used to emit items after items in the current stream ran out.
     * @return a new stream that contains items from both streams.
     */
    public Stream merge(final Iterable with) {
        return new Stream() {
            @Override
            public Iterator iterator() {
                return new ReadOnlyIterator() {

                    Iterator iterator = Stream.this.iterator();
                    Iterator withIterator = with.iterator();

                    @Override
                    public boolean hasNext() {
                        return iterator.hasNext() || withIterator.hasNext();
                    }

                    @Override
                    public T next() {
                        return iterator.hasNext() ? iterator.next() : withIterator.next();
                    }
                };
            }
        };
    }

    /**
     * Returns a stream that includes only that items of the current stream that do not
     * exist in a given stream.
     *
     * @param from a stream of values that should be separated from the current stream.
     * @return a stream that includes only that items of the current stream that do not
     * exist in a given stream.
     */
    public Stream separate(Iterable from) {
        final ArrayList list = ToArrayList.toArrayList().call(from);
        return filter(new Func1() {
            @Override
            public Boolean call(T it) {return !list.contains(it);}
        });
    }

    /**
     * Creates a new stream that contains only the first given amount of items of the current stream.
     *
     * @param count a number of items to take.
     * @return a new stream that contains only the first given amount of items of the current stream.
     */
    public Stream take(final int count) {
        return new Stream() {
            @Override
            public Iterator iterator() {
                return new ReadOnlyIterator() {

                    Iterator iterator = Stream.this.iterator();
                    int left = count;

                    @Override
                    public boolean hasNext() {
                        return left > 0 && iterator.hasNext();
                    }

                    @Override
                    public T next() {
                        left--;
                        return iterator.next();
                    }
                };
            }
        };
    }

    /**
     * Creates a new stream that contains elements of the current stream with a given number of them skipped from the beginning.
     *
     * @param count a number items to skip.
     * @return a new stream that contains elements of the current stream with a given number of them skipped from the beginning.
     */
    public Stream skip(final int count) {
        return new Stream() {
            @Override
            public Iterator iterator() {
                Iterator iterator = Stream.this.iterator();
                for (int skip = count; skip > 0 && iterator.hasNext(); skip--)
                    iterator.next();
                return iterator;
            }
        };
    }

    /**
     * Returns a new stream that filters out duplicate items off the current stream.
     * 

* This operator keeps a list of all items that has been passed to * compare it against next items. * * @return a new stream that filters out duplicate items off the current stream. */ public Stream distinct() { final ArrayList passed = new ArrayList<>(); return filter(new Func1() { @Override public Boolean call(T value) { if (passed.contains(value)) return false; passed.add(value); return true; } }); } /** * Returns a new stream that contains all items of the current stream in sorted order. * The operator creates a list of all items internally. * * @param comparator a comparator to apply. * @return a new stream that contains all items of the current stream in sorted order. */ public Stream sort(final Comparator comparator) { return new Stream() { @Override public Iterator iterator() { final ArrayList array = ToArrayList.toArrayList().call(Stream.this); Collections.sort(array, comparator); return array.iterator(); } }; } /** * Returns a new stream that contains all items of the current stream in reverse order. * The operator creates a list of all items internally. * * @return a new stream that emits all items of the current stream in reverse order. */ public Stream reverse() { return new Stream() { @Override public Iterator iterator() { final ArrayList array = ToArrayList.toArrayList().call(Stream.this); Collections.reverse(array); return array.iterator(); } }; } /** * Returns a stream that contains all values of the original stream that has been casted to a given class type. * * @param c a class to cast into * @param a type of the class * @return a stream that contains all values of the original stream that has been casted to a given class type. */ public Stream cast(final Class c) { return map(new Func1() { @Override public R call(T obj) {return c.cast(obj);} }); } /** * Returns true if all of stream items satisfy a given condition. * * @param predicate a condition to test. * @return true if all of stream items satisfy a given condition. */ public boolean every(Func1 predicate) { for (T item : this) { if (!predicate.call(item)) return false; } return true; } /** * Returns true if any of stream items satisfy a given condition. * * @param predicate a condition to test. * @return true if any of stream items satisfy a given condition. */ public boolean any(Func1 predicate) { for (T item : this) { if (predicate.call(item)) return true; } return false; } /** * Returns a new stream of {@link Grouped} that is composed from keys and values that has been * extracted from each source stream item. * * @param groupSelector a function that extracts a key from a given item. * @param valueSelector a function that extracts a value from a given item. * @param a type of key value. * @return a new stream of {@link Grouped} that is grouped by a key extracted from each source stream item. */ public Stream> groupBy(final Func1 groupSelector, final Func1 valueSelector) { return new Stream>() { @Override public Iterator> iterator() { List keys = new ArrayList<>(); final Map> map = new HashMap<>(); for (T item : Stream.this) { K key = groupSelector.call(item); ArrayList list = map.get(key); if (list == null) { keys.add(key); map.put(key, list = new ArrayList<>()); } list.add(valueSelector.call(item)); } return stream(keys) .map(new Func1>() { @Override public Grouped call(K key) { return new Grouped<>(key, stream(map.get(key))); } }) .iterator(); } }; } /** * Returns a new stream of {@link Grouped} that is composed from keys that has been * extracted from each source stream item. * * @param groupSelector a function that extracts a key from a given item. * @param a type of key value. * @return a new stream of {@link Grouped} that is grouped by a key extracted from each source stream item. */ public Stream> groupBy(final Func1 groupSelector) { return groupBy(groupSelector, new Func1() { @Override public T call(T value) { return value; } }); } /** * Returns a new stream of {@link Indexed} that where each item's index is equal to its sequence number. * * @return a new stream of {@link Indexed} that where each item's index is equal to its sequence number. */ public Stream> index() { return map(new Func1>() { int i; @Override public Indexed call(T value) { return new Indexed<>(i++, value); } }); } /** * Executes an action for each item in the stream. * * @param action an action to execute for each item in the stream. */ public void forEach(Action1 action) { for (T value : this) action.call(value); } /** * Executes an action for each item in the stream. * * @param action an action to execute for each item in the stream. */ public Stream onNext(final Action1 action) { return new Stream() { @Override public Iterator iterator() { return new ReadOnlyIterator() { Iterator iterator = Stream.this.iterator(); @Override public boolean hasNext() { return iterator.hasNext(); } @Override public T next() { final T next = iterator.next(); action.call(next); return next; } }; } }; } private static final ReadOnlyIterator EMPTY_ITERATOR = new ReadOnlyIterator() { @Override public boolean hasNext() { return false; } @Override public Object next() { throw new UnsupportedOperationException(); } }; private static final Stream EMPTY = new Stream() { @Override public Iterator iterator() { return EMPTY_ITERATOR; } }; }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy