diff --git a/src/main/java/org/apache/commons/collections4/ListUtils.java b/src/main/java/org/apache/commons/collections4/ListUtils.java index ec268b773..95491f99c 100644 --- a/src/main/java/org/apache/commons/collections4/ListUtils.java +++ b/src/main/java/org/apache/commons/collections4/ListUtils.java @@ -457,7 +457,7 @@ public class ListUtils { * * * After the above code is executed, date will refer to - * a new Date instance. Furthermore, that Date + * a new Date instance. Furthermore, that Date * instance is the fourth element in the list. The first, second, * and third element are all set to null. * @@ -471,6 +471,37 @@ public class ListUtils { return LazyList.lazyList(list, factory); } + /** + * Returns a "lazy" list whose elements will be created on demand. + *

+ * When the index passed to the returned list's {@link List#get(int) get} + * method is greater than the list's size, then the transformer will be used + * to create a new object and that object will be inserted at that index. + *

+ * For instance: + * + *

+     * List<Integer> hours = Arrays.asList(7, 5, 8, 2);
+     * Transformer<Integer,Date> transformer = input -> LocalDateTime.now().withHour(hours.get(input));
+     * List<LocalDateTime> lazy = ListUtils.lazyList(new ArrayList<LocalDateTime>(), transformer);
+     * Date date = lazy.get(3);
+     * 
+ * + * After the above code is executed, date will refer to + * a new Date instance. Furthermore, that Date + * instance is the fourth element in the list. The first, second, + * and third element are all set to null. + * + * @param the element type + * @param list the list to make lazy, must not be null + * @param transformer the transformer for creating new objects, must not be null + * @return a lazy list backed by the given list + * @throws NullPointerException if the List or Transformer is null + */ + public static List lazyList(final List list, final Transformer transformer) { + return LazyList.lazyList(list, transformer); + } + /** * Returns a fixed-sized list backed by the given list. * Elements may not be added or removed from the returned list, but diff --git a/src/main/java/org/apache/commons/collections4/list/LazyList.java b/src/main/java/org/apache/commons/collections4/list/LazyList.java index 237cdd266..e9e227032 100644 --- a/src/main/java/org/apache/commons/collections4/list/LazyList.java +++ b/src/main/java/org/apache/commons/collections4/list/LazyList.java @@ -17,18 +17,20 @@ package org.apache.commons.collections4.list; import java.util.List; +import java.util.Objects; import org.apache.commons.collections4.Factory; +import org.apache.commons.collections4.Transformer; /** * Decorates another List to create objects in the list on demand. *

* When the {@link #get(int)} method is called with an index greater than * the size of the list, the list will automatically grow in size and return - * a new object from the specified factory. The gaps will be filled by null. - * If a get method call encounters a null, it will be replaced with a new - * object from the factory. Thus this list is unsuitable for storing null - * objects. + * a new object from the specified factory or transformer. The gaps will be + * filled by null. If a get method call encounters a null, it will be replaced + * with a new object from the factory. Thus this list is unsuitable for + * storing null objects. *

*

* For instance: @@ -65,11 +67,14 @@ import org.apache.commons.collections4.Factory; public class LazyList extends AbstractSerializableListDecorator { /** Serialization version */ - private static final long serialVersionUID = -1708388017160694542L; + private static final long serialVersionUID = -3677737457567429713L; /** The factory to use to lazily instantiate the objects */ private final Factory factory; + /** The transformer to use to lazily instantiate the objects */ + private final Transformer transformer; + /** * Factory method to create a lazily instantiating list. * @@ -84,6 +89,20 @@ public class LazyList extends AbstractSerializableListDecorator { return new LazyList<>(list, factory); } + /** + * Transformer method to create a lazily instantiating list. + * + * @param the type of the elements in the list + * @param list the list to decorate, must not be null + * @param transformer the transformer to use for creation, must not be null + * @return a new lazy list + * @throws NullPointerException if list or transformer is null + * @since 4.4 + */ + public static LazyList lazyList(final List list, final Transformer transformer) { + return new LazyList<>(list, transformer); + } + //----------------------------------------------------------------------- /** * Constructor that wraps (not copies). @@ -94,10 +113,21 @@ public class LazyList extends AbstractSerializableListDecorator { */ protected LazyList(final List list, final Factory factory) { super(list); - if (factory == null) { - throw new IllegalArgumentException("Factory must not be null"); - } - this.factory = factory; + this.factory = Objects.requireNonNull(factory); + this.transformer = null; + } + + /** + * Constructor that wraps (not copies). + * + * @param list the list to decorate, must not be null + * @param transformer the transformer to use for creation, must not be null + * @throws NullPointerException if list or transformer is null + */ + protected LazyList(final List list, final Transformer transformer) { + super(list); + this.factory = null; + this.transformer = Objects.requireNonNull(transformer); } //----------------------------------------------------------------------- @@ -105,9 +135,10 @@ public class LazyList extends AbstractSerializableListDecorator { * Decorate the get method to perform the lazy behaviour. *

* If the requested index is greater than the current size, the list will - * grow to the new size and a new object will be returned from the factory. - * Indexes in-between the old size and the requested size are left with a - * placeholder that is replaced with a factory object when requested. + * grow to the new size and a new object will be returned from the factory + * or transformer. Indexes in-between the old size and the requested size + * are left with a placeholder that is replaced with a factory or + * transformer object when requested. * * @param index the index to retrieve * @return the element at the given index @@ -120,7 +151,7 @@ public class LazyList extends AbstractSerializableListDecorator { E object = decorated().get(index); if (object == null) { // item is a place holder, create new one, set and return - object = factory.create(); + object = element(index); decorated().set(index, object); return object; } @@ -132,7 +163,7 @@ public class LazyList extends AbstractSerializableListDecorator { decorated().add(null); } // create our last object, set and return - final E object = factory.create(); + final E object = element(index); decorated().add(object); return object; } @@ -140,7 +171,23 @@ public class LazyList extends AbstractSerializableListDecorator { @Override public List subList(final int fromIndex, final int toIndex) { final List sub = decorated().subList(fromIndex, toIndex); - return new LazyList<>(sub, factory); + if (factory != null) { + return new LazyList<>(sub, factory); + } else if (transformer != null) { + return new LazyList<>(sub, transformer); + } else { + throw new IllegalStateException("Factory and Transformer are both null!"); + } + } + + private E element(final int index) { + if (factory != null) { + return factory.create(); + } else if (transformer != null) { + return transformer.transform(index); + } else { + throw new IllegalStateException("Factory and Transformer are both null!"); + } } } diff --git a/src/test/java/org/apache/commons/collections4/ListUtilsTest.java b/src/test/java/org/apache/commons/collections4/ListUtilsTest.java index a9bf8e8ce..9a3be4c7b 100644 --- a/src/test/java/org/apache/commons/collections4/ListUtilsTest.java +++ b/src/test/java/org/apache/commons/collections4/ListUtilsTest.java @@ -145,7 +145,7 @@ public class ListUtilsTest { } @Test - public void testLazyList() { + public void testLazyFactoryList() { final List list = ListUtils.lazyList(new ArrayList(), new Factory() { private int index; @@ -164,6 +164,27 @@ public class ListUtilsTest { assertEquals(6, list.size()); } + @Test + public void testLazyTransformerList() { + final List offsets = Arrays.asList(3, 5, 1, 5, 3, 6); + final List list = ListUtils.lazyList(new ArrayList<>(), new Transformer() { + + private int index; + + @Override + public Integer transform(Integer input) { + return offsets.get(input) + index++; + } + + }); + + assertNotNull(list.get(5)); + assertEquals(6, list.size()); + + assertNotNull(list.get(5)); + assertEquals(6, list.size()); + } + @Test public void testEmptyIfNull() { assertTrue(ListUtils.emptyIfNull(null).isEmpty()); diff --git a/src/test/java/org/apache/commons/collections4/list/LazyListTest.java b/src/test/java/org/apache/commons/collections4/list/LazyListTest.java new file mode 100644 index 000000000..26adbe02a --- /dev/null +++ b/src/test/java/org/apache/commons/collections4/list/LazyListTest.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.collections4.list; + +import org.apache.commons.collections4.AbstractObjectTest; +import org.apache.commons.collections4.Factory; +import org.apache.commons.collections4.Transformer; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class LazyListTest extends AbstractObjectTest { + + public LazyListTest(String testName) { + super(testName); + } + + @Override + public Object makeObject() { + final Factory dateFactory = LocalDateTime::now; + return new LazyList<>(new ArrayList<>(), dateFactory); + } + + @Override + public void testSimpleSerialization() { + // Factory and Transformer are not serializable + } + + @Override + public void testSerializeDeserializeThenCompare() { + // Factory and Transformer are not serializable + } + + @Override + public void testCanonicalEmptyCollectionExists() { + // Factory and Transformer are not serializable + } + + @Override + public void testCanonicalFullCollectionExists() { + // Factory and Transformer are not serializable + } + + public void testElementCreationWithFactory() { + final Factory dateFactory = LocalDateTime::now; + final List list = new LazyList<>(new ArrayList<>(), dateFactory); + + assertTrue(list.isEmpty()); + + final LocalDateTime firstElement = list.get(0); + assertNotNull(firstElement); + assertFalse(list.isEmpty()); + } + + public void testElementCreationWithTransformer() { + final Factory dateFactory = LocalDateTime::now; + final List list = new LazyList<>(new ArrayList<>(), dateFactory); + + assertTrue(list.isEmpty()); + + final LocalDateTime firstElement = list.get(0); + assertNotNull(firstElement); + assertFalse(list.isEmpty()); + } + + public void testCreateNullGapsWithFactory() { + final Factory dateFactory = LocalDateTime::now; + final List list = new LazyList<>(new ArrayList<>(), dateFactory); + + final LocalDateTime fourthElement = list.get(3); + assertFalse(list.isEmpty()); + assertNotNull(fourthElement); + } + + public void testCreateNullGapsWithTransformer() { + final List hours = Arrays.asList(7, 5, 8, 2); + final Transformer dateFactory = input -> LocalDateTime.now().withHour(hours.get(input)); + final List list = new LazyList<>(new ArrayList<>(), dateFactory); + + final LocalDateTime fourthElement = list.get(3); + assertFalse(list.isEmpty()); + assertNotNull(fourthElement); + } + + + +}