001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * https://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.collections4.iterators; 018 019import java.util.Collection; 020import java.util.Iterator; 021import java.util.NoSuchElementException; 022import java.util.Objects; 023 024import org.apache.commons.collections4.ResettableIterator; 025 026/** 027 * An Iterator that restarts when it reaches the end. 028 * <p> 029 * The iterator will loop continuously around the provided elements, unless 030 * there are no elements in the collection to begin with, or all the elements 031 * have been {@link #remove removed}. 032 * </p> 033 * <p> 034 * Concurrent modifications are not directly supported, and for most collection 035 * implementations will throw a ConcurrentModificationException. 036 * </p> 037 * 038 * @param <E> The type of elements returned by this iterator. 039 * @since 3.0 040 */ 041public class LoopingIterator<E> implements ResettableIterator<E> { 042 043 /** The collection to base the iterator on */ 044 private final Collection<? extends E> collection; 045 046 /** The current iterator */ 047 private Iterator<? extends E> iterator; 048 049 /** 050 * Constructor that wraps a collection. 051 * <p> 052 * There is no way to reset an Iterator instance without recreating it from 053 * the original source, so the Collection must be passed in. 054 * </p> 055 * 056 * @param collection The collection to wrap 057 * @throws NullPointerException if the collection is null 058 */ 059 public LoopingIterator(final Collection<? extends E> collection) { 060 this.collection = Objects.requireNonNull(collection, "collection"); 061 reset(); 062 } 063 064 /** 065 * Has the iterator any more elements. 066 * <p> 067 * Returns false only if the collection originally had zero elements, or 068 * all the elements have been {@link #remove removed}. 069 * </p> 070 * 071 * @return {@code true} if there are more elements 072 */ 073 @Override 074 public boolean hasNext() { 075 return !collection.isEmpty(); 076 } 077 078 /** 079 * Returns the next object in the collection. 080 * <p> 081 * If at the end of the collection, return the first element. 082 * </p> 083 * 084 * @return The next object 085 * @throws NoSuchElementException if there are no elements 086 * at all. Use {@link #hasNext} to avoid this error. 087 */ 088 @Override 089 public E next() { 090 if (collection.isEmpty()) { 091 throw new NoSuchElementException("There are no elements for this iterator to loop on"); 092 } 093 if (!iterator.hasNext()) { 094 reset(); 095 } 096 return iterator.next(); 097 } 098 099 /** 100 * Removes the previously retrieved item from the underlying collection. 101 * <p> 102 * This feature is only supported if the underlying collection's 103 * {@link Collection#iterator()} method returns an implementation 104 * that supports it. 105 * </p> 106 * <p> 107 * This method can only be called after at least one {@link #next} method call. 108 * After a removal, the remove method may not be called again until another 109 * next has been performed. If the {@link #reset} is called, then remove may 110 * not be called until {@link #next} is called again. 111 * </p> 112 */ 113 @Override 114 public void remove() { 115 iterator.remove(); 116 } 117 118 /** 119 * Resets the iterator back to the start of the collection. 120 */ 121 @Override 122 public void reset() { 123 iterator = collection.iterator(); 124 } 125 126 /** 127 * Gets the size of the collection underlying the iterator. 128 * 129 * @return The current collection size 130 */ 131 public int size() { 132 return collection.size(); 133 } 134 135}