Skip to content

Instantly share code, notes, and snippets.

@danieldietrich
Last active August 29, 2015 14:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save danieldietrich/3a80c0e788ed1524ae64 to your computer and use it in GitHub Desktop.
Save danieldietrich/3a80c0e788ed1524ae64 to your computer and use it in GitHub Desktop.
A coproduct on Iterables providing collection concatenation in O(1)
import java.util.*;
/**
* <p>
* Colist is a view on Iterables, more specific a disjoint union of Iterables. In fact, Colist is an
* algebraic data type, namely a coproduct (see <a
* href="http://en.wikipedia.org/wiki/Coproduct">Wikipedia</a>). Iterables like List, Set and also
* Colist can be concatenated in constant time.
* </p>
* For example, instead of
*
* <pre>
* <code>final List&lt;T&gt; list1 = getList1();
* final List&lt;T&gt; list2 = getList2();
* final List&lt;T&gt; newList = new ArrayList&lt;&gt;(list1);
* newList.addAll(list2);
* for(List&lt;T&gt; list : lists) {
* newList.addAll(list);
* }</code>
* </pre>
*
* we would write
*
* <pre>
* <code>final List&lt;T&gt; newList = new Colist&lt;&gt;(getList1(), getList1())
* .addAll(lists)
* .toList();</code>
* </pre>
*
* <p>
* Because Colist is an Iterable, we may call {@code for(T t : colist)}, given a
* {@code Colist<T> colist}.
* </p>
*/
public final class Colist<T> implements Iterable<T> {
private static final Iterator<Object> EMPTY_ITERATOR = new Iterator<Object>() {
@Override
public boolean hasNext() {
return false;
}
@Override
public Object next() {
throw new NoSuchElementException();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
private final List<Iterable<? extends T>> iterables;
/**
* Constructs a Colist. Adds all elements of all given iterables to this Colist in constant time
* O(iterables.length) = O(1).
*
* @param iterables an array of Iterable, none of them may be null
*/
@SuppressWarnings("unchecked")
public Colist(Iterable<? extends T>... iterables) {
this.iterables = new LinkedList<>();
for (Iterable<? extends T> iterable : iterables) {
this.iterables.add(iterable);
}
}
/**
* Adds all elements of an iterable to this Colist in constant time O(1).
*
* @param iterable An iterable
* @return this Colist instance
*/
public Colist<T> add(Iterable<? extends T> iterable) {
this.iterables.add(iterable);
return this;
}
/**
* Adds all elements of all given iterables to this Colist in constant time O(iterables.length)
* = O(1).
*
* @param iterable An iterable
* @return this Colist instance
*/
@SuppressWarnings("unchecked")
public Colist<T> addAll(Iterable<? extends T>... iterables) {
for (Iterable<? extends T> iterable : iterables) {
this.iterables.add(iterable);
}
return this;
}
/**
* Returns a new List instance containing all elements of this Colist.
*
* @return A new List instance.
*/
public List<T> toList() {
return toCollection(new ArrayList<T>());
}
/**
* Returns a new Set instance containing all elements of this Colist.
*
* @return A new Set instance.
*/
public Set<T> toSet() {
return toCollection(new HashSet<T>());
}
/**
* Returns a new SortedSet instance containing all elements of this Colist in their natural
* order.
*
* @return A new SortedSet instance.
*/
public SortedSet<T> toSortedSet() {
return toCollection(new TreeSet<T>());
}
/**
* Returns a new SortedSet instance containing all elements of this Colist in the order defined
* by the given comparator.
*
* @return A new SortedSet instance.
*/
public SortedSet<T> toSortedSet(Comparator<? super T> comparator) {
return toCollection(new TreeSet<>(comparator));
}
private <C extends Collection<T>> C toCollection(C collection) {
for(T t : this) {
collection.add(t);
}
return collection;
}
@Override
public Iterator<T> iterator() {
return flatten(iterables.iterator());
}
private static <T> Iterator<T> flatten(final Iterator<? extends Iterable<? extends T>> inputs) {
return new Iterator<T>() {
@SuppressWarnings("unchecked")
Iterator<? extends T> current = (Iterator<? extends T>) EMPTY_ITERATOR;
@Override
public boolean hasNext() {
boolean currentHasNext;
while(!(currentHasNext = current.hasNext()) && inputs.hasNext()) {
current = inputs.next().iterator();
}
return currentHasNext;
}
@Override
public T next() {
if(!hasNext()) {
throw new NoSuchElementException();
}
return current.next();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
}
@danieldietrich
Copy link
Author

Thx for the hint @szarnekow !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment