What is the most efficient way to create a 2d string array of initally repetitive data?

Viewed 170

In Python, typically to define said game board I would have used the following code:

board = [['0','0','0','0','0','0','0','0','0','0'] for i in range(10)]

However, using Java the only way I have been able to find so far to produce the same result would be to do:

public static String[][] board = {
        {"0","0","0","0","0","0","0","0","0","0"},
        {"0","0","0","0","0","0","0","0","0","0"},
        {"0","0","0","0","0","0","0","0","0","0"},
        {"0","0","0","0","0","0","0","0","0","0"},
        {"0","0","0","0","0","0","0","0","0","0"},
        {"0","0","0","0","0","0","0","0","0","0"},
        {"0","0","0","0","0","0","0","0","0","0"},
        {"0","0","0","0","0","0","0","0","0","0"},
        {"0","0","0","0","0","0","0","0","0","0"},
        {"0","0","0","0","0","0","0","0","0","0"}};

Surely there must be a more efficient way of doing this. I know I can do it efficiently if it were to be integers, but it has to be strings given the situation. I had thought a for loop would also have applied, but I can't seem to find the right syntax for it; perhaps it isn't applicable for Java. Are there any suggestions?

6 Answers

You can loop over each row and use Arrays.fill:

public static String[][] board = new String[rows][cols];
static {
    for (String[] row : board) Arrays.fill(row, "0");
}

Demo

You can use nested for loops to create the 10 rows and 10 columns. So within the first loop (which creates a "row"), you'd add another loop to fill the "columns."

String[][] board = new String[10][10];

// 10 rows
for (int i = 0; i < 10; i++) {
    // 10 columns
    for (int j = 0; j < 10; j++) {
        // Set the value of this row/column
        board[i][j] = "0";
    }
}

This obviously isn't as elegant as Python's one-line solution, but it does what you need.

There are other ways to do this (such as just adding a full 10-digit array to the board array in each loop), but I find this to be best for scalability and readability.


For further clarity, if you may possibly need to change the number of rows or columns in the future, it would be better to move those values to variables and use those instead:

int rows = 10;
int cols = 10;

String[][] board = new String[rows][cols];

for (int i = 0; i < rows; i++) {
    for (int j = 0; j < cols; i++) {
        board[i][j] = "0";
    }
}

This way you only need to update the variables instead of also modifying the for loops and array declaration.

You can do it like this. Since Strings are immutable, it's safe to use clone() here. Thanks to @iota for suggesting setAll

int n = 10;
String[][] arr = new String[n][n];
Arrays.fill(arr[0], "0");
Arrays.setAll(arr, i->arr[0].clone());


for(String[] a : arr) {
    System.out.println(Arrays.toString(a));
}

Prints

[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

This is a hybrid of for loop and manually creating one row of cells.

String[][] board = new String[10][];
for (int i = 0; i < 10; i++) {
    board[i] = new String[]{"0","0","0","0","0","0","0","0","0","0"};
}

The following is a stream-based solution to the problem that uses a functional style, rather than an iterative style:

return Stream.generate(() -> 
                Collections.nCopies(10, "0").toArray(String[]::new))
        .limit(10)
        .toArray(String[][]::new);

Note that depending on the readability, etc. requirements, a classic iterative style may be deemed more desirable.

Explanation

Collections.nCopies(10, "0") creates a List<String> of 10 "0" values. This is converted to a String array using Collection.toArray(IntFunction). This takes an IntFunction which generates an array of a given length. String[]::new is a constructor lambda which implements IntFunction<String[]>, creating an array of the given length.

Stream.generate creates an infinite stream with each element created by a call to a given Supplier. In this case, the supplier creates the aforementioned 10-element long list, resulting in a Stream<String[]>. A generator is used, rather than Collections.nCopies, since each String[] in the String[][] needs be a different object.

The infinite stream is limited to just ten elements using Stream.limit(10).

Finally, the Stream<String[]> is collected into a String[][] by using Stream.toArray(IntFunction), which works in the same manner as the Collection.toArray method already described.

String#repeat since 11

String[][] board = new String[10][];
  1. Arrays.setAll - for each row repeat 0 ten times and split into an array:
    Arrays.setAll(board, i -> "0".repeat(10).split(""));
    
  2. Arrays.fill - in this case, the result is the same:
    Arrays.fill(board, "0".repeat(10).split(""));
    

Output:

Arrays.stream(board).map(Arrays::toString).forEach(System.out::println);
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

See also: How can I fill this 2D array with X characters?

Related