Split List into Multiple Lists with Fixed Number of Elements in Java 8

Split list into multiple lists with fixed number of elements in java 8

It sounds like a problem that is better handled like a low-level Stream operation just like the ops provided by the Stream API itself. A (relative) simple solution may look like:

public static <T> Stream<List<T>> chunked(Stream<T> s, int chunkSize) {
if(chunkSize<1) throw new IllegalArgumentException("chunkSize=="+chunkSize);
if(chunkSize==1) return s.map(Collections::singletonList);
Spliterator<T> src=s.spliterator();
long size=src.estimateSize();
if(size!=Long.MAX_VALUE) size=(size+chunkSize-1)/chunkSize;
int ch=src.characteristics();
ch&=Spliterator.SIZED|Spliterator.ORDERED|Spliterator.DISTINCT|Spliterator.IMMUTABLE;
ch|=Spliterator.NONNULL;
return StreamSupport.stream(new Spliterators.AbstractSpliterator<List<T>>(size, ch)
{
private List<T> current;
@Override
public boolean tryAdvance(Consumer<? super List<T>> action) {
if(current==null) current=new ArrayList<>(chunkSize);
while(current.size()<chunkSize && src.tryAdvance(current::add));
if(!current.isEmpty()) {
action.accept(current);
current=null;
return true;
}
return false;
}
}, s.isParallel());
}

Simple test:

chunked(Stream.of(1, 2, 3, 4, 5, 6, 7), 3)
.parallel().forEachOrdered(System.out::println);

The advantage is that you do not need a full collection of all items for subsequent stream processing, e.g.

chunked(
IntStream.range(0, 1000).mapToObj(i -> {
System.out.println("processing item "+i);
return i;
}), 2).anyMatch(list->list.toString().equals("[6, 7]")));

will print:

processing item 0
processing item 1
processing item 2
processing item 3
processing item 4
processing item 5
processing item 6
processing item 7
true

rather than processing a thousand items of IntStream.range(0, 1000). This also enables using infinite source Streams:

chunked(Stream.iterate(0, i->i+1), 2).anyMatch(list->list.toString().equals("[6, 7]")));

If you are interested in a fully materialized collection rather than applying subsequent Stream operations, you may simply use the following operation:

List<Integer> list=Arrays.asList(1, 2, 3, 4, 5, 6, 7);
int listSize=list.size(), chunkSize=2;
List<List<Integer>> list2=
IntStream.range(0, (listSize-1)/chunkSize+1)
.mapToObj(i->list.subList(i*=chunkSize,
listSize-chunkSize>=i? i+chunkSize: listSize))
.collect(Collectors.toList());

Divide a list into fixed number of list in java

Edit: Please refer to this solution below for partitioning in N times,

Java 8

class SamplePartition
{
public static void main (String[] args) throws java.lang.Exception {
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
final int N=4;
System.out.println(part(l,N));
}

private static <T> List<List<T>> Part(List<T> objs, final int N) {
return new ArrayList<>(IntStream.range(0, objs.size()).boxed().collect(
Collectors.groupingBy(e->e%N,Collectors.mapping(e->objs.get(e), Collectors.toList())
)).values());
}
}

Using Guava

`List<List<Integer>> partitionedLists = Lists.partition(intList, partition`);

Apache Commons Util

List<List<Integer>> partitionedLists = ListUtils.partition(largeList, partition);

Split a list into sublists in java using if possible flatMap

Given that the "sublists" are all of equal size and that you can divide the list into exact sublists of the same size, you could calculate the desired size and then map an IntStream to the starting indexes of each sublist and use that to extract them:

List<Integer> mylist = Arrays.asList(1,2,3,4,5,6,7,8,9,10,11,12);
int size = mylist.size();
int parts = 6;
int partSize = size / parts;
List<List<Integer>> result =
IntStream.range(0, parts)
.mapToObj(i -> mylist.subList(i * partSize, (i + 1) * partSize)))
.collect(Collectors.toList());

EDIT:

IdeOne demo graciously provided by @Turing85

Split an ordered list of numbers multiple lists based on the difference between elements using a Java 8 stream

