I was looking for a decent implementation of a generic lazy non-modifiable list implementation to wrap my search result entries. The unmodifiable part of the task is easy as it can be achieved by Collections.unmodifiableList()
so I only need to sort out the the lazy part.
Surprisingly, google-collections doesn't have anything to offer; while LazyList from Apache Commons Collections does not support generics.
I have found an attempt to build something on top of google-collections but it seems to be incomplete (e.g. does not support size()
), outdated (does not compile with 1.0 final) and requiring some external classes, but could be used as a good starting point to build my own class.
Is anybody aware of any good implementation of a LazyList? If not, which option do you think is better:
java.util.AbstractList
;Any other suggestions are welcome.
EDIT: explanation why I need a lazy list.
I have got a Lucene search result (TopDocs) which is basically a bunch of pointers to Lucene documents. My search result class would take these pointers as an input and return a list of objects which are made of extracted and otherwise processed Lucene documents. By wrapping everything into a lazy list I want to ensure I am not doing expensive processing when unnecessary.
Google-collections and Guava's Lists.transform
method gives you the laziness you seek. Sticking with Iterables.transform
should be just as good.
But, if you're also concerned that the results should be cached once first created, well... right now, this is the best I'm coming up with, and it won't be very comforting:
List<Supplier<ExpensiveResult>> suppliers =
ImmutableList.copyOf(Lists.transform(keys,
new Function<Key, Supplier<ExpensiveResult>>() {
public Supplier<ExpensiveResult> apply(Key key) {
return Suppliers.memoize(Suppliers.compose(
myExpensiveFunction(),
Suppliers.ofInstance(key)));
}
}));
return Lists.transform(suppliers, ThisClass.<ExpensiveResult>supplyFunction());
. . .
private static <T> Function<Supplier<T>, T> supplyFunction() {
return new Function<Supplier<T>, T>() {
public T apply(Supplier<T> supplier) {
return supplier.get();
}
};
}
Yes, you can laugh. And you probably should. I... don't really recommend this. Still might be less code than what you're currently doing. And I just tested it.. it works.
There's a project which added Generics features to Apache commons-collections:
http://sourceforge.net/projects/collections/
(Commons-collections with Generics)
I have actually solved this in a different way. Instead of going through lazy and unmodifiable, I simply implemented java.lang.Iterable<T>
. The implementation throws UnsupportedOperationException
on remove()
.
I had to slightly modify some other code parts, give up something, but I believe this was the best choice. Iterable
allows it to be put on foreach loop.
Sorry to disappoint if this won't be a viable choice for somebody in a similar situation and thanks very much for the ideas.
The solution of Peter Maas you link looks good to me - I strongly suggest you work with that instead spending time reinventing this. Just replace Factory<T>
with Supplier<T>
(included in google collections). His implementation of subList is quite smart too, though it has some peculiar implications: if you get a subList()
, and try to add an element out of the subList's bounds, you won't get an IndexOutOfBoundsException
(as a correct subList ought to do), but you'll insert extra elements in the list. The odds are you wouldn't need sublists, so the safest would be to implement that method by throwing UnsupportedOperationException
(or construct a LazyList that has an extra flag of whether it is allowed to grow by get()
invocations beyond its size: if it created by subList
, then it isn't).
size()
is supported (automatically, by ForwardingList
itself).
Update: Note that, as Kevin said, you did not explained why something like this would really be what you need. Also, perhaps you might want to consider whether something like this could be applicable:
final Supplier<T> supplier = ...;
Map<Integer, T> graphs = new MapMaker()
.makeComputingMap(
new Function<Integer, T>() {
public T apply(Integer index) {
return supplier.get();
}
});
Since List<T>
and Map<Integer, T>
more or less represent the same abstract data type, and since it appears from your comment that (1) you don't like nulls to be considered as elements (good!), and (2) that your structure might be sparse, where an actual ArrayList would be wasteful.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With