LazyList.java

  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. import java.util.List;
  19. import java.util.Objects;

  20. import org.apache.commons.collections4.Factory;
  21. import org.apache.commons.collections4.Transformer;

  22. /**
  23.  * Decorates another {@code List} to create objects in the list on demand.
  24.  * <p>
  25.  * When the {@link #get(int)} method is called with an index greater than
  26.  * the size of the list, the list will automatically grow in size and return
  27.  * a new object from the specified factory or transformer. The gaps will be
  28.  * filled by null. If a get method call encounters a null, it will be replaced
  29.  * with a new object from the factory. Thus this list is unsuitable for
  30.  * storing null objects.
  31.  * </p>
  32.  * <p>
  33.  * For instance:
  34.  * </p>
  35.  *
  36.  * <pre>
  37.  * Factory&lt;Date&gt; factory = new Factory&lt;Date&gt;() {
  38.  *     public Date create() {
  39.  *         return new Date();
  40.  *     }
  41.  * }
  42.  * List&lt;Date&gt; lazy = LazyList.decorate(new ArrayList&lt;Date&gt;(), factory);
  43.  * Date date = lazy.get(3);
  44.  * </pre>
  45.  *
  46.  * <p>
  47.  * After the above code is executed, {@code date} will contain
  48.  * a new {@code Date} instance.  Furthermore, that {@code Date}
  49.  * instance is the fourth element in the list.  The first, second,
  50.  * and third element are all set to {@code null}.
  51.  * </p>
  52.  * <p>
  53.  * This class differs from {@link GrowthList} because here growth occurs on
  54.  * get, where {@code GrowthList} grows on set and add. However, they
  55.  * could easily be used together by decorating twice.
  56.  * </p>
  57.  * <p>
  58.  * This class is Serializable from Commons Collections 3.1.
  59.  * </p>
  60.  *
  61.  * @param <E> the type of the elements in the list.
  62.  * @see GrowthList
  63.  * @since 3.0
  64.  */
  65. public class LazyList<E> extends AbstractSerializableListDecorator<E> {

  66.     /** Serialization version */
  67.     private static final long serialVersionUID = -3677737457567429713L;

  68.     /**
  69.      * Factory method to create a lazily instantiating list.
  70.      *
  71.      * @param <E> the type of the elements in the list
  72.      * @param list  the list to decorate, must not be null
  73.      * @param factory  the factory to use for creation, must not be null
  74.      * @return a new lazy list
  75.      * @throws NullPointerException if list or factory is null
  76.      * @since 4.0
  77.      */
  78.     public static <E> LazyList<E> lazyList(final List<E> list, final Factory<? extends E> factory) {
  79.         return new LazyList<>(list, factory);
  80.     }

  81.     /**
  82.      * Transformer method to create a lazily instantiating list.
  83.      *
  84.      * @param <E> the type of the elements in the list
  85.      * @param list  the list to decorate, must not be null
  86.      * @param transformer  the transformer to use for creation, must not be null
  87.      * @return a new lazy list
  88.      * @throws NullPointerException if list or transformer is null
  89.      * @since 4.4
  90.      */
  91.     public static <E> LazyList<E> lazyList(final List<E> list, final Transformer<Integer, ? extends E> transformer) {
  92.         return new LazyList<>(list, transformer);
  93.     }

  94.     /** The factory to use to lazily instantiate the objects */
  95.     private final Factory<? extends E> factory;

  96.     /** The transformer to use to lazily instantiate the objects */
  97.     private final Transformer<Integer, ? extends E> transformer;

  98.     /**
  99.      * Constructor that wraps (not copies).
  100.      *
  101.      * @param list  the list to decorate, must not be null
  102.      * @param factory  the factory to use for creation, must not be null
  103.      * @throws NullPointerException if list or factory is null
  104.      */
  105.     protected LazyList(final List<E> list, final Factory<? extends E> factory) {
  106.         super(list);
  107.         this.factory = Objects.requireNonNull(factory);
  108.         this.transformer = null;
  109.     }

  110.     /**
  111.      * Constructor that wraps (not copies).
  112.      *
  113.      * @param list  the list to decorate, must not be null
  114.      * @param transformer  the transformer to use for creation, must not be null
  115.      * @throws NullPointerException if list or transformer is null
  116.      */
  117.     protected LazyList(final List<E> list, final Transformer<Integer, ? extends E> transformer) {
  118.         super(list);
  119.         this.factory = null;
  120.         this.transformer = Objects.requireNonNull(transformer);
  121.     }

  122.     private E element(final int index) {
  123.         if (factory != null) {
  124.             return factory.get();
  125.         }
  126.         if (transformer != null) {
  127.             return transformer.apply(index);
  128.         }
  129.         throw new IllegalStateException("Factory and Transformer are both null!");
  130.     }

  131.     /**
  132.      * Decorate the get method to perform the lazy behavior.
  133.      * <p>
  134.      * If the requested index is greater than the current size, the list will
  135.      * grow to the new size and a new object will be returned from the factory
  136.      * or transformer. Indexes in-between the old size and the requested size
  137.      * are left with a placeholder that is replaced with a factory or
  138.      * transformer object when requested.
  139.      *
  140.      * @param index  the index to retrieve
  141.      * @return the element at the given index
  142.      */
  143.     @Override
  144.     public E get(final int index) {
  145.         final int size = decorated().size();
  146.         if (index < size) {
  147.             // within bounds, get the object
  148.             E object = decorated().get(index);
  149.             if (object == null) {
  150.                 // item is a placeholder, create new one, set and return
  151.                 object = element(index);
  152.                 decorated().set(index, object);
  153.             }
  154.             // good and ready to go
  155.             return object;
  156.         }
  157.         // we have to grow the list
  158.         for (int i = size; i < index; i++) {
  159.             decorated().add(null);
  160.         }
  161.         // create our last object, set and return
  162.         final E object = element(index);
  163.         decorated().add(object);
  164.         return object;
  165.     }

  166.     @Override
  167.     public List<E> subList(final int fromIndex, final int toIndex) {
  168.         final List<E> sub = decorated().subList(fromIndex, toIndex);
  169.         if (factory != null) {
  170.             return new LazyList<>(sub, factory);
  171.         }
  172.         if (transformer != null) {
  173.             return new LazyList<>(sub, transformer);
  174.         }
  175.         throw new IllegalStateException("Factory and Transformer are both null!");
  176.     }

  177. }