Java tutorial
/* * LingPipe v. 3.9 * Copyright (C) 2003-2010 Alias-i * * This program is licensed under the Alias-i Royalty Free License * Version 1 WITHOUT ANY WARRANTY, without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Alias-i * Royalty Free License Version 1 for more details. * * You should have received a copy of the Alias-i Royalty Free License * Version 1 along with this program; if not, visit * http://alias-i.com/lingpipe/licenses/lingpipe-license-1.txt or contact * Alias-i, Inc. at 181 North 11th Street, Suite 401, Brooklyn, NY 11211, * +1 (718) 290-9170. */ //package com.aliasi.util; import java.util.ArrayList; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.List; /** * Static utility methods, classes, and abstract classes for * iteration. * * @author Bob Carpenter * @author Mike Ross * @version 4.0.0 * @since LingPipe2.0 */ public class Iterators { // do not allow instances private Iterators() { /* no instances */ } static final Iterator<Object> EMPTY_ITERATOR = new Empty<Object>(); /** * Returns an iterator that has no elements. Thus {@code * hasNext()} will always return {@code false} and {@code next()} * will always throw a {@code NoSuchElementException}. * * @return The empty iterator. */ public static <E> Iterator<E> empty() { @SuppressWarnings("unchecked") Iterator<E> emptyIterator = (Iterator<E>) EMPTY_ITERATOR; return emptyIterator; } /** * Returns an iterator over the single specified element. * Thus {@code next()} will return the specified element * on the first call, with subsequent calls * throwing a {@code NoSuchElementException} . * * @param e The object to return in the iterator. * @return The iterator over the specified object. */ public static <E> Iterator<E> singleton(E e) { Iterator<E> singletonIterator = new Singleton<E>(e); return singletonIterator; } /** * Returns a newly constructed iterator over the specified pair of * elements. * * <p><i>Implementation Note:</i> References to the elemenets are * removed from iterator after they are returned through {@code * next()}. * * @param e1 First element returned. * @param e2 Second element returned. * @return The iterator over the pair of elements specified. */ public static <E> Iterator<E> pair(E e1, E e2) { return new Pair<E>(e1, e2); } /** * Returns an iterator constructed from the specified variable * length argument list of objects. * * <p>Remove is supported, but has no effect on anything as the * list from which elements are being deleted is only used for the * iterator. * * @param es Array of objects for iteration. * @return Iterator over list. */ public static <E> Iterator<E> list(E... es) { return java.util.Arrays.<E>asList(es).iterator(); } /** * Returns an unmodifiable view of the specified iterator. * Calls to {@code next()} and {@code hasNext()} are delegated * to the specified iterator as made. Calls to {@code remove()} * throw unsupported operation exceptions. * * @param it Iterator to view. * @return Unmodifiable view of specified iterator. */ public static <E> Iterator<E> unmodifiable(Iterator<E> it) { final Iterator<E> mIt = it; return new Iterator<E>() { public boolean hasNext() { return mIt.hasNext(); } public E next() { return mIt.next(); } public void remove() { String msg = "Cannot remove from an unmodifiable iterator."; throw new UnsupportedOperationException(msg); } }; } /** * Returns an iterator that runs through the first iterator's * elements and then runs through the second iterator's elements. * * <p>References to the iterators are freed after it is finished * iterating. * * @param it1 The first iterator in the sequence. * @param it2 The second iterator in the sequence. * @return The iterator over the two iterators. */ public static <E> Iterator<E> sequence(Iterator<? extends E> it1, Iterator<? extends E> it2) { Iterator<E> result = new Sequence<E>(it1, it2); return result; } public static <E> Iterator<E> sequence(List<? extends Iterator<? extends E>> iterators) { @SuppressWarnings({ "unchecked", "rawtypes" }) Iterator<? extends E>[] elts = (Iterator<? extends E>[]) iterators.toArray(new Iterator[0]); Iterator<E> it = new Sequence<E>(elts); return it; } public static <E> Iterator<E> sequence(Iterator<? extends Iterator<? extends E>> iteratorOfIterators) { Iterator<E> it = new Sequence<E>(iteratorOfIterators); return it; } /** * Returns an iterator over the specified array of objects. * * <p><i>Warning:</i> The array is not copied, so any changes in * the underlying array are reflected in the iterator. * * <P><I>Implementation Note:</I> this class does not * automatically free references in the underlying array, because * the array may be used elswhere. If reference freeing is * critical here, a call to {@code remove()} after ever call to * {@code next()} will free the references in the array. * * @param members Array of elements over which to iterate. * @return The iterator over the array elements. */ public static <E> Iterator<E> array(E[] members) { return new Array<E>(members); } /** * Returns a newly constructed iterator over the specified * object array slice. * * <p>The {@code remove()} method is not supported. * * <p><i>Warning:</i> The array is not copied, so any changes in * the underlying array are reflected in the iterator. * * <P><I>Implementation Note:</I> this class does not * automatically free references in the underlying array, because * the array may be used elswhere. * * @param members Array of objects over which to iterate. * @param start Index of first object to return. * @param length Number of objects to iterate. * @throws Illegal argument exception if {@code start < 0} or * {@code start + length > objects.length()}. */ public static <E> Iterator<E> arraySlice(E[] members, int start, int length) { return new ArraySlice<E>(members, start, length); } /** * An <code>Iterator.Array</code> iterates over the elements of an * array specified at construction time. * * <P><I>Implementation Note:</I> this class does not automatically * free references in the underlying array, because the array * may be used elswhere. If reference freeing is critical here, * a call to <code>remove()</code> after ever <code>next()</code> * will free the references in the array. * * @author Bob Carpenter * @version 3.8 * @since LingPipe1.0 * @param <E> the type of objects returned by the iterator */ static class Array<E> implements Iterator<E> { /** * Array to iterate over. */ private final E[] mMembers; /** * Current position of next element to return in array. */ private int mPosition; /** * Construct an array iterator from the specified array. * * @param members Array basis of the constructed iterator. */ public Array(E[] members) { mMembers = members; } /** * Returns <code>true</code> if this iterator has more * elements. * * @return <code>true</code> if this iterator has more * elements. */ public boolean hasNext() { return mPosition < mMembers.length; } /** * Returns the next element in the array. * * @return Next element in the array. * @throws NoSuchElementException If there are no more * elements left in the array to return. */ public E next() { if (!hasNext()) throw new NoSuchElementException(); return mMembers[mPosition++]; } /** * Sets position in underlying array corresponding to the most * recently returned token to <code>null</code>. * * @throws IllegalStateException If the <code>next</code> * method has not been called, or the <code>remove</code> * method has already been called after the last call to the * next method. */ public void remove() { if (mPosition < 1) throw new IllegalStateException("Next not yet called."); if (mMembers[mPosition - 1] == null) throw new IllegalStateException("Remove already called."); mMembers[mPosition - 1] = null; } } /** * An <code>Iterators.ArraySlice</code> iterates over a slice of * an array specified by start index and length. * * @author Bob Carpenter * @version 3.8 * @since LingPipe1.0 * @param <E> the type of objects returned by the iterator */ static class ArraySlice<E> implements Iterator<E> { /** * The underlying objects represented in this array iterator. */ private final E[] mObjects; /** * Index of next element to return. */ private int mNext; /** * Index one past the index of the last element to return. */ private final int mLast; /** * Construct an iterator over the specified array of objects * that begins at the object at the specified start index * and carries through to the other objects. * * @param objects Array of objects over which to iterate. * @param start Index of first object to return. * @param length Number of objects to iterate. * @throws Illegal argument exception if {@code start < 0} or * {@code start + length > objects.length}. */ public ArraySlice(E[] objects, int start, int length) { if (start < 0) { String msg = "Require start of slice to be non-negative." + " Found start=" + start; throw new IllegalArgumentException(msg); } if (start + length > objects.length) { String msg = "Start plus length must not exceed array length." + " Found objects.length=" + objects.length + " start=" + start + " length=" + length + " (start+length)=" + (start + length); throw new IllegalArgumentException(msg); } mObjects = objects; mNext = start; mLast = start + length; } /** * Returns <code>true</code> if there are more objects * to return. * * @return <code>true</code> if this iterator has more * elements. */ public boolean hasNext() { return mNext < mLast; } /** * Returns the next object for this iterator, throwing * an exception if there are no more objects left. * * @return Next object from this iterator. * @throws NoSuchElementException If there are no more * elements to return. */ public E next() { if (!hasNext()) throw new NoSuchElementException(); return mObjects[mNext++]; } /** * Throws an <code>UnsupportedOperationException</code>. * * @throws UnsupportedOperationException If called. */ public void remove() { throw new UnsupportedOperationException(); } } /** * An <code>Iterator.Empty</code> is an iterator with no * objects. The method {@link #hasNext()} always returns * <code>false</code>, while the methods {@link #next()} * and {@link #remove()} always throw exceptions. * * @author Bob Carpenter * @version 3.8 * @since LingPipe3.0 * @param <E> the type of objects returned by the iterator */ static class Empty<E> implements Iterator<E> { /** * Construct an empty iterator. */ public Empty() { /* do nothing */ } /** * Always returns <code>false</code>. * * @return <code>false</code>. */ public boolean hasNext() { return false; } /** * Calling this method throws a no-such-element exception. * * @return Always throws an exception. * @throws NoSuchElementException Always. */ public E next() { String msg = "No elements in empty iterator."; throw new NoSuchElementException(msg); } /** * Calling this method throws an illegal state exception. * * @throws IllegalStateException Always. */ public void remove() { String msg = "No elements to remove in empty iterator."; throw new IllegalStateException(msg); } } /** * An <code>Iterator.Singleton</code> is an iterator over a * singleton object. * * @author Bob Carpenter * @version 3.8 * @since LingPipe2.0 * @param <E> the type of objects returned by the iterator */ static class Singleton<E> implements Iterator<E> { /** * The single object to return. */ private E mMember; /** * <code>true</code> if the single element has not already * been returned. */ private boolean mHasNext = true; /** * Construct a singleton iterator that returns the specified * object. * * @param member Single member over which to iterate. */ public Singleton(E member) { mMember = member; } /** * Returns <code>true</code> if the single member has not * already been returned. * * @return <code>true</code> if the single member has not * already been returned. */ public boolean hasNext() { return mHasNext; } /** * Returns the singleton member if it has not yet been * returned, otherwise throw an exception. * * @return Singleton member if it has not yet been returned. * @throws NoSuchElementException If the singleton member has * already been returned and no elements remain. */ public E next() { if (!mHasNext) throw new NoSuchElementException(); mHasNext = false; E result = mMember; mMember = null; return result; } /** * Throws an unsupported operation exception. * * @throws UnsupportedOperationException Whenver called. */ public void remove() { String msg = "This iterator does not support remove()." + " class=" + this.getClass(); throw new UnsupportedOperationException(msg); } } /** * An <code>Iterators.Pair</code> provides an iterator * over a sequence of exactly two elements. * * @author Bob Carpenter * @version 3.0 * @since LingPipe2.0 * @param <E> the type of objects returned by the iterator */ static class Pair<E> implements Iterator<E> { /** * Number of members returned so far. */ private int mMembersReturned = 0; private E mMember1; private E mMember2; /** * Construct a pair iterator based on the containing * pair set. * * @param member1 First member. * @param member2 Second member. */ public Pair(E member1, E member2) { mMember1 = member1; mMember2 = member2; } /** * Returns <code>true</code> if there are more elements * left to return. * * @return <code>true</code> if there are more elements * left to return. */ public boolean hasNext() { return mMembersReturned < 2; } /** * Returns the next object in this pair iterator, * or throws an exception if there aren't any more. * * @return Next object in this pair iterator. */ public E next() { if (mMembersReturned == 0) { ++mMembersReturned; E result1 = mMember1; mMember1 = null; return result1; } if (mMembersReturned == 1) { ++mMembersReturned; E result2 = mMember2; mMember2 = null; return result2; } throw new NoSuchElementException(); } /** * Throws an unsupported operation exception. * * @throws UnsupportedOperationException Whenver called. */ public void remove() { String msg = "This iterator does not support remove()." + " class=" + this.getClass(); throw new UnsupportedOperationException(msg); } } /** * An <code>Iterators.Filter</code> filters the stream of objects * returned by another iterator by subjecting them to an acceptance * test. Instances are constructed with a specified iterator to be * filtered; this filter is saved in this class and should not be * accessed by other methods. Concrete subclasses must implement * {@link #accept(Object)}, which determines which of the elements * produced by the containediterator are returned. * * @author Bob Carpenter * @version 3.0 * @since LingPipe1.0 * @param <E> the type of objects returned by the iterator */ public static abstract class Filter<E> implements Iterator<E> { private final Iterator<? extends E> mIterator; private boolean mFoundNext = false; private E mNext; /** * Construct a filtered iterator from the specified iterator. * * @param iterator Contained iterator to be filtered. */ public Filter(Iterator<? extends E> iterator) { mIterator = iterator; } /** * Returns <code>true</code> for objects returned by the contained * iterator that should be returned by this iterator. * * @param x Object to test. * @return <code>true</code> if this object should be returned by * this iterator. */ abstract public boolean accept(E x); /** * Returns <code>true</code> if calls to <code>next()</code> will * return a value. * * <P><i>Implementation note:</i> This iterator stores the next * element in a local variable after calls to this method. Calls to * {@link #next()} remove this reference. * * @return <code>true</code> if calls to <code>next()</code> will * return a value. */ public boolean hasNext() { if (mFoundNext) return true; while (mIterator.hasNext()) { E y = mIterator.next(); if (accept(y)) { mFoundNext = true; mNext = y; return true; } } return false; } /** * Returns the next value from the contained iterator that is accepted. * Acceptance is determined by the method {@link #accept(Object)}. * * @return Next object from the underlying iterator that passes * the acceptance test. * @throws NoSuchElementException If there are no more elements in * the underlying iterator that pass the acceptance test. */ public E next() { if (!hasNext()) throw new NoSuchElementException(); mFoundNext = false; E result = mNext; mNext = null; return result; } /** * This operation is not supported. * * <P><i>Implementation Note:</i> Because calls to {@link * #hasNext()} must iterate over the contained iterator to find * the next acceptable object to return, it is not guaranteed that * the last element returned by this iterator is the same as the * last element returned by the underlying iterator. Thus the * underlying iterator can't be used to do the removal without * tripping the fail-fast behavior of {@link Iterator}. * * @throws UnsupportedOperationException Always. */ public void remove() { String msg = "Cannot remove from a filtered iterator."; throw new UnsupportedOperationException(msg); } } /** * An <code>Iterator.Modifier</code> uses a single abstract method * to operate on the elements returned by an underlying iterator * to return modified objects. The {@link #remove()} and {@link * #hasNext()} methods are simply passed to the underlying iterator. * This implements the filter pattern, which is known as a map in * functional programming. * * @author Bob Carpenter * @version 3.0 * @since LingPipe2.1 * @param <E> the type of objects returned by the iterator */ public static abstract class Modifier<E> implements Iterator<E> { private final Iterator<? extends E> mIt; /** * Construct a modifier from an underlying iterator. * * @param it Underlying iterator. */ public Modifier(Iterator<? extends E> it) { mIt = it; } /** * Remove the next element by delegating the call to the the * underlying iterator. */ public void remove() { mIt.remove(); } /** * Returns <code>true</code> if the underlying iterator has * more elements. This method is simply delegated to the * underlying iterator. * * @return <code>true</code> if this iterator has more * elements. */ public boolean hasNext() { return mIt.hasNext(); } /** * Returns the next element for this iterator, which is * the object returned by the underlying iterator after * undergoing modification by {@link #modify(Object)}. * * @return The modified next element from the underlying * iterator. */ public E next() { return modify(mIt.next()); } /** * This abstract method is applied to objects returned by the * underlying iterator to create the object returned by this * iterator. This method is called once for each call to * {@link #next()}. * * @param next Object returned by underlying iterator. * @return Object returned by this iterator. */ public abstract E modify(E next); } /** * An <code>Iterators.Buffered</code> uses a single method to return * objects, buffering the result and returning it as the next element * if it is non-<code>null</code>. This class does not support * <code>null</code> return values for {@link #next()}. The {@link * #remove()} operation is unsupported, but may be overridden. * * @author Bob Carpenter * @version 3.0 * @since LingPipe1.0 * @param <E> the type of objects returned by the iterator */ public static abstract class Buffered<E> implements Iterator<E> { private E mNext; /** * Construct a buffered iterator. This constructor does not * do anything. */ protected Buffered() { // do nothing } /** * Returns the next object for this iterator, or <code>null</code> * if there are no more objects. * * @return Next object for this iterator. */ protected abstract E bufferNext(); /** * Returns <code>true</code> if the next call to {@link #next()} * will return a non-<code>null</code> value. * * @return <code>true</code> if the next call to {@link #next()} * will return a non-<code>null</code> value. */ public boolean hasNext() { return mNext != null || (mNext = bufferNext()) != null; } /** * Returns the next object for this iterator. * * @return The next object for this iterator. * @throws NoSuchElementException If there are no more elements. */ public E next() { if (!hasNext()) throw new NoSuchElementException(); E result = mNext; mNext = null; return result; } /** * Throws an unsupported operation exception unless overridden by * a subclass. * * @throws UnsupportedOperationException Always. */ public void remove() { throw new UnsupportedOperationException(); } } /** * An <code>Iterators.Sequence</code> iterates over the elements of an * ordered sequence of iterators in turn. Each iterator is exhausted * before moving to the next. These iterators may be supplied as a * pair of iterators, as an array of iterators, or as an iterator of * iterators. The sequence iterator delegates calls to {@link * #next()} and {@link #remove()} to the relevant iterator in the * underlying sequence of iterators. For <code>next()</code>, this is * the current underlying iterator. For <code>remove()</code>, it's * the last iterator whose <code>next()</code> element was called, and * it throws an illegal state exception if there isn't one. * * <P><I>Implementation Note:</I> Because of the requirements of * {@link Iterator#remove()}, a reference to the last iterator is * kept, as well as to the current iterator. Otherwise, the sequence * iterator will release resources as soon as possible. If the * supplied sequence is an array, the elements will not be * automatically returned from that array; it is simply wrapped in an * instance of {@link Iterators.Array}. * * @author Bob Carpenter * @version 3.8 * @since LingPipe1.0 * @param <E> the type of objects returned by the iterator */ static class Sequence<E> implements Iterator<E> { private final Iterator<? extends Iterator<? extends E>> mIterators; private Iterator<? extends E> mCurrentIterator; private Iterator<? extends E> mLastIteratorNextCalled; /** * Construct a sequenc iterator that calls the pair of * iterators specified in turn. * * @param iterator1 First iterator. * @param iterator2 Second iterator. */ public Sequence(Iterator<? extends E> iterator1, Iterator<? extends E> iterator2) { this(toIteratorIterator(iterator1, iterator2)); } /** * Construct a sequence iterator that calls the iterators in the * specified array in the order they are given in the array. * * @param iterators Sequence of iterators. */ public Sequence(Iterator<? extends E>[] iterators) { this(new Iterators.Array<Iterator<? extends E>>(iterators)); } /** * Construct a sequence iterator that calls the iterators returned * by the iterator of iterators specified. If one of the elements * is not an iterator, <code>hasNext()</code> and * <code>next()</code> will throw a * <code>ClassCastException</code>. * * @param iteratorOfIterators Iterator of iterators. */ public Sequence(Iterator<? extends Iterator<? extends E>> iteratorOfIterators) { mIterators = iteratorOfIterators; } /** * Returns <code>true</code> if this iterator has another element. * This sequence of iterators has another element if it has * another iterator that has another element. * * @return <code>true</code> if this sequence of iterators * has another iterator with another element. * @throws ClassCastException If an object is returned by * the iterator of iterators specified at construction time * that is not an iterator. */ public boolean hasNext() { if (mCurrentIterator == null) nextIterator(); // get started for (; mCurrentIterator != null; nextIterator()) if (mCurrentIterator.hasNext()) return true; return false; } /** * Return the next element returned by the next iterator in the * iterator sequence. * * @return The next object in the iteration. * @throws ClassCastException If an object is returned by * the iterator of iterators specified at construction time * that is not an iterator. */ public E next() { if (!hasNext()) throw new NoSuchElementException(); mLastIteratorNextCalled = mCurrentIterator; return mCurrentIterator.next(); } /** * Removes the last element returned by this iterator from the * collection underlying the iterator from which it was returned. * The method can only be called once per call to * <code>next()</code>. * @throws IllegalStateException If <code>next()</code> has not * yet been called, or <code>remove()</code> method has * been called after the last call to <code>next()</code>. */ public void remove() { if (mLastIteratorNextCalled == null) throw new IllegalStateException("next() not yet called."); mLastIteratorNextCalled.remove(); mLastIteratorNextCalled = null; } private void nextIterator() { // possible cast exception mCurrentIterator = mIterators.hasNext() ? mIterators.next() : null; } static <E> Iterator<? extends Iterator<? extends E>> toIteratorIterator(Iterator<? extends E> it1, Iterator<? extends E> it2) { ArrayList<Iterator<? extends E>> list = new ArrayList<Iterator<? extends E>>(2); list.add(it1); list.add(it2); return list.iterator(); } } /** * A {@code Iterators.PrimitiveInt} is an integer iterator that * also allows objects to be accessed as primitive {@code int} * values. * * <p>The method {@link #next()} returns an {@code Integer}, * whereas {@link #nextPrimitive()} returns a primitive {@code * int} value. Using either one will advanced the iterator to the * next element. * * <p><i>Implementation Note:</i> Using the standard iterator * implementation as {@code Integer} requires boxing of the * primitive values as objects. Therefore, use the method * {@code nextPrimitive()} wherever possible. * * @author Mike Ross * @author Bob Carpenter * @version 3.8 * @since Lingpipe3.8 */ public static abstract class PrimitiveInt implements Iterator<Integer> { /** * Returns the next primitive integer. * * @return The next primitive integer. */ abstract public int nextPrimitive(); /** * Returns {@code true} if there is another integer in the * iteration. * * @return {@code true} if there is another integer in the * iteration. */ abstract public boolean hasNext(); /** * Throws unsupported operation exception. * * @throws UnsupportedOperationException Always. */ public void remove() { String msg = "Iterators.PrimitiveInt does not support remove."; throw new UnsupportedOperationException(msg); } /** * Returns the next integer result. * * <p><i>Implementation Note:</i> Requires boxing the * primitive {@code int} returned by {@link #nextPrimitive()}. * * @return The next integer. */ public Integer next() { return nextPrimitive(); } } /** * Construct an Integer iterator from 0 (inclusive) the specified * end point (exclusive). The returned iterator does not support * {@code remove()}. * * @param end One plus the last integer returned. */ public static PrimitiveInt intRange(int end) { return new IntRange(0, end); } /** * Returns a newly constructed primitive integer iterator that * iterates from the start (inclusive) to end (exclusive). * * <p>The returned iterator does not support {@code remove()}. * * @param start The first and lowest value to return. * @param end One plus the last integer returned. * @throws IllegalArgumentException If {@code start > end}. */ public static PrimitiveInt intRange(int start, int end) { return new IntRange(start, end); } /** * Returns an iterator over the array of primitive integers * specified. * * <p>There are no order restrictions on the array -- its elements * may be in any order and may contain duplicates. * * <p>The returned iterator does not support {@code remove()}. * * @param members Array of members to iterate. */ public static PrimitiveInt intArray(int[] members) { return new IntArray(members); } /** * See {@link #intRange(int,int)} for usage. * * @author Mike Ross * @version 3.8 * @since Lingpipe3.8 */ static class IntRange extends PrimitiveInt { private int mCur; private final int mEnd; public IntRange(int start, int end) { if (end < start) { String msg = "End point must be >= start point." + " Found start=" + start + " end=" + end; throw new IllegalArgumentException(msg); } mCur = start; mEnd = end; } public boolean hasNext() { return mCur < mEnd; } public int nextPrimitive() { if (!hasNext()) throw new NoSuchElementException(); return mCur++; } } /** * See {@link #intArray(int[])} for usage. * * @author Mike Ross * @version 3.8 * @since Lingpipe3.8 */ static class IntArray extends PrimitiveInt { private final int[] mMembers; private int mPosition; public IntArray(int[] members) { mMembers = members; } public boolean hasNext() { return mPosition < mMembers.length; } public int nextPrimitive() { if (!hasNext()) throw new NoSuchElementException(); return mMembers[mPosition++]; } } }