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

org.reactfx.util.LL Maven / Gradle / Ivy

There is a newer version: 1.11
Show newest version
package org.reactfx.util;

import java.util.Collections;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.Spliterator;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

/**
 * Immutable singly-linked list.
 */
public abstract class LL implements Iterable {

    private static class Nil extends LL {
        private static final Nil INSTANCE = new Nil();

        @SuppressWarnings("unchecked")
        static  Nil instance() { return (Nil) INSTANCE; }

        @Override public boolean isEmpty() { return true; }
        @Override public int size() { return 0; }
        @Override public T head() { throw new NoSuchElementException(); }
        @Override public LL tail() { throw new NoSuchElementException(); }
        @Override public  LL map(Function f) { return instance(); }
        @Override public Iterator iterator() { return Collections.emptyIterator(); }

        @Override
        public  R fold(
                R acc,
                BiFunction reduction) {
            return acc;
        }

        @Override
        public  Optional mapReduce(
                Function map,
                BinaryOperator reduce) {
            return Optional.empty();
        }
    }

    public static final class Cons extends LL {
        private final T head;
        private final LL tail;
        private final int size;

        private Cons(T head, LL tail) {
            this.head = head;
            this.tail = tail;
            this.size = 1 + tail.size();
        }

        @Override
        public boolean isEmpty() {
            return false;
        }

        @Override
        public int size() {
            return size;
        }

        @Override
        public T head() {
            return head;
        }

        @Override
        public LL tail() {
            return tail;
        }

        @Override
        public  Cons map(Function f) {
            return cons(f.apply(head), tail.map(f));
        }

        @Override
        public  R fold(
                R acc,
                BiFunction reduction) {
            return tail.fold(reduction.apply(acc, head), reduction);
        }

        @Override
        public final Iterator iterator() {
            return new Iterator() {
                private LL l = Cons.this;

                @Override
                public boolean hasNext() {
                    return !l.isEmpty();
                }

                @Override
                public T next() {
                    T res = l.head();
                    l = l.tail();
                    return res;
                }
            };
        }

        @Override
        public  Optional mapReduce(
                Function map,
                BinaryOperator reduce) {
            return Optional.of(mapReduce1(map, reduce));
        }

        public  R mapReduce1(
                Function map,
                BinaryOperator reduce) {
            R acc = map.apply(head);
            return tail.fold(acc, (r, t) -> reduce.apply(r, map.apply(t)));
        }
    }

    public static  LL nil() {
        return Nil.instance();
    }

    public static  Cons cons(T head, LL tail) {
        return new Cons<>(head, tail);
    }

    @SafeVarargs
    public static  Cons of(T head, T... tail) {
        return cons(head, of(tail, tail.length, LL.nil()));
    }

    private static  LL of(T[] elems, int to, LL tail) {
        if(to == 0) {
            return tail;
        } else {
            return of(elems, to - 1, cons(elems[to-1], tail));
        }
    }

    public static  LL concat(LL l1, LL l2) {
        if(l1.isEmpty()) {
            return l2;
        } else {
            return cons(l1.head(), concat(l1.tail(), l2));
        }
    }

    // private constructor to prevent subclassing
    private LL() {}

    public abstract boolean isEmpty();
    public abstract int size();
    public abstract T head();
    public abstract LL tail();
    public abstract  LL map(Function f);
    public abstract  R fold(R acc, BiFunction reduction);
    public abstract  Optional mapReduce(
            Function map,
            BinaryOperator reduce);

    public boolean all(Predicate cond) {
        return fold(true, (b, t) -> b && cond.test(t));
    }

    public  U mapFirst2(BiFunction f) {
        return f.apply(head(), tail().head());
    }

    public  U mapFirst3(TriFunction f) {
        return tail().mapFirst2(f.pApply(head()));
    }

    public  U mapFirst4(TetraFunction f) {
        return tail().mapFirst3(f.pApply(head()));
    }

    public  U mapFirst5(PentaFunction f) {
        return tail().mapFirst4(f.pApply(head()));
    }

    public  U mapFirst6(HexaFunction f) {
        return tail().mapFirst5(f.pApply(head()));
    }

    @Override
    public String toString() {
        if(isEmpty()) {
            return "[]";
        } else {
            StringBuilder sb = new StringBuilder();
            sb.append("[");
            sb.append(head());
            LL tail = tail();
            while(!tail.isEmpty()) {
                sb.append(",").append(tail.head());
                tail = tail.tail();
            }
            sb.append("]");
            return sb.toString();
        }
    }

    public Stream stream() {
        Spliterator spliterator = new Spliterator() {
            private final Iterator iterator = iterator();

            @Override
            public boolean tryAdvance(Consumer action) {
                if(iterator.hasNext()) {
                    action.accept(iterator.next());
                    return true;
                } else {
                    return false;
                }
            }

            @Override
            public Spliterator trySplit() {
                return null;
            }

            @Override
            public long estimateSize() {
                return size();
            }

            @Override
            public int characteristics() {
                return Spliterator.IMMUTABLE | Spliterator.SIZED;
            }
        };

        return StreamSupport.stream(spliterator, false);
    }
}