2

How can I take a Java list and split it into smaller lists of size n in Java using streams?

In JavaScript, I'd use the reduce() function and do something like this:

const n = 3;
const sublists = [1,2,3,4,5,6,7,8,9,0]
  .reduce((r, i) => {
    r[r.length - 1].length == n 
      ? r.push([i])
      : r[r.length - 1].push(i);
    return r;
  }, [[]]);
console.log(sublists);

I'm trying to do that with a Java stream, but I can't seem to figure out how to get it to let me use an ArrayList<ArrayList<Integer>> as my initial value and then add the lists. I'm a little confused how the combinator and accumulator play in to let me use them, or even if reduce() is the best approach for Java.

samanime
  • 25,408
  • 15
  • 90
  • 139
  • 1
    You might want to take a look at the answers at [Is there a common Java utility to break a list into batches?](https://stackoverflow.com/questions/12026885/is-there-a-common-java-utility-to-break-a-list-into-batches) – Muhd Apr 14 '20 at 06:14

2 Answers2

2

It looks like you have a JavaScript array, so the equivalent Java code would presumably use an IntStream. First, calculate the correct number of rows and then use Arrays.copyOfRange collect to a List and then convert to an int[][]. Finally, use Arrays.deepToString to print the array. Like,

final int n = 3;
int[] arr = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
int rows = 1 + arr.length / n;
int[][] sublists = IntStream.range(0, rows)
        .mapToObj(i -> 
                Arrays.copyOfRange(arr, n * i, Math.min(n + (n * i), arr.length)))
        .collect(Collectors.toList()).toArray(new int[rows][n]);
System.out.println(Arrays.deepToString(sublists));

Which outputs

[[1, 2, 3], [4, 5, 6], [7, 8, 9], [0]]

For a List<Integer> it might be done like

final int n = 3;
List<Integer> arr = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
int rows = 1 + arr.size() / n;
List<List<Integer>> sublists = IntStream.range(0, rows)
        .mapToObj(i -> arr.subList(n * i, Math.min(n + (n * i), arr.size())))
        .collect(Collectors.toList());
System.out.println(sublists);

for the same (requested) output.

Elliott Frisch
  • 198,278
  • 20
  • 158
  • 249
  • Thanks, this looks about like what I'm looking for. I actually have a `List`, but I should be able to figure out the conversion. – samanime Dec 04 '17 at 18:08
1

Pretty straightforward:

    List<Integer> list = List.of(1, 2, 3, 4, 5, 7, 8, 9, 10, 11);
    int n = 3;
    List<List<Integer>> result = new LinkedList<>();
    int size = list.size();
    for (int i = 0; i <= size; i += n) {
        result.add(list.subList(i, Math.min(i + n, size)));
    }        
    System.out.println(result);  // [[1, 2, 3], [4, 5, 7], [8, 9, 10], [11]]

A Java 8 solution:

List<Integer> list = List.of(1, 2, 3, 4, 5, 7, 8, 9, 10, 11);
int n = 3;
List<List<Integer>> result = IntStream.range(0, list.size())
    .filter(i -> i % n == 0)
    .mapToObj(i -> list.subList(i, Math.min(i + n, list.size())))
    .collect(Collectors.toList());
System.out.println(result);  // [[1, 2, 3], [4, 5, 7], [8, 9, 10], [11]]
Nir Alfasi
  • 53,191
  • 11
  • 86
  • 129