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

org.organicdesign.fp.collections.UnmodIterable Maven / Gradle / Ivy

package org.organicdesign.fp.collections;

import org.organicdesign.fp.function.Fn1;
import org.organicdesign.fp.function.Fn2;
import org.organicdesign.fp.oneOf.Option;
import org.organicdesign.fp.oneOf.Or;
import org.organicdesign.fp.xform.Transformable;
import org.organicdesign.fp.xform.Xform;

import java.util.Iterator;

import static org.organicdesign.fp.FunctionUtils.stringify;

/** An unmodifiable Iterable, without any guarantee about order. */
public interface UnmodIterable extends Iterable, Transformable {
    // ========================================== Static ==========================================

    //    /**
// Caution: this is a convenient optimization for immutable data structures and a nightmare
// waiting to happen to mutable ones.  Don't go slapping this on immutable wrappers for mutable
// data.  If all the underlying data is truly immutable, this allows you to compute the hashCode
// the first time it is needed, then return that same code without re-computing it again.  It's
// the internal version of a memoizer.  Also, use this only for decent sized collections.  If you
// only have 2 or 3 fields, this isn't buying you anything.
//     */
//    static Lazy.Int lazyHashCode(UnmodIterable iter) {
//        if (iter == null) { throw new IllegalArgumentException("Can't have a null iterable."); }
//        return Lazy.Int.of(() -> UnmodIterable.hashCode(iter));
//    }
//
//    /**
//     Caution: this is a convenient optimization for immutable data structures and a nightmare
//     waiting to happen to mutable ones.  Don't go slapping this on immutable wrappers for mutable
//     data structures.  If all the underlying data is truly immutable, this allows you to compute a
//     reasonable toString() the first time it is needed, then return that same String without
//     re-computing it again.  It's the internal version of a memoizer.
//     */
//    static LazyRef lazyToString(String name, UnmodIterable iter) {
//        if (name == null) { throw new IllegalArgumentException("Can't have a null name."); }
//        if (iter == null) { throw new IllegalArgumentException("Can't have a null iterable."); }
//        return LazyRef.of(() -> UnmodIterable.toString(name, iter));
//    }

//    /** Lets underlying compareTo method handle comparing nulls to non-null values. */
//    static > int compareHelper(E e1, E e2) {
//        if (e1 == e2) { return 0; }
//        if (e1 == null) {
//            //noinspection ConstantConditions
//            return -e2.compareTo(e1);
//        }
//        return e1.compareTo(e2);
//    }
//
//    /** A default comparator for UnIterables comparable */
//    static ,E extends UnmodIterable> Comparator
//    iterableComparator() {
//        return new Comparator() {
//            @Override
//            public int compare(E o1, E o2) {
//                if (o1 == null) {
//                    if (o2 == null) {
//                        return 0;
//                    } else {
//                        //noinspection ConstantConditions
//                        return -compare(o2, o1);
//                    }
//                }
//                UnmodIterator as = o1.iterator();
//                UnmodIterator bs = o2.iterator();
//                while (as.hasNext() && bs.hasNext()) {
//                    int ret = compareHelper(as.next(), bs.next());
//                    if (ret != 0) {
//                        return ret;
//                    }
//                }
//                // If we run out of items in one, the longer one is considered greater, just like
//                // ordering words in a dictionary.
//                if (as.hasNext()) { return -1; }
//                if (bs.hasNext()) { return 1; }
//                // All items compare 0 and same number of items - these are sorted the same (and
//                // probably equal)
//                return 0;
//            }
//        };
//    }

    /**
     This is correct, but O(n).  It also works regardless of the order of the items because
     a + b = b + a, even when an overflow occurs.
     */
    static int hash(Iterable is) {
        if (is == null) { throw new IllegalArgumentException("Can't have a null iteratable."); }
//        System.out.println("hashCode for: " + is);
        int ret = 0;
        for (Object t : is) {
            if (t != null) {
//                System.out.println("\tt: " + t + " hashCode: " + t.hashCode());
                ret = ret + t.hashCode();
            }
        }
        return ret;
    }

    /** Computes a reasonable to-string. */
    static String toString(String name, Iterable iterable) {
        if (name == null) { throw new IllegalArgumentException("Can't have a null name."); }
        if (iterable == null) {
            throw new IllegalArgumentException("Can't have a null iteratable.");
        }
        StringBuilder sB = new StringBuilder();
        sB.append(name).append("(");
        int i = 0;
        Iterator iter = iterable.iterator();
        while (iter.hasNext()) {
            if (i > 0) { sB.append(","); }
//            if (i > 4) { break; }
            Object item = iter.next();
            sB.append(stringify(item));
            i++;
        }
//        if (iter.hasNext()) {
//            sB.append("...");
//        }
        return sB.append(")").toString();
    }

    // ================================== Inherited from Iterable ==================================
    /**
     A one-time use, mutable, not-thread-safe way to get each value of the underling collection in
     turn. I experimented with various thread-safe alternatives, but the JVM is optimized around
     iterators so this is the lowest common denominator of collection iteration, even though
     iterators are inherently mutable.
     */
    @Override UnmodIterator iterator();

    // =============================== Inherited from Transformable ===============================

    /** {@inheritDoc} */
    @Override default UnmodIterable concat(Iterable list) {
        return Xform.of(this).concat(list);
    }

    /** {@inheritDoc} */
    @Override default UnmodIterable precat(Iterable list) {
        return Xform.of(this).precat(list);
    }

    /** {@inheritDoc} */
    @Override default UnmodIterable drop(long n) {
        return Xform.of(this).drop(n);
    }

    /** {@inheritDoc} */
    @Override default UnmodIterable dropWhile(Fn1 predicate) {
        return Xform.of(this).dropWhile(predicate);
    }

    /** {@inheritDoc} */
    @Override default  B fold(B ident, Fn2 reducer) {
        return Xform.of(this).fold(ident, reducer);
    }

    /** {@inheritDoc} */
    @Override default  Or foldUntil(G accum,
                                              Fn2 terminator,
                                              Fn2 reducer) {

        return Xform.of(this).foldUntil(accum, terminator, reducer);
    }

    /** {@inheritDoc} */
    @Override default UnmodIterable filter(Fn1 f) {
        return Xform.of(this).filter(f);
    }

    /** {@inheritDoc} */
    @Override default  UnmodIterable flatMap(Fn1> f) {
        return Xform.of(this).flatMap(f);
    }

    /** {@inheritDoc} */
    @Override default  UnmodIterable map(Fn1 f) {
        return Xform.of(this).map(f);
    }

    /** {@inheritDoc} */
    @Override default UnmodIterable take(long numItems) {
        return Xform.of(this).take(numItems);
    }

    /** {@inheritDoc} */
    @Override default UnmodIterable takeWhile(Fn1 f) {
        return Xform.of(this).takeWhile(f);
    }

    /** The first item in this iterable. */
    @Override default Option head() {
        Iterator iter = iterator();
        return iter.hasNext() ? Option.some(iter.next())
                              : Option.none();
    }

//    /**
//     The rest of this sequnce (all the items after its head).  This was originally called rest(),
//     but when I renamed first() to head(), I renamed rest() to tail() so that it wouldn't mix
//     metaphors.
//     */
//    @Deprecated
//    default Transformable tail() {
//        return Xform.of(this).drop(1);
//    }
}