Well I can only think of a custom collector, since you need some previous state, but this is by far not concise (unless you hide it behind a method):

 private static <T> Collector<Integer, ?, List<List<Integer>>> diffCollector() {

class Acc {

private Integer previous;

private List<List<Integer>> result = new ArrayList<>();

void accumulate(Integer elem) {
if (previous == null) {
previous = elem;
List<Integer> list = new ArrayList<>();
list.add(previous);
result.add(list);
return;
}

if (elem - previous > 2) {
List<Integer> oneMore = new ArrayList<>();
oneMore.add(elem);
result.add(oneMore);
previous = elem;
} else {
result.get(result.size() - 1).add(elem);
previous = elem;
}
}

Acc combine(Acc other) {

throw new UnsupportedOperationException("Not for parallel");
}

List<List<Integer>> finisher() {
return result;
}

}
return Collector.of(Acc::new, Acc::accumulate, Acc::combine, Acc::finisher);
}

And usage would be:

 List<Integer> myList = Arrays.asList(1, 2, 3, 7, 9, 12, 13, 15);
System.out.println(myList.stream().collect(diffCollector()));

java Split one list into multiple lists by exclusion of elements

From what I understand, you want to partition a set elements based on an arbitrary comparison between any two elements in the set. I don't think java comes with that functionality out of the box. One way you can do that is the following:

public class Partition<T> {

public List<Set<T>> partition(List<T> list, BiPredicate<T, T> partitionCondtion) {
List<Set<T>> partition = new ArrayList<>();

while (!list.isEmpty()) {
// get first element from the remaining elements on the original list
T firstElement = list.remove(0);

// add first element into a subset
// all elements on this subset must not be mutually exclusive with firstElement
Set<T> subset = new HashSet<>();
subset.add(firstElement);

// get all remaining elements which can reside in the same subset of
// firstElement
List<T> notMutuallyExclusive = list.stream().filter(e -> !partitionCondtion.test(firstElement, e))
.collect(Collectors.toList());
// add them to the subset of firstElement
subset.addAll(notMutuallyExclusive);

// add subset to partition (list of subsets)
partition.add(subset);

// remove elements added from original list
list.removeAll(notMutuallyExclusive);
}

return partition;
}

}

You can test your scenario like this:

public class PartitionSmallTest {

private BiPredicate<String, String> areMutuallyExclusive() {
return (left, right) -> ("A".equals(left) && "C".equals(right)) || ("C".equals(left) && "A".equals(right));
}

@Test
public void test() {
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));

List<Set<String>> expected = new ArrayList<>();
expected.add(Set.of("A", "B"));
expected.add(Set.of("C"));

List<Set<String>> actual = new Partition<String>().partition(list, areMutuallyExclusive());

Assert.assertEquals(expected, actual);
}

}

Splitting List into sublists along elements

The only solution I come up with for the moment is by implementing your own custom collector.

Before reading the solution, I want to add a few notes about this. I took this question more as a programming exercise, I'm not sure if it can be done with a parallel stream.

So you have to be aware that it'll silently break if the pipeline is run in parallel.

This is not a desirable behavior and should be avoided. This is why I throw an exception in the combiner part (instead of (l1, l2) -> {l1.addAll(l2); return l1;}), as it's used in parallel when combining the two lists, so that you have an exception instead of a wrong result.

Also this is not very efficient due to list copying (although it uses a native method to copy the underlying array).

So here's the collector implementation:

