Java tutorial
/* * Copyright (c) 2012, Jonathan Ross <jonross@alum.mit.edu> * * 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 com.github.jonross.seq4j; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.collect.AbstractIterator; import com.google.common.collect.Iterators; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; import com.google.common.collect.UnmodifiableIterator; import static com.github.jonross.seq4j.SeqMisc.*; /** * A fluent iteration API for Google Guava, a <code>Seq</code> extends * {@link UnmodifiableIterator} with chainable behaviors. * <p> * Static methods on this class return sequences that wrap individual values, arrays, * collections, other iterables and iterators. * <p> * NOTE: As of this writing, the API is highly subject to change. * <p> * Seq4J was inspired by Python, Scala, Guava itself, and most of all, by the ongoing * lack of terseness and fluency in Java. * * @author Jonathan Ross * @version 0.1.0 */ public abstract class Seq<T> extends UnmodifiableIterator<T> { // factory methods `========================================================================== /** * Create a {@link Seq} wrapping an immutable iterator for any {@link Iterable}. * A null argument is acceptable and is the same as creating a sequence around an * empty list. */ public static <T> Seq<T> seq(Iterable<T> iterable) { if (iterable != null) { return new IteratorSeq<T>(iterable.iterator()); } else { return new IteratorSeq<T>(Collections.<T>emptyList().iterator()); } } /** * Create a {@link Seq} wrapping an immutable iterator for the specified objects. */ public static <T> Seq<T> seq(T... objects) { return seq(Arrays.asList(objects)); } /** * Create a {@link Seq} wrapping an {@link Iterator}. */ public static <T> Seq<T> seq(Iterator<T> iterator) { return new IteratorSeq<T>(iterator); } // instance methods ========================================================================== /** * Wraps {@link Iterators#all(Iterator, Predicate); returns true if every element in the * sequence satisfies the given predicate. */ boolean all(Predicate<? super T> p) { return Iterators.all(this, p); } /** * Wraps {@link Iterators#any(Iterator, Predicate); returns true if any element in the * sequence satisfies the given predicate. */ boolean any(Predicate<? super T> p) { return Iterators.any(this, p); } /** * Wraps {@link Iterators#concat(Iterator)}; combines this sequence and another iterator * into one sequence. */ Seq<T> concat(Iterator<? extends T> iter) { return seq(Iterators.concat(this, iter)); } /** * Wraps {@link Iterators#skip(Iterator, int)}; skips the first <code>count</code> * items that are available from this iterator. Note this is not a lazy sequence; the * iterator is immediately advanced <code>count</code> items. * * @param count The number of elements to skip. * @return A new <code>Seq</code> for which the limit applies. */ public Seq<T> drop(int count) { Iterators.skip(this, count); return this; } /** * Like {@link #drop(int)}, but returns a sequence that skips leading elements * as long as a predicate returns true. */ public Seq<T> dropWhile(final Predicate<T> p) { return seq(new AbstractIterator<T>() { private boolean done = false; protected T computeNext() { if (!done) { while (Seq.this.hasNext()) { T t = Seq.this.next(); if (!p.apply(t)) { done = true; return t; } } } if (Seq.this.hasNext()) { return Seq.this.next(); } else { return endOfData(); } } }); } /** * Make the sequence usable as the <code>{@link Iterable} in a for loop. NOTE: this * does not return a general-purpose <code>Iterable</code>; the sequence will be consumed * in the loop. (It's for this reason that <code>Seq</code> does not implement * <code>Iterable</code> directly.) */ public Iterable<T> each() { return new Iterable<T>() { public Iterator<T> iterator() { return Seq.this; } }; } /** * Wraps {@link Iterators#filter(Iterator, Predicate)}; returns a new sequence * that provides the elements of the given sequence that satisfy a predicate. */ public Seq<T> filter(final Predicate<? super T> p) { return seq(Iterators.filter(this, p)); } /** * Uses {@link Iterators#find}; returns the first element in the sequence that * satisfies a predicate, as an {@link Optional<T>}, which contains either the * found element or <code>null</code> if not fo und. */ public Optional<T> find(Predicate<? super T> p) { return Optional.fromNullable(Iterators.find(this, p, null)); } /** * Invokes a function on each element of the sequence, ignoring the return value; * returns the number of elements that were present. */ public int foreach(final Function<? super T, ?> f) { int count = 0; while (hasNext()) { f.apply(next()); ++count; } return count; } /** * Wraps {@link Multimaps#index(Iterator, Function); treating each element in the * sequence as a value, applies a function t determine the key and uses the * multimap to group all values with the same key. */ public <K> Multimap<K, T> groupBy(Function<? super T, K> f) { return Multimaps.index(this, f); } /** * Return a string that is the result of concatenating the string representation * of each element in the sequence, using the given separator. Nulls will be * represented by <code>"null"</code>. */ public String join(String separator) { return join(Joiner.on(separator)); } /** * Return the result of applying a {@link Joiner} to the elements in the sequence. */ public String join(Joiner joiner) { return joiner.join(this.each()); } /** * Wraps {@link Iterators#transform(Iterator, Function)}; returns a sequence that * applies a function to each element of this sequence as they are retrieved. */ public <R> Seq<R> map(final Function<? super T, ? extends R> function) { return seq(Iterators.transform(this, function)); } /** * Return a <code>List</code> of two <code>Lists</code> generated by applying * a predicate to each element; the first list contains those for which it * returns true, the second, false. */ public List<List<T>> split(Predicate<? super T> p) { final List<T> a = Lists.newArrayList(), b = Lists.newArrayList(); for (T elt : each()) { (p.apply(elt) ? a : b).add(elt); } List<List<T>> result = Lists.newArrayList(); result.add(a); result.add(b); return result; } /** * Wraps {@link Iterators#limit(Iterator, int)}; limits the number of items * that are available from this iterator. * * @param count The number of elements after which <code>hasNext</code> will return false. * @return A new <code>Seq</code> for which the limit applies. */ public Seq<T> take(int count) { return seq(Iterators.limit(this, count)); } /** * Like {@link #take(int)} but returns a sequence that provides elements as long * as a predicate returns true. */ public Seq<T> takeWhile(final Predicate<T> p) { return seq(new AbstractIterator<T>() { protected T computeNext() { if (!Seq.this.hasNext()) { return endOfData(); } T t = Seq.this.next(); return p.apply(t) ? t : endOfData(); } }); } /** * Wraps {@link Iterators#toArray(Iterator, Class); returns an array of the given * type containing the elements of the sequence. */ public T[] toArray(Class<T> klass) { return Iterators.toArray(this, klass); } /** * Returns a new <code>List</code> containing the remaining values in the sequence. * This is a shortcut for * <pre> * List<T> = Lists.newArrayList(); * theSeq.foreach(addTo(list)); * </pre> */ public List<T> toList() { List<T> list = Lists.newArrayList(); foreach(addTo(list)); return list; } /** * Returns a map whose values are the sequence items and whose keys are the result * of applying <code>keyMaker</code> to each value. * This is a shortcut for * <pre> * Map<K,T> = Maps.newHashMap(); * theSeq.foreach(addTo(map, keyMaker)); * </pre> */ public <K> Map<K, T> toMap(Function<T, K> keyMaker) { Map<K, T> map = Maps.newHashMap(); foreach(addTo(map, keyMaker)); return map; } }