-1

I'd like to split an array into n roughly equal sized chunks, without knowing how large these chunks will be beforehand.

Using Numpy, this can be done with array_split:

>>> import numpy
>>> x = [7, 3, 9, 10, 5, 6, 8, 13]
>>> x
[7, 3, 9, 10, 5, 6, 8, 13]
>>> numpy.array_split(x, 3)
[array([7, 3, 9]), array([10,  5,  6]), array([ 8, 13])]

What's the Java equivalent of doing this? I'm happy to use a library function if available.

TMOTTM
  • 3,286
  • 6
  • 32
  • 63
  • Have you looked it up? A quick search gives you tons of examples and some also mention Guava's [`Lists.partition`](https://guava.dev/releases/31.1-jre/api/docs/com/google/common/collect/Lists.html#partition(java.util.List,int)) – QBrute Nov 06 '22 at 11:48
  • Perhaps this helps you https://stackoverflow.com/questions/27857011/how-to-split-a-string-array-into-small-chunk-arrays-in-java – office.aizaz Nov 06 '22 at 11:50
  • @QBrute Of course, and I'm surprised `Lists.partitions` hasn't shown up so far. – TMOTTM Nov 06 '22 at 11:52
  • @office.aizaz The problem with this approach is, that it needs me to know the chunksize before hand. – TMOTTM Nov 06 '22 at 11:53
  • @TMOTTM you know, if you have number of chunks you can calculate chunk size from list size – Sharon Ben Asher Nov 06 '22 at 11:59
  • @SharonBenAsher Sure, but it just feels as if there should be a library function accepting the list and number of chunks. Now I'm doing `Math.ceil(x.size() / (double) nChunks)`, which I find awfully low-level. – TMOTTM Nov 06 '22 at 12:02
  • Split an array into multiple sub-arrays. From the numpy documentation: "...For an array of length l that should be split into n sections, it returns l % n sub-arrays of size l//n + 1 and the rest of size l//n." I might try it like that – TMOTTM Nov 06 '22 at 12:06

2 Answers2

0

If it helps anyone.

@Test
public void testListPartition() {
    // Given
    Integer[] is = new Integer[] {7, 3, 9, 10, 5, 6, 8, 13};
    List<Integer> isList = new ArrayList<Integer>();
    Collections.addAll(isList, is);

    int nThreads = 3;
    int sizeSublist = (int) Math.ceil(isList.size()/(double)nThreads);

    List<Integer> truthOne = new ArrayList<Integer>();
    Collections.addAll(truthOne, new Integer[] {7, 3, 9});

    List<Integer> truthTwo = new ArrayList<Integer>();
    Collections.addAll(truthTwo, new Integer[] {10, 5, 6});

    List<Integer> truthThree = new ArrayList<Integer>();
    Collections.addAll(truthThree, new Integer[] {8, 13});

    // When
    List<List<Integer>> partitions = Lists.partition(isList, sizeSublist);

    // Then
    assertEquals(truthOne, partitions.get(0));
    assertEquals(truthTwo, partitions.get(1));
    assertEquals(truthThree, partitions.get(2));
}
TMOTTM
  • 3,286
  • 6
  • 32
  • 63
0

You can split the list using Stream.

static <T> List<List<T>> splitList(List<T> list, int n) {
    int size = list.size();
    return IntStream.range(0, (size + n - 1) / n)
        .map(i -> n * i)
        .mapToObj(i -> list.subList(i, Math.min(i + n, size)))
        .collect(Collectors.toList());
}

Note that since this uses subList(), each split list shares the original list.

You can split the array as well.

@SuppressWarnings("unchecked")
static <T> T[][] splitArray(T[] array, int n) {
    int size = array.length;
    return IntStream.range(0, (size + n - 1) / n)
        .map(i -> n * i)
        .mapToObj(i -> Arrays.copyOfRange(array, i, Math.min(i + n, size)))
        .toArray(i -> (T[][])Array.newInstance(array.getClass(), n));
}

However, this cannot be applied to arrays of primitive types.

Separate implementations are required for primitive types.

For example, for int array:

static  int[][] splitArray(int[] array, int n) {
    int size = array.length;
    return IntStream.range(0, (size + n - 1) / n)
        .map(i -> n * i)
        .mapToObj(i -> Arrays.copyOfRange(array, i, Math.min(i + n, size)))
        .toArray(int[][]::new);
}

test:

public static void main(String[] args) {
    List<Integer> list = List.of(7, 3, 9, 10, 5, 6, 8, 13);
    System.out.println(splitList(list, 3));
    Integer[] array = {7, 3, 9, 10, 5, 6, 8, 13};
    System.out.println(Arrays.deepToString(splitArray(array, 3)));
    int[] ints = {7, 3, 9, 10, 5, 6, 8, 13};
    System.out.println(Arrays.deepToString(splitArray(ints, 3)));
}

output:

[[7, 3, 9], [10, 5, 6], [8, 13]]
[[7, 3, 9], [10, 5, 6], [8, 13]]
[[7, 3, 9], [10, 5, 6], [8, 13]]