private static Collector<String, List<List<String>>, List<List<String>>> splitBySeparator(Predicate<String> sep) {
final List<String> current = new ArrayList<>();
return Collector.of(() -> new ArrayList<List<String>>(),
(l, elem) -> {
if (sep.test(elem)) {
l.add(new ArrayList<>(current));
current.clear();
}
else {
current.add(elem);
}
},
(l1, l2) -> {
throw new RuntimeException("Should not run this in parallel");
},
l -> {
if (current.size() != 0) {
l.add(current);
return l;
}
);
}

and how to use it:

List<List<String>> ll = list.stream().collect(splitBySeparator(Objects::isNull));

Output:

[[a, b], [c], [d, e]]



As the answer of Joop Eggen is out, it appears that it can be done in parallel (give him credit for that!). With that it reduces the custom collector implementation to:

private static Collector<String, List<List<String>>, List<List<String>>> splitBySeparator(Predicate<String> sep) {
return Collector.of(() -> new ArrayList<List<String>>(Arrays.asList(new ArrayList<>())),
(l, elem) -> {if(sep.test(elem)){l.add(new ArrayList<>());} else l.get(l.size()-1).add(elem);},
(l1, l2) -> {l1.get(l1.size() - 1).addAll(l2.remove(0)); l1.addAll(l2); return l1;});
}

which let the paragraph about parallelism a bit obsolete, however I let it as it can be a good reminder.


Note that the Stream API is not always a substitute. There are tasks that are easier and more suitable using the streams and there are tasks that are not. In your case, you could also create a utility method for that:

private static <T> List<List<T>> splitBySeparator(List<T> list, Predicate<? super T> predicate) {
final List<List<T>> finalList = new ArrayList<>();
int fromIndex = 0;
int toIndex = 0;
for(T elem : list) {
if(predicate.test(elem)) {
finalList.add(list.subList(fromIndex, toIndex));
fromIndex = toIndex + 1;
}
toIndex++;
}
if(fromIndex != toIndex) {
finalList.add(list.subList(fromIndex, toIndex));
}
return finalList;
}

and call it like List<List<String>> list = splitBySeparator(originalList, Objects::isNull);.

It can be improved for checking edge-cases.

Java: how can I split an ArrayList in multiple small ArrayLists?

You can use subList(int fromIndex, int toIndex) to get a view of a portion of the original list.

From the API:

Returns a view of the portion of this list between the specified fromIndex, inclusive, and toIndex, exclusive. (If fromIndex and toIndex are equal, the returned list is empty.) The returned list is backed by this list, so non-structural changes in the returned list are reflected in this list, and vice-versa. The returned list supports all of the optional list operations supported by this list.

Example:

List<Integer> numbers = new ArrayList<Integer>(
Arrays.asList(5,3,1,2,9,5,0,7)
);

List<Integer> head = numbers.subList(0, 4);
List<Integer> tail = numbers.subList(4, 8);
System.out.println(head); // prints "[5, 3, 1, 2]"
System.out.println(tail); // prints "[9, 5, 0, 7]"

Collections.sort(head);
System.out.println(numbers); // prints "[1, 2, 3, 5, 9, 5, 0, 7]"

tail.add(-1);
System.out.println(numbers); // prints "[1, 2, 3, 5, 9, 5, 0, 7, -1]"

If you need these chopped lists to be NOT a view, then simply create a new List from the subList. Here's an example of putting a few of these things together:

// chops a list into non-view sublists of length L
static <T> List<List<T>> chopped(List<T> list, final int L) {
List<List<T>> parts = new ArrayList<List<T>>();
final int N = list.size();
for (int i = 0; i < N; i += L) {
parts.add(new ArrayList<T>(
list.subList(i, Math.min(N, i + L)))
);
}
return parts;
}

List<Integer> numbers = Collections.unmodifiableList(
Arrays.asList(5,3,1,2,9,5,0,7)
);
List<List<Integer>> parts = chopped(numbers, 3);
System.out.println(parts); // prints "[[5, 3, 1], [2, 9, 5], [0, 7]]"
parts.get(0).add(-1);
System.out.println(parts); // prints "[[5, 3, 1, -1], [2, 9, 5], [0, 7]]"
System.out.println(numbers); // prints "[5, 3, 1, 2, 9, 5, 0, 7]" (unmodified!)

Split list into multiple lists with fixed number of elements

I think you're looking for grouped. It returns an iterator, but you can convert the result to a list,

scala> List(1,2,3,4,5,6,"seven").grouped(4).toList
res0: List[List[Any]] = List(List(1, 2, 3, 4), List(5, 6, seven))

Efficient way to divide a list into lists of n size

You'll want to do something that makes use of List.subList(int, int) views rather than copying each sublist. To do this really easily, use Guava's Lists.partition(List, int) method:

List<Foo> foos = ...
for (List<Foo> partition : Lists.partition(foos, n)) {
// do something with partition
}

Note that this, like many things, isn't very efficient with a List that isn't RandomAccess (such as a LinkedList).



Related Topics



Leave a reply



Submit