[COLLECTIONS-694] Support Transformer for LazyList (#52)
Squashing and merging. @stovocor @kinow
This commit is contained in:
parent
7b101411e5
commit
d88810bd10
|
@ -457,7 +457,7 @@ public class ListUtils {
|
|||
* </pre>
|
||||
*
|
||||
* After the above code is executed, <code>date</code> will refer to
|
||||
* a new <code>Date</code> instance. Furthermore, that <code>Date</code>
|
||||
* a new <code>Date</code> instance. Furthermore, that <code>Date</code>
|
||||
* instance is the fourth element in the list. The first, second,
|
||||
* and third element are all set to <code>null</code>.
|
||||
*
|
||||
|
@ -471,6 +471,37 @@ public class ListUtils {
|
|||
return LazyList.lazyList(list, factory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a "lazy" list whose elements will be created on demand.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* For instance:
|
||||
*
|
||||
* <pre>
|
||||
* 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);
|
||||
* </pre>
|
||||
*
|
||||
* After the above code is executed, <code>date</code> will refer to
|
||||
* a new <code>Date</code> instance. Furthermore, that <code>Date</code>
|
||||
* instance is the fourth element in the list. The first, second,
|
||||
* and third element are all set to <code>null</code>.
|
||||
*
|
||||
* @param <E> 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 <E> List<E> lazyList(final List<E> list, final Transformer<Integer, ? extends E> 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
|
||||
|
|
|
@ -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 <code>List</code> to create objects in the list on demand.
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
* <p>
|
||||
* For instance:
|
||||
|
@ -65,11 +67,14 @@ import org.apache.commons.collections4.Factory;
|
|||
public class LazyList<E> extends AbstractSerializableListDecorator<E> {
|
||||
|
||||
/** 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<? extends E> factory;
|
||||
|
||||
/** The transformer to use to lazily instantiate the objects */
|
||||
private final Transformer<Integer, ? extends E> transformer;
|
||||
|
||||
/**
|
||||
* Factory method to create a lazily instantiating list.
|
||||
*
|
||||
|
@ -84,6 +89,20 @@ public class LazyList<E> extends AbstractSerializableListDecorator<E> {
|
|||
return new LazyList<>(list, factory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transformer method to create a lazily instantiating list.
|
||||
*
|
||||
* @param <E> 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 <E> LazyList<E> lazyList(final List<E> list, final Transformer<Integer, ? extends E> transformer) {
|
||||
return new LazyList<>(list, transformer);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* Constructor that wraps (not copies).
|
||||
|
@ -94,10 +113,21 @@ public class LazyList<E> extends AbstractSerializableListDecorator<E> {
|
|||
*/
|
||||
protected LazyList(final List<E> list, final Factory<? extends E> 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<E> list, final Transformer<Integer, ? extends E> transformer) {
|
||||
super(list);
|
||||
this.factory = null;
|
||||
this.transformer = Objects.requireNonNull(transformer);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
@ -105,9 +135,10 @@ public class LazyList<E> extends AbstractSerializableListDecorator<E> {
|
|||
* Decorate the get method to perform the lazy behaviour.
|
||||
* <p>
|
||||
* 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<E> extends AbstractSerializableListDecorator<E> {
|
|||
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<E> extends AbstractSerializableListDecorator<E> {
|
|||
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<E> extends AbstractSerializableListDecorator<E> {
|
|||
@Override
|
||||
public List<E> subList(final int fromIndex, final int toIndex) {
|
||||
final List<E> 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!");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -145,7 +145,7 @@ public class ListUtilsTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testLazyList() {
|
||||
public void testLazyFactoryList() {
|
||||
final List<Integer> list = ListUtils.lazyList(new ArrayList<Integer>(), new Factory<Integer>() {
|
||||
|
||||
private int index;
|
||||
|
@ -164,6 +164,27 @@ public class ListUtilsTest {
|
|||
assertEquals(6, list.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLazyTransformerList() {
|
||||
final List<Integer> offsets = Arrays.asList(3, 5, 1, 5, 3, 6);
|
||||
final List<Integer> list = ListUtils.lazyList(new ArrayList<>(), new Transformer<Integer, Integer>() {
|
||||
|
||||
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());
|
||||
|
|
|
@ -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<LocalDateTime> 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<LocalDateTime> dateFactory = LocalDateTime::now;
|
||||
final List<LocalDateTime> 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<LocalDateTime> dateFactory = LocalDateTime::now;
|
||||
final List<LocalDateTime> 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<LocalDateTime> dateFactory = LocalDateTime::now;
|
||||
final List<LocalDateTime> list = new LazyList<>(new ArrayList<>(), dateFactory);
|
||||
|
||||
final LocalDateTime fourthElement = list.get(3);
|
||||
assertFalse(list.isEmpty());
|
||||
assertNotNull(fourthElement);
|
||||
}
|
||||
|
||||
public void testCreateNullGapsWithTransformer() {
|
||||
final List<Integer> hours = Arrays.asList(7, 5, 8, 2);
|
||||
final Transformer<Integer, LocalDateTime> dateFactory = input -> LocalDateTime.now().withHour(hours.get(input));
|
||||
final List<LocalDateTime> list = new LazyList<>(new ArrayList<>(), dateFactory);
|
||||
|
||||
final LocalDateTime fourthElement = list.get(3);
|
||||
assertFalse(list.isEmpty());
|
||||
assertNotNull(fourthElement);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
Loading…
Reference in New Issue