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   * @see GrowthList
65   * @since 3.0
66   */
67  public class LazyList<E> extends AbstractSerializableListDecorator<E> {
68  
69      /** Serialization version */
70      private static final long serialVersionUID = -3677737457567429713L;
71  
72      /**
73       * Factory method to create a lazily instantiating list.
74       *
75       * @param <E> the type of the elements in the list
76       * @param list  the list to decorate, must not be null
77       * @param factory  the factory to use for creation, must not be null
78       * @return a new lazy list
79       * @throws NullPointerException if list or factory is null
80       * @since 4.0
81       */
82      public static <E> LazyList<E> lazyList(final List<E> list, final Factory<? extends E> factory) {
83          return new LazyList<>(list, factory);
84      }
85  
86      /**
87       * Transformer method to create a lazily instantiating list.
88       *
89       * @param <E> the type of the elements in the list
90       * @param list  the list to decorate, must not be null
91       * @param transformer  the transformer to use for creation, must not be null
92       * @return a new lazy list
93       * @throws NullPointerException if list or transformer is null
94       * @since 4.4
95       */
96      public static <E> LazyList<E> lazyList(final List<E> list, final Transformer<Integer, ? extends E> transformer) {
97          return new LazyList<>(list, transformer);
98      }
99  
100     /** The factory to use to lazily instantiate the objects */
101     private final Factory<? extends E> factory;
102 
103     /** The transformer to use to lazily instantiate the objects */
104     private final Transformer<Integer, ? extends E> transformer;
105 
106     /**
107      * Constructor that wraps (not copies).
108      *
109      * @param list  the list to decorate, must not be null
110      * @param factory  the factory to use for creation, must not be null
111      * @throws NullPointerException if list or factory is null
112      */
113     protected LazyList(final List<E> list, final Factory<? extends E> factory) {
114         super(list);
115         this.factory = Objects.requireNonNull(factory);
116         this.transformer = null;
117     }
118 
119     /**
120      * Constructor that wraps (not copies).
121      *
122      * @param list  the list to decorate, must not be null
123      * @param transformer  the transformer to use for creation, must not be null
124      * @throws NullPointerException if list or transformer is null
125      */
126     protected LazyList(final List<E> list, final Transformer<Integer, ? extends E> transformer) {
127         super(list);
128         this.factory = null;
129         this.transformer = Objects.requireNonNull(transformer);
130     }
131 
132     private E element(final int index) {
133         if (factory != null) {
134             return factory.create();
135         }
136         if (transformer != null) {
137             return transformer.transform(index);
138         }
139         throw new IllegalStateException("Factory and Transformer are both null!");
140     }
141 
142     /**
143      * Decorate the get method to perform the lazy behavior.
144      * <p>
145      * If the requested index is greater than the current size, the list will
146      * grow to the new size and a new object will be returned from the factory
147      * or transformer. Indexes in-between the old size and the requested size
148      * are left with a placeholder that is replaced with a factory or
149      * transformer object when requested.
150      *
151      * @param index  the index to retrieve
152      * @return the element at the given index
153      */
154     @Override
155     public E get(final int index) {
156         final int size = decorated().size();
157         if (index < size) {
158             // within bounds, get the object
159             E object = decorated().get(index);
160             if (object == null) {
161                 // item is a placeholder, create new one, set and return
162                 object = element(index);
163                 decorated().set(index, object);
164             }
165             // good and ready to go
166             return object;
167         }
168         // we have to grow the list
169         for (int i = size; i < index; i++) {
170             decorated().add(null);
171         }
172         // create our last object, set and return
173         final E object = element(index);
174         decorated().add(object);
175         return object;
176     }
177 
178     @Override
179     public List<E> subList(final int fromIndex, final int toIndex) {
180         final List<E> sub = decorated().subList(fromIndex, toIndex);
181         if (factory != null) {
182             return new LazyList<>(sub, factory);
183         }
184         if (transformer != null) {
185             return new LazyList<>(sub, transformer);
186         }
187         throw new IllegalStateException("Factory and Transformer are both null!");
188     }
189 
190 }