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 19 import java.util.Iterator; 20 import java.util.NoSuchElementException; 21 import java.util.Objects; 22 23 /** 24 * Decorates another iterator to return elements in a specific range. 25 * <p> 26 * The decorated iterator is bounded in the range [offset, offset+max). 27 * The {@code offset} corresponds to the position of the first element to 28 * be returned from the decorated iterator, and {@code max} is the maximum 29 * number of elements to be returned at most. 30 * </p> 31 * <p> 32 * In case an offset parameter other than 0 is provided, the decorated 33 * iterator is immediately advanced to this position, skipping all elements 34 * before that position. 35 * </p> 36 * 37 * @param <E> the type of elements returned by this iterator. 38 * @since 4.1 39 */ 40 public class BoundedIterator<E> implements Iterator<E> { 41 42 /** The iterator being decorated. */ 43 private final Iterator<? extends E> iterator; 44 45 /** The offset to bound the first element return */ 46 private final long offset; 47 48 /** The max number of elements to return */ 49 private final long max; 50 51 /** The position of the current element */ 52 private long pos; 53 54 /** 55 * Decorates the specified iterator to return at most the given number of elements, 56 * skipping all elements until the iterator reaches the position at {@code offset}. 57 * <p> 58 * The iterator is immediately advanced until it reaches the position at {@code offset}, 59 * incurring O(n) time. 60 * </p> 61 * 62 * @param iterator the iterator to be decorated 63 * @param offset the index of the first element of the decorated iterator to return 64 * @param max the maximum number of elements of the decorated iterator to return 65 * @throws NullPointerException if iterator is null 66 * @throws IllegalArgumentException if either offset or max is negative 67 */ 68 public BoundedIterator(final Iterator<? extends E> iterator, final long offset, final long max) { 69 if (offset < 0) { 70 throw new IllegalArgumentException("Offset parameter must not be negative."); 71 } 72 if (max < 0) { 73 throw new IllegalArgumentException("Max parameter must not be negative."); 74 } 75 76 this.iterator = Objects.requireNonNull(iterator, "iterator"); 77 this.offset = offset; 78 this.max = max; 79 pos = 0; 80 init(); 81 } 82 83 /** 84 * Checks whether the iterator is still within its bounded range. 85 * @return {@code true} if the iterator is within its bounds, {@code false} otherwise 86 */ 87 private boolean checkBounds() { 88 if (pos - offset + 1 > max) { 89 return false; 90 } 91 return true; 92 } 93 94 @Override 95 public boolean hasNext() { 96 if (!checkBounds()) { 97 return false; 98 } 99 return iterator.hasNext(); 100 } 101 102 /** 103 * Advances the underlying iterator to the beginning of the bounded range. 104 */ 105 private void init() { 106 while (pos < offset && iterator.hasNext()) { 107 iterator.next(); 108 pos++; 109 } 110 } 111 112 @Override 113 public E next() { 114 if (!checkBounds()) { 115 throw new NoSuchElementException(); 116 } 117 final E next = iterator.next(); 118 pos++; 119 return next; 120 } 121 122 /** 123 * {@inheritDoc} 124 * <p> 125 * In case an offset other than 0 was specified, the underlying iterator will be advanced 126 * to this position upon creation. A call to {@link #remove()} will still result in an 127 * {@link IllegalStateException} if no explicit call to {@link #next()} has been made prior 128 * to calling {@link #remove()}. 129 * </p> 130 */ 131 @Override 132 public void remove() { 133 if (pos <= offset) { 134 throw new IllegalStateException("remove() cannot be called before calling next()"); 135 } 136 iterator.remove(); 137 } 138 }