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;
18  
19  import java.util.Arrays;
20  import java.util.Collection;
21  import java.util.Comparator;
22  import java.util.Enumeration;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.Objects;
26  
27  import org.apache.commons.collections4.iterators.SingletonIterator;
28  
29  /**
30   * A FluentIterable provides a powerful yet simple API for manipulating
31   * Iterable instances in a fluent manner.
32   * <p>
33   * A FluentIterable can be created either from an Iterable or from a set
34   * of elements. The following types of methods are provided:
35   * </p>
36   * <ul>
37   *   <li>fluent methods which return a new {@code FluentIterable} instance,
38   *       providing a view of the original iterable (e.g. filter(Predicate));
39   *   <li>conversion methods which copy the FluentIterable's contents into a
40   *       new collection or array (e.g. toList());
41   *   <li>utility methods which answer questions about the FluentIterable's
42   *       contents (e.g. size(), anyMatch(Predicate)).
43   *   <li>
44   * </ul>
45   * <p>
46   * The following example outputs the first 3 even numbers in the range [1, 10]
47   * into a list:
48   * </p>
49   * <pre>
50   * List&lt;String&gt; result =
51   *   FluentIterable
52   *       .of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
53   *       .filter(new Predicate&lt;Integer&gt;() {
54   *                   public boolean evaluate(Integer number) {
55   *                        return number % 2 == 0;
56   *                   }
57   *              )
58   *       .transform(TransformerUtils.stringValueTransformer())
59   *       .limit(3)
60   *       .toList();
61   * </pre>
62   * The resulting list will contain the following elements:
63   * <pre>[2, 4, 6]</pre>
64   *
65   * @param <E>  the element type
66   * @since 4.1
67   */
68  public class FluentIterable<E> implements Iterable<E> {
69  
70      /**
71       * Creates a new empty FluentIterable.
72       *
73       * @param <T>  the element type
74       * @return a new empty FluentIterable
75       */
76      public static <T> FluentIterable<T> empty() {
77          return IterableUtils.EMPTY_ITERABLE;
78      }
79  
80      /**
81       * Constructs a new FluentIterable from the provided iterable. If the
82       * iterable is already an instance of FluentIterable, the instance
83       * will be returned instead.
84       * <p>
85       * The returned iterable's iterator supports {@code remove()} when the
86       * corresponding input iterator supports it.
87       *
88       * @param <T>  the element type
89       * @param iterable  the iterable to wrap into a FluentIterable, may not be null
90       * @return a new FluentIterable wrapping the provided iterable
91       * @throws NullPointerException if iterable is null
92       */
93      public static <T> FluentIterable<T> of(final Iterable<T> iterable) {
94          IterableUtils.checkNotNull(iterable);
95          if (iterable instanceof FluentIterable<?>) {
96              return (FluentIterable<T>) iterable;
97          }
98          return new FluentIterable<>(iterable);
99      }
100 
101     /**
102      * Creates a new FluentIterable of the single provided element.
103      * <p>
104      * The returned iterable's iterator does not support {@code remove()}.
105      *
106      * @param <T>  the element type
107      * @param singleton  the singleton element
108      * @return a new FluentIterable containing the singleton
109      */
110     public static <T> FluentIterable<T> of(final T singleton) {
111         return of(IteratorUtils.asIterable(new SingletonIterator<>(singleton, false)));
112     }
113 
114     /**
115      * Creates a new FluentIterable from the provided elements.
116      * <p>
117      * The returned iterable's iterator does not support {@code remove()}.
118      *
119      * @param <T>  the element type
120      * @param elements  the elements to be contained in the FluentIterable
121      * @return a new FluentIterable containing the provided elements
122      */
123     public static <T> FluentIterable<T> of(final T... elements) {
124         return of(Arrays.asList(elements));
125     }
126 
127     /** A reference to the wrapped iterable. */
128     private final Iterable<E> iterable;
129 
130     /**
131      * Don't allow instances.
132      */
133     FluentIterable() {
134         this.iterable = this;
135     }
136 
137     /**
138      * Create a new FluentIterable by wrapping the provided iterable.
139      * @param iterable  the iterable to wrap
140      */
141     private FluentIterable(final Iterable<E> iterable) {
142         this.iterable = iterable;
143     }
144 
145     /**
146      * Checks if all elements contained in this iterable are matching the
147      * provided predicate.
148      * <p>
149      * A {@code null} or empty iterable returns true.
150      *
151      * @param predicate  the predicate to use, may not be null
152      * @return true if all elements contained in this iterable match the predicate,
153      *   false otherwise
154      * @throws NullPointerException if predicate is null
155      */
156     public boolean allMatch(final Predicate<? super E> predicate) {
157         return IterableUtils.matchesAll(iterable, predicate);
158     }
159 
160     /**
161      * Checks if this iterable contains any element matching the provided predicate.
162      * <p>
163      * A {@code null} or empty iterable returns false.
164      *
165      * @param predicate  the predicate to use, may not be null
166      * @return true if at least one element contained in this iterable matches the predicate,
167      *   false otherwise
168      * @throws NullPointerException if predicate is null
169      */
170     public boolean anyMatch(final Predicate<? super E> predicate) {
171         return IterableUtils.matchesAny(iterable, predicate);
172     }
173 
174     /**
175      * Returns a new FluentIterable whose iterator will first traverse
176      * the elements of the current iterable, followed by the provided
177      * elements.
178      *
179      * @param elements  the elements to append to the iterable
180      * @return a new iterable, combining this iterable with the elements
181      */
182     public FluentIterable<E> append(final E... elements) {
183         return append(Arrays.asList(elements));
184     }
185 
186     /**
187      * Returns a new FluentIterable whose iterator will first traverse
188      * the elements of the current iterable, followed by the elements
189      * of the provided iterable.
190      *
191      * @param other  the other iterable to combine, may not be null
192      * @return a new iterable, combining this iterable with other
193      * @throws NullPointerException if other is null
194      */
195     public FluentIterable<E> append(final Iterable<? extends E> other) {
196         return of(IterableUtils.chainedIterable(iterable, other));
197     }
198 
199     /**
200      * Returns an Enumeration that will enumerate all elements contained
201      * in this iterable.
202      *
203      * @return an Enumeration over the elements of this iterable
204      */
205     public Enumeration<E> asEnumeration() {
206         return IteratorUtils.asEnumeration(iterator());
207     }
208 
209     /**
210      * Returns a new FluentIterable whose iterator will traverse the
211      * elements of the current and provided iterable in natural order.
212      * <p>
213      * Example: natural ordering
214      * <ul>
215      *   <li>this contains elements [1, 3, 5, 7]
216      *   <li>other contains elements [2, 4, 6, 8]
217      * </ul>
218      * <p>
219      * The returned iterable will traverse the elements in the following
220      * order: [1, 2, 3, 4, 5, 6, 7, 8]
221      *
222      * @param other  the other iterable to collate, may not be null
223      * @return a new iterable, collating this iterable with the other in natural order
224      * @throws NullPointerException if other is null
225      * @see org.apache.commons.collections4.iterators.CollatingIterator
226      */
227     public FluentIterable<E> collate(final Iterable<? extends E> other) {
228         return of(IterableUtils.collatedIterable(iterable, other));
229     }
230 
231     /**
232      * Returns a new FluentIterable whose iterator will traverse the
233      * elements of the current and provided iterable according to the
234      * ordering defined by a comparator.
235      * <p>
236      * Example: descending order
237      * <ul>
238      *   <li>this contains elements [7, 5, 3, 1]
239      *   <li>other contains elements [8, 6, 4, 2]
240      * </ul>
241      * <p>
242      * The returned iterable will traverse the elements in the following
243      * order: [8, 7, 6, 5, 4, 3, 2, 1]
244      *
245      * @param comparator  the comparator to define an ordering, may be null,
246      *   in which case natural ordering will be used
247      * @param other  the other iterable to collate, may not be null
248      * @return a new iterable, collating this iterable with the other in natural order
249      * @throws NullPointerException if other is null
250      * @see org.apache.commons.collections4.iterators.CollatingIterator
251      */
252     public FluentIterable<E> collate(final Iterable<? extends E> other,
253                                      final Comparator<? super E> comparator) {
254         return of(IterableUtils.collatedIterable(comparator, iterable, other));
255     }
256 
257     /**
258      * Checks if the object is contained in this iterable.
259      *
260      * @param object  the object to check
261      * @return true if the object is contained in this iterable, false otherwise
262      */
263     public boolean contains(final Object object) {
264         return IterableUtils.contains(iterable, object);
265     }
266 
267     /**
268      * Traverses an iterator of this iterable and adds all elements
269      * to the provided collection.
270      *
271      * @param collection  the collection to add the elements
272      * @throws NullPointerException if collection is null
273      */
274     public void copyInto(final Collection<? super E> collection) {
275         Objects.requireNonNull(collection, "collection");
276         CollectionUtils.addAll(collection, iterable);
277     }
278 
279     /**
280      * This method fully traverses an iterator of this iterable and returns
281      * a new iterable with the same contents, but without any reference
282      * to the originating iterables and/or iterators.
283      * <p>
284      * Calling this method is equivalent to:
285      * <pre>
286      *   FluentIterable&lt;E&gt; someIterable = ...;
287      *   FluentIterable.of(someIterable.toList());
288      * </pre>
289      *
290      * @return a new iterable with the same contents as this iterable
291      */
292     public FluentIterable<E> eval() {
293         return of(toList());
294     }
295 
296     /**
297      * Returns a new FluentIterable whose iterator will only return
298      * elements from this iterable matching the provided predicate.
299      *
300      * @param predicate  the predicate used to filter elements
301      * @return a new iterable, providing a filtered view of this iterable
302      * @throws NullPointerException if predicate is null
303      */
304     public FluentIterable<E> filter(final Predicate<? super E> predicate) {
305         return of(IterableUtils.filteredIterable(iterable, predicate));
306     }
307 
308     /**
309      * Applies the closure to all elements contained in this iterable.
310      *
311      * @param closure  the closure to apply to each element, may not be null
312      * @throws NullPointerException if closure is null
313      */
314     public void forEach(final Closure<? super E> closure) {
315         IterableUtils.forEach(iterable, closure);
316     }
317 
318     /**
319      * Returns the element at the provided position in this iterable.
320      * In order to return the element, an iterator needs to be traversed
321      * up to the requested position.
322      *
323      * @param position  the position of the element to return
324      * @return the element
325      * @throws IndexOutOfBoundsException if the provided position is outside the
326      *   valid range of this iterable: [0, size)
327      */
328     public E get(final int position) {
329         return IterableUtils.get(iterable, position);
330     }
331 
332     /**
333      * Checks if this iterable is empty.
334      *
335      * @return true if this iterable does not contain any elements, false otherwise
336      */
337     public boolean isEmpty() {
338         return IterableUtils.isEmpty(iterable);
339     }
340 
341     /** {@inheritDoc} */
342     @Override
343     public Iterator<E> iterator() {
344         return iterable.iterator();
345     }
346 
347     /**
348      * Returns a new FluentIterable whose iterator will return at most
349      * the provided maximum number of elements from this iterable.
350      *
351      * @param maxSize  the maximum number of elements
352      * @return a new iterable, providing a bounded view of this iterable
353      * @throws IllegalArgumentException if maxSize is negative
354      */
355     public FluentIterable<E> limit(final long maxSize) {
356         return of(IterableUtils.boundedIterable(iterable, maxSize));
357     }
358 
359     /**
360      * Returns a new FluentIterable whose iterator will loop infinitely
361      * over the elements from this iterable.
362      *
363      * @return a new iterable, providing a looping view of this iterable
364      */
365     public FluentIterable<E> loop() {
366         return of(IterableUtils.loopingIterable(iterable));
367     }
368 
369     /**
370      * Returns a new FluentIterable whose iterator will traverse the
371      * elements from this iterable in reverse order.
372      *
373      * @return a new iterable, providing a reversed view of this iterable
374      */
375     public FluentIterable<E> reverse() {
376         return of(IterableUtils.reversedIterable(iterable));
377     }
378 
379     /**
380      * Returns the number of elements that are contained in this iterable.
381      * In order to determine the size, an iterator needs to be traversed.
382      *
383      * @return the size of this iterable
384      */
385     public int size() {
386         return IterableUtils.size(iterable);
387     }
388 
389     /**
390      * Returns a new FluentIterable whose iterator will skip the first
391      * N elements from this iterable.
392      *
393      * @param elementsToSkip  the number of elements to skip
394      * @return a new iterable, providing a view of this iterable by skipping
395      *   the first N elements
396      * @throws IllegalArgumentException if elementsToSkip is negative
397      */
398     public FluentIterable<E> skip(final long elementsToSkip) {
399         return of(IterableUtils.skippingIterable(iterable, elementsToSkip));
400     }
401 
402     /**
403      * Returns an array containing all elements of this iterable by traversing
404      * its iterator.
405      *
406      * @param arrayClass  the class of array to create
407      * @return an array of the iterable contents
408      * @throws ArrayStoreException if arrayClass is invalid
409      */
410     public E[] toArray(final Class<E> arrayClass) {
411         return IteratorUtils.toArray(iterator(), arrayClass);
412     }
413 
414     /**
415      * Returns a mutable list containing all elements of this iterable
416      * by traversing its iterator.
417      * <p>
418      * The returned list is guaranteed to be mutable.
419      *
420      * @return a list of the iterable contents
421      */
422     public List<E> toList() {
423         return IterableUtils.toList(iterable);
424     }
425 
426     /** {@inheritDoc} */
427     @Override
428     public String toString() {
429         return IterableUtils.toString(iterable);
430     }
431 
432     /**
433      * Returns a new FluentIterable whose iterator will return all elements
434      * of this iterable transformed by the provided transformer.
435      *
436      * @param <O>  the output element type
437      * @param transformer  the transformer applied to each element
438      * @return a new iterable, providing a transformed view of this iterable
439      * @throws NullPointerException if transformer is null
440      */
441     public <O> FluentIterable<O> transform(final Transformer<? super E, ? extends O> transformer) {
442         return of(IterableUtils.transformedIterable(iterable, transformer));
443     }
444 
445     /**
446      * Returns a new FluentIterable whose iterator will return a unique view
447      * of this iterable.
448      *
449      * @return a new iterable, providing a unique view of this iterable
450      */
451     public FluentIterable<E> unique() {
452         return of(IterableUtils.uniqueIterable(iterable));
453     }
454 
455     /**
456      * Returns a new FluentIterable whose iterator will return an unmodifiable
457      * view of this iterable.
458      *
459      * @return a new iterable, providing an unmodifiable view of this iterable
460      */
461     public FluentIterable<E> unmodifiable() {
462         return of(IterableUtils.unmodifiableIterable(iterable));
463     }
464 
465     /**
466      * Returns a new FluentIterable whose iterator will traverse
467      * the elements of this iterable and the other iterable in
468      * alternating order.
469      *
470      * @param other  the other iterable to interleave, may not be null
471      * @return a new iterable, interleaving this iterable with others
472      * @throws NullPointerException if other is null
473      */
474     public FluentIterable<E> zip(final Iterable<? extends E> other) {
475         return of(IterableUtils.zippingIterable(iterable, other));
476     }
477 
478     /**
479      * Returns a new FluentIterable whose iterator will traverse
480      * the elements of this iterable and the other iterables in
481      * alternating order.
482      *
483      * @param others  the iterables to interleave, may not be null
484      * @return a new iterable, interleaving this iterable with others
485      * @throws NullPointerException if either of the provided iterables is null
486      */
487     public FluentIterable<E> zip(final Iterable<? extends E>... others) {
488         return of(IterableUtils.zippingIterable(iterable, others));
489     }
490 
491 }