GrowthList.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.ArrayList;
  19. import java.util.Collection;
  20. import java.util.Collections;
  21. import java.util.List;

  22. /**
  23.  * Decorates another {@code List} to make it seamlessly grow when
  24.  * indices larger than the list size are used on add and set,
  25.  * avoiding most IndexOutOfBoundsExceptions.
  26.  * <p>
  27.  * This class avoids errors by growing when a set or add method would
  28.  * normally throw an IndexOutOfBoundsException.
  29.  * Note that IndexOutOfBoundsException IS returned for invalid negative indices.
  30.  * </p>
  31.  * <p>
  32.  * Trying to set or add to an index larger than the size will cause the list
  33.  * to grow (using {@code null} elements). Clearly, care must be taken
  34.  * not to use excessively large indices, as the internal list will grow to
  35.  * match.
  36.  * </p>
  37.  * <p>
  38.  * Trying to use any method other than add or set with an invalid index will
  39.  * call the underlying list and probably result in an IndexOutOfBoundsException.
  40.  * </p>
  41.  * <p>
  42.  * Take care when using this list with {@code null} values, as
  43.  * {@code null} is the value added when growing the list.
  44.  * </p>
  45.  * <p>
  46.  * All sub-lists will access the underlying list directly, and will throw
  47.  * IndexOutOfBoundsExceptions.
  48.  * </p>
  49.  * <p>
  50.  * This class differs from {@link LazyList} because here growth occurs on
  51.  * set and add, where {@code LazyList} grows on get. However, they
  52.  * can be used together by decorating twice.
  53.  * </p>
  54.  *
  55.  * @param <E> the type of the elements in the list.
  56.  * @see LazyList
  57.  * @since 3.2
  58.  */
  59. public class GrowthList<E> extends AbstractSerializableListDecorator<E> {

  60.     /** Serialization version */
  61.     private static final long serialVersionUID = -3620001881672L;

  62.     /**
  63.      * Factory method to create a growth list.
  64.      *
  65.      * @param <E> the type of the elements in the list
  66.      * @param list  the list to decorate, must not be null
  67.      * @return a new growth list
  68.      * @throws NullPointerException if list is null
  69.      * @since 4.0
  70.      */
  71.     public static <E> GrowthList<E> growthList(final List<E> list) {
  72.         return new GrowthList<>(list);
  73.     }

  74.     /**
  75.      * Constructor that uses an ArrayList internally.
  76.      */
  77.     public GrowthList() {
  78.         super(new ArrayList<>());
  79.     }

  80.     /**
  81.      * Constructor that uses an ArrayList internally.
  82.      *
  83.      * @param initialCapacity  the initial capacity of the ArrayList
  84.      * @throws IllegalArgumentException if initial capacity is invalid
  85.      */
  86.     public GrowthList(final int initialCapacity) {
  87.         super(new ArrayList<>(initialCapacity));
  88.     }

  89.     /**
  90.      * Constructor that wraps (not copies).
  91.      *
  92.      * @param list  the list to decorate, must not be null
  93.      * @throws NullPointerException if list is null
  94.      */
  95.     protected GrowthList(final List<E> list) {
  96.         super(list);
  97.     }

  98.     /**
  99.      * Decorate the add method to perform the growth behavior.
  100.      * <p>
  101.      * If the requested index is greater than the current size, the list will
  102.      * grow to the new size. Indices between the old size and the requested
  103.      * size will be filled with {@code null}.
  104.      * <p>
  105.      * If the index is less than the current size, the value will be added to
  106.      * the underlying list directly.
  107.      * If the index is less than zero, the underlying list is called, which
  108.      * will probably throw an IndexOutOfBoundsException.
  109.      *
  110.      * @param index  the index to add at
  111.      * @param element  the object to add at the specified index
  112.      * @throws UnsupportedOperationException if the underlying list doesn't implement set
  113.      * @throws ClassCastException if the underlying list rejects the element
  114.      * @throws IllegalArgumentException if the underlying list rejects the element
  115.      */
  116.     @Override
  117.     public void add(final int index, final E element) {
  118.         final int size = decorated().size();
  119.         if (index > size) {
  120.             decorated().addAll(Collections.<E>nCopies(index - size, null));
  121.         }
  122.         decorated().add(index, element);
  123.     }

  124.     /**
  125.      * Decorate the addAll method to perform the growth behavior.
  126.      * <p>
  127.      * If the requested index is greater than the current size, the list will
  128.      * grow to the new size. Indices between the old size and the requested
  129.      * size will be filled with {@code null}.
  130.      * <p>
  131.      * If the index is less than the current size, the values will be added to
  132.      * the underlying list directly.
  133.      * If the index is less than zero, the underlying list is called, which
  134.      * will probably throw an IndexOutOfBoundsException.
  135.      *
  136.      * @param index  the index to add at
  137.      * @param coll  the collection to add at the specified index
  138.      * @return true if the list changed
  139.      * @throws UnsupportedOperationException if the underlying list doesn't implement set
  140.      * @throws ClassCastException if the underlying list rejects the element
  141.      * @throws IllegalArgumentException if the underlying list rejects the element
  142.      */
  143.     @Override
  144.     public boolean addAll(final int index, final Collection<? extends E> coll) {
  145.         final int size = decorated().size();
  146.         boolean result = false;
  147.         if (index > size) {
  148.             decorated().addAll(Collections.<E>nCopies(index - size, null));
  149.             result = true;
  150.         }
  151.         return decorated().addAll(index, coll) || result;
  152.     }

  153.     /**
  154.      * Decorate the set method to perform the growth behavior.
  155.      * <p>
  156.      * If the requested index is greater than the current size, the list will
  157.      * grow to the new size. Indices between the old size and the requested
  158.      * size will be filled with {@code null}.
  159.      * <p>
  160.      * If the index is less than the current size, the value will be set onto
  161.      * the underlying list directly.
  162.      * If the index is less than zero, the underlying list is called, which
  163.      * will probably throw an IndexOutOfBoundsException.
  164.      *
  165.      * @param index  the index to set
  166.      * @param element  the object to set at the specified index
  167.      * @return the object previously at that index
  168.      * @throws UnsupportedOperationException if the underlying list doesn't implement set
  169.      * @throws ClassCastException if the underlying list rejects the element
  170.      * @throws IllegalArgumentException if the underlying list rejects the element
  171.      */
  172.     @Override
  173.     public E set(final int index, final E element) {
  174.         final int size = decorated().size();
  175.         if (index >= size) {
  176.             decorated().addAll(Collections.<E>nCopies(index - size + 1, null));
  177.         }
  178.         return decorated().set(index, element);
  179.     }

  180. }