The requirement is a bit odd, but you could do:
final int[] counter = new int[] {0};
List<List<Object>> listOfLists = in.stream()
.collect(Collectors.groupingBy( x -> counter[0]++ / MAX_ROW_LENGTH ))
.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.map(Map.Entry::getValue)
.collect(Collectors.toList());
You could probably streamline this by using the variant of groupingBy
that takes a mapSupplier
lambda, and supplying a SortedMap
. This should return an EntrySet
that iterates in order. I leave it as an exercise.
What we're doing here is:
- Collecting your list items into a
Map<Integer,Object>
using a counter to group. The counter is held in a single-element array because the lambda can only use local variables if they're final
.
- Getting the map entries as a stream, and sorting by the
Integer
key.
- Using
Stream::map()
to convert the stream of Map.Entry<Integer,Object>
into a stream of Object
values.
- Collecting this into a list.
This doesn't benefit from any "free" parallelisation. It has a memory overhead in the intermediate Map
. It's not particularly easy to read.
However, I wouldn't do this, just for the sake of using a lambda. I would do something like:
for(int i=0; i<in.size(); i += MAX_ROW_LENGTH) {
listOfList.add(
listToSplit.subList(i, Math.min(i + MAX_ROW_LENGTH, in.size());
}
(Yours had a defensive copy new ArrayList<>(listToSplit.subList(...))
. I've not duplicated it because it's not always necessary - for example if the input list is unmodifiable and the output lists aren't intended to be modifiable. But do put it back in if you decide you need it in your case.)
This will be extremely fast on any in-memory list. You're very unlikely to want to parallelise it.
Alternatively, you could write your own (unmodifiable) implementation of List
that's a view over the underlying List<Object>
:
public class PartitionedList<T> extends AbstractList<List<T>> {
private final List<T> source;
private final int sublistSize;
public PartitionedList(T source, int sublistSize) {
this.source = source;
this.sublistSize = sublistSize;
}
@Override
public int size() {
return source.size() / sublistSize;
}
@Override
public List<T> get(int index) {
int sourceIndex = index * sublistSize
return source.subList(sourceIndex,
Math.min(sourceIndex + sublistSize, source.size());
}
}
Again, it's up to you whether you want to make defensive copies here.
This will be have equivalent big-O access time to the underlying list.