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.Option;
import org.organicdesign.fp.function.Function1;
import org.organicdesign.fp.function.Function2;
import org.organicdesign.fp.xform.Transformable;
import org.organicdesign.fp.xform.Xform;

import java.util.Iterator;

/** 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;
//            }
//        };
//    }

    /**
     Renamed to {@link UnmodIterable#hash(Iterable)} to avoid confusion with instance method
     {@link Object#hashCode()} 2016-09-17
     */
    @Deprecated
    static int hashCode(Iterable is) { return hash(is); }

    /**
     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()) {
            Object item = iter.next();
            if (i > 0) { sB.append(","); }
            if (i > 4) { break; }
            sB.append(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  B foldLeft(B ident, Function2 reducer) {
        return Xform.of(this).foldLeft(ident, reducer);
    }

    /** {@inheritDoc} */
    @Override default  B foldLeft(B ident, Function2 reducer,
                                    Function1 terminateWhen) {
        return Xform.of(this).foldLeft(ident, reducer, terminateWhen);
    }

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

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

    /** {@inheritDoc} */
    @Override default  UnmodIterable map(Function1 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(Function1 f) {
        return Xform.of(this).takeWhile(f);
    }

    // TODO: This is for temporary Sequence backward-compatibility.  Remove once Sequence is deleted.
    /**
     The first item in this sequence.  This was originally called first() but that conflicted with
     SortedSet.first() which did not return an Option and threw an exception when the set was empty.
     */
    default Option head() {
        Iterator iter = iterator();
        return iter.hasNext() ? Option.of(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);
//    }
}