BoundedIterator.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.collections4.iterators;

  18. import java.util.Iterator;
  19. import java.util.NoSuchElementException;
  20. import java.util.Objects;

  21. /**
  22.  * Decorates another iterator to return elements in a specific range.
  23.  * <p>
  24.  * The decorated iterator is bounded in the range [offset, offset+max).
  25.  * The {@code offset} corresponds to the position of the first element to
  26.  * be returned from the decorated iterator, and {@code max} is the maximum
  27.  * number of elements to be returned at most.
  28.  * </p>
  29.  * <p>
  30.  * In case an offset parameter other than 0 is provided, the decorated
  31.  * iterator is immediately advanced to this position, skipping all elements
  32.  * before that position.
  33.  * </p>
  34.  *
  35.  * @param <E> the type of elements returned by this iterator.
  36.  * @since 4.1
  37.  */
  38. public class BoundedIterator<E> implements Iterator<E> {

  39.     /** The iterator being decorated. */
  40.     private final Iterator<? extends E> iterator;

  41.     /** The offset to bound the first element return */
  42.     private final long offset;

  43.     /** The max number of elements to return */
  44.     private final long max;

  45.     /** The position of the current element */
  46.     private long pos;

  47.     /**
  48.      * Decorates the specified iterator to return at most the given number of elements,
  49.      * skipping all elements until the iterator reaches the position at {@code offset}.
  50.      * <p>
  51.      * The iterator is immediately advanced until it reaches the position at {@code offset},
  52.      * incurring O(n) time.
  53.      * </p>
  54.      *
  55.      * @param iterator  the iterator to be decorated
  56.      * @param offset  the index of the first element of the decorated iterator to return
  57.      * @param max  the maximum number of elements of the decorated iterator to return
  58.      * @throws NullPointerException if iterator is null
  59.      * @throws IllegalArgumentException if either offset or max is negative
  60.      */
  61.     public BoundedIterator(final Iterator<? extends E> iterator, final long offset, final long max) {
  62.         if (offset < 0) {
  63.             throw new IllegalArgumentException("Offset parameter must not be negative.");
  64.         }
  65.         if (max < 0) {
  66.             throw new IllegalArgumentException("Max parameter must not be negative.");
  67.         }

  68.         this.iterator = Objects.requireNonNull(iterator, "iterator");
  69.         this.offset = offset;
  70.         this.max = max;
  71.         pos = 0;
  72.         init();
  73.     }

  74.     /**
  75.      * Checks whether the iterator is still within its bounded range.
  76.      * @return {@code true} if the iterator is within its bounds, {@code false} otherwise
  77.      */
  78.     private boolean checkBounds() {
  79.         if (pos - offset + 1 > max) {
  80.             return false;
  81.         }
  82.         return true;
  83.     }

  84.     @Override
  85.     public boolean hasNext() {
  86.         if (!checkBounds()) {
  87.             return false;
  88.         }
  89.         return iterator.hasNext();
  90.     }

  91.     /**
  92.      * Advances the underlying iterator to the beginning of the bounded range.
  93.      */
  94.     private void init() {
  95.         while (pos < offset && iterator.hasNext()) {
  96.             iterator.next();
  97.             pos++;
  98.         }
  99.     }

  100.     @Override
  101.     public E next() {
  102.         if (!checkBounds()) {
  103.             throw new NoSuchElementException();
  104.         }
  105.         final E next = iterator.next();
  106.         pos++;
  107.         return next;
  108.     }

  109.     /**
  110.      * {@inheritDoc}
  111.      * <p>
  112.      * In case an offset other than 0 was specified, the underlying iterator will be advanced
  113.      * to this position upon creation. A call to {@link #remove()} will still result in an
  114.      * {@link IllegalStateException} if no explicit call to {@link #next()} has been made prior
  115.      * to calling {@link #remove()}.
  116.      * </p>
  117.      */
  118.     @Override
  119.     public void remove() {
  120.         if (pos <= offset) {
  121.             throw new IllegalStateException("remove() cannot be called before calling next()");
  122.         }
  123.         iterator.remove();
  124.     }
  125. }