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.list;
18  
19  import java.util.List;
20  import java.util.Objects;
21  
22  import org.apache.commons.collections4.Factory;
23  import org.apache.commons.collections4.Transformer;
24  
25  /**
26   * Decorates another {@code List} to create objects in the list on demand.
27   * <p>
28   * When the {@link #get(int)} method is called with an index greater than
29   * the size of the list, the list will automatically grow in size and return
30   * a new object from the specified factory or transformer. The gaps will be
31   * filled by null. If a get method call encounters a null, it will be replaced
32   * with a new object from the factory. Thus this list is unsuitable for
33   * storing null objects.
34   * </p>
35   * <p>
36   * For instance:
37   * </p>
38   *
39   * <pre>
40   * Factory&lt;Date&gt; factory = new Factory&lt;Date&gt;() {
41   *     public Date create() {
42   *         return new Date();
43   *     }
44   * }
45   * List&lt;Date&gt; lazy = LazyList.decorate(new ArrayList&lt;Date&gt;(), factory);
46   * Date date = lazy.get(3);
47   * </pre>
48   *
49   * <p>
50   * After the above code is executed, {@code date} will contain
51   * a new {@code Date} instance.  Furthermore, that {@code Date}
52   * instance is the fourth element in the list.  The first, second,
53   * and third element are all set to {@code null}.
54   * </p>
55   * <p>
56   * This class differs from {@link GrowthList} because here growth occurs on
57   * get, where {@code GrowthList} grows on set and add. However, they
58   * could easily be used together by decorating twice.
59   * </p>
60   * <p>
61   * This class is Serializable from Commons Collections 3.1.
62   * </p>
63   *
64   * @param <E> the type of the elements in the list.
65   * @see GrowthList
66   * @since 3.0
67   */
68  public class LazyList<E> extends AbstractSerializableListDecorator<E> {
69  
70      /** Serialization version */
71      private static final long serialVersionUID = -3677737457567429713L;
72  
73      /**
74       * Factory method to create a lazily instantiating list.
75       *
76       * @param <E> the type of the elements in the list
77       * @param list  the list to decorate, must not be null
78       * @param factory  the factory to use for creation, must not be null
79       * @return a new lazy list
80       * @throws NullPointerException if list or factory is null
81       * @since 4.0
82       */
83      public static <E> LazyList<E> lazyList(final List<E> list, final Factory<? extends E> factory) {
84          return new LazyList<>(list, factory);
85      }
86  
87      /**
88       * Transformer method to create a lazily instantiating list.
89       *
90       * @param <E> the type of the elements in the list
91       * @param list  the list to decorate, must not be null
92       * @param transformer  the transformer to use for creation, must not be null
93       * @return a new lazy list
94       * @throws NullPointerException if list or transformer is null
95       * @since 4.4
96       */
97      public static <E> LazyList<E> lazyList(final List<E> list, final Transformer<Integer, ? extends E> transformer) {
98          return new LazyList<>(list, transformer);
99      }
100 
101     /** The factory to use to lazily instantiate the objects */
102     private final Factory<? extends E> factory;
103 
104     /** The transformer to use to lazily instantiate the objects */
105     private final Transformer<Integer, ? extends E> transformer;
106 
107     /**
108      * Constructor that wraps (not copies).
109      *
110      * @param list  the list to decorate, must not be null
111      * @param factory  the factory to use for creation, must not be null
112      * @throws NullPointerException if list or factory is null
113      */
114     protected LazyList(final List<E> list, final Factory<? extends E> factory) {
115         super(list);
116         this.factory = Objects.requireNonNull(factory);
117         this.transformer = null;
118     }
119 
120     /**
121      * Constructor that wraps (not copies).
122      *
123      * @param list  the list to decorate, must not be null
124      * @param transformer  the transformer to use for creation, must not be null
125      * @throws NullPointerException if list or transformer is null
126      */
127     protected LazyList(final List<E> list, final Transformer<Integer, ? extends E> transformer) {
128         super(list);
129         this.factory = null;
130         this.transformer = Objects.requireNonNull(transformer);
131     }
132 
133     private E element(final int index) {
134         if (factory != null) {
135             return factory.get();
136         }
137         if (transformer != null) {
138             return transformer.apply(index);
139         }
140         throw new IllegalStateException("Factory and Transformer are both null!");
141     }
142 
143     /**
144      * Decorate the get method to perform the lazy behavior.
145      * <p>
146      * If the requested index is greater than the current size, the list will
147      * grow to the new size and a new object will be returned from the factory
148      * or transformer. Indexes in-between the old size and the requested size
149      * are left with a placeholder that is replaced with a factory or
150      * transformer object when requested.
151      *
152      * @param index  the index to retrieve
153      * @return the element at the given index
154      */
155     @Override
156     public E get(final int index) {
157         final int size = decorated().size();
158         if (index < size) {
159             // within bounds, get the object
160             E object = decorated().get(index);
161             if (object == null) {
162                 // item is a placeholder, create new one, set and return
163                 object = element(index);
164                 decorated().set(index, object);
165             }
166             // good and ready to go
167             return object;
168         }
169         // we have to grow the list
170         for (int i = size; i < index; i++) {
171             decorated().add(null);
172         }
173         // create our last object, set and return
174         final E object = element(index);
175         decorated().add(object);
176         return object;
177     }
178 
179     @Override
180     public List<E> subList(final int fromIndex, final int toIndex) {
181         final List<E> sub = decorated().subList(fromIndex, toIndex);
182         if (factory != null) {
183             return new LazyList<>(sub, factory);
184         }
185         if (transformer != null) {
186             return new LazyList<>(sub, transformer);
187         }
188         throw new IllegalStateException("Factory and Transformer are both null!");
189     }
190 
191 }