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