View Javadoc
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    *      https://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.Collection;
20  import java.util.Iterator;
21  import java.util.NoSuchElementException;
22  import java.util.Objects;
23  
24  import org.apache.commons.collections4.ResettableIterator;
25  
26  /**
27   * An Iterator that restarts when it reaches the end.
28   * <p>
29   * The iterator will loop continuously around the provided elements, unless
30   * there are no elements in the collection to begin with, or all the elements
31   * have been {@link #remove removed}.
32   * </p>
33   * <p>
34   * Concurrent modifications are not directly supported, and for most collection
35   * implementations will throw a ConcurrentModificationException.
36   * </p>
37   *
38   * @param <E> The type of elements returned by this iterator.
39   * @since 3.0
40   */
41  public class LoopingIterator<E> implements ResettableIterator<E> {
42  
43      /** The collection to base the iterator on */
44      private final Collection<? extends E> collection;
45  
46      /** The current iterator */
47      private Iterator<? extends E> iterator;
48  
49      /**
50       * Constructor that wraps a collection.
51       * <p>
52       * There is no way to reset an Iterator instance without recreating it from
53       * the original source, so the Collection must be passed in.
54       * </p>
55       *
56       * @param collection  The collection to wrap
57       * @throws NullPointerException if the collection is null
58       */
59      public LoopingIterator(final Collection<? extends E> collection) {
60          this.collection = Objects.requireNonNull(collection, "collection");
61          reset();
62      }
63  
64      /**
65       * Has the iterator any more elements.
66       * <p>
67       * Returns false only if the collection originally had zero elements, or
68       * all the elements have been {@link #remove removed}.
69       * </p>
70       *
71       * @return {@code true} if there are more elements
72       */
73      @Override
74      public boolean hasNext() {
75          return !collection.isEmpty();
76      }
77  
78      /**
79       * Returns the next object in the collection.
80       * <p>
81       * If at the end of the collection, return the first element.
82       * </p>
83       *
84       * @return The next object
85       * @throws NoSuchElementException if there are no elements
86       *         at all.  Use {@link #hasNext} to avoid this error.
87       */
88      @Override
89      public E next() {
90          if (collection.isEmpty()) {
91              throw new NoSuchElementException("There are no elements for this iterator to loop on");
92          }
93          if (!iterator.hasNext()) {
94              reset();
95          }
96          return iterator.next();
97      }
98  
99      /**
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 }