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    *      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.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      /** The current iterator */
46      private Iterator<? extends E> iterator;
47  
48      /**
49       * Constructor that wraps a collection.
50       * <p>
51       * There is no way to reset an Iterator instance without recreating it from
52       * the original source, so the Collection must be passed in.
53       * </p>
54       *
55       * @param collection  the collection to wrap
56       * @throws NullPointerException if the collection is null
57       */
58      public LoopingIterator(final Collection<? extends E> collection) {
59          this.collection = Objects.requireNonNull(collection, "collection");
60          reset();
61      }
62  
63      /**
64       * Has the iterator any more elements.
65       * <p>
66       * Returns false only if the collection originally had zero elements, or
67       * all the elements have been {@link #remove removed}.
68       * </p>
69       *
70       * @return {@code true} if there are more elements
71       */
72      @Override
73      public boolean hasNext() {
74          return !collection.isEmpty();
75      }
76  
77      /**
78       * Returns the next object in the collection.
79       * <p>
80       * If at the end of the collection, return the first element.
81       * </p>
82       *
83       * @return the next object
84       * @throws NoSuchElementException if there are no elements
85       *         at all.  Use {@link #hasNext} to avoid this error.
86       */
87      @Override
88      public E next() {
89          if (collection.isEmpty()) {
90              throw new NoSuchElementException("There are no elements for this iterator to loop on");
91          }
92          if (!iterator.hasNext()) {
93              reset();
94          }
95          return iterator.next();
96      }
97  
98      /**
99       * Removes the previously retrieved item from the underlying collection.
100      * <p>
101      * This feature is only supported if the underlying collection's
102      * {@link Collection#iterator()} method returns an implementation
103      * that supports it.
104      * </p>
105      * <p>
106      * This method can only be called after at least one {@link #next} method call.
107      * After a removal, the remove method may not be called again until another
108      * next has been performed. If the {@link #reset} is called, then remove may
109      * not be called until {@link #next} is called again.
110      * </p>
111      */
112     @Override
113     public void remove() {
114         iterator.remove();
115     }
116 
117     /**
118      * Resets the iterator back to the start of the collection.
119      */
120     @Override
121     public void reset() {
122         iterator = collection.iterator();
123     }
124 
125     /**
126      * Gets the size of the collection underlying the iterator.
127      *
128      * @return the current collection size
129      */
130     public int size() {
131         return collection.size();
132     }
133 
134 }