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