简体   繁体   中英

How to initialize two dimensional array in java 8

Before Java 8 versions, we can initialize a two-dimensional array using for loop like below. How can I do the below in JDK 1.8 using lambda expressions?

int[][] board = new int[3][3];
int v = 1;
for (int i = 0; i < board.length; i++) {

    for (int j = 0; j < 3; j++) {

        board[i][j] = v;
        v++;
        System.out.println(v);
    }
}
System.out.println(Arrays.deepToString(board));

Basically, I am looking for an array like below

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

Use IntStream . This will create a continuous stream of integers from 0 inclusive to n exclusive. So for a 3 x 3 matrix, the IntStream is 0, 1, 2 . make another IntStream for each integer in the outer stream from 0 to number of columns - also 3.

You cannot increment v in a stream because it must be "effectively final". Instead we use the equation board[j][i] = 1 + (j + m * i) which is effectively the similar to computing the index of the value if you were to flatten board into a single array (1D matrix).

import java.util.stream.IntStream;
import java.util.Arrays;

class J {

  public static void main(String[] args) {

    int n = 4;
    int m = 2;

    // n x m matrix
    int[][] board = new int[n][m];

    // Cols (m)
    IntStream.range(0, m).forEach( i -> {

      // Rows (n)
      IntStream.range(0, n).forEach( j -> {

        // Multiply i by row count
        // to account for the row we are in
        board[j][i] = 1 + (j + n * i);
      });


    });

    System.out.println(Arrays.deepToString(board));

  }

}

Output:

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

Note: Just because streams allow you to write a neater, functional programming-like syntax, there is often times an associated performance toll. It stresses the idea, "why fix what's not broken?". For a 3 x 3 board, you probably won't see a difference. But, if your board were a lot larger, it probably won't prove itself to be worth it, considering all the objects created and extra space used behind the scene. Sometimes a simple for-loop (especially when working with arrays) is better.

Remember, simplicity is key.

I highly recommend you stick with using the for-loop for initialization of arrays with primitive values. My recommendation is stronger in the case of a multidimensional array.

However, here is the way:

int a = 4;               // Number of outer array elements
int b = 2;               // Number of inner array elements

int[][] board = IntStream
    .range(0, a)                                               // iterate 0..3
    .mapToObj(i -> IntStream.range(0, b)                       // for each iteratoe 0..1
                            .map(j -> 1 + (i + (a + 1) * j))   // calculate the value
                            .toArray())                        // compose the inner array
    .toArray(int[][]::new);                                    // compose the outer array

Note that the IntStream is able to create an array of primitives since it is a sequence of primitive int-valued elements. its method IntStream::toArray reutrns int[] .

The composition of the outer array is a bit tricky since int[] is no longer a primitive value but an array itself. There is needed to use a method IntStream::mapToObj which maps int to an object - then the Stream<int[]> is returned and the method Stream::toArray(IntFunction<A[]> generator) converting to array with parameter has to be used since you cannot convert Object[] to int[][] .

The parameter passed is simple. int[][]::new is nothing different than i -> new int[a][b] .

You can use a combination of the IntStream methods range and iterate :

int width = 4, height = 2;

IntStream.rangeClosed(1, width)
         .mapToObj(column -> IntStream.iterate(column, v -> v + width)
                                      .limit(height)
                                      .toArray())
         .toArray(int[][]::new);

By using iterate for the inner loop and rangeClosed(1, width) instead of range(0, width) we can simplify the calcuation a bit. Using toArray removes the need to create and modify the array yourself and would enable parallel processing.

It is important to actually use streams properly to make them more than just weird looking for loops. Merely replacing your loop by a range(0, x).forEach(...) to modify a local variable does not really "use streams", and it is better to stick to for loops then.

Here is a stream solution that is "nested for cycles" look a like.

  1. Get the sequence of the numbers from 0 to N(Upper limit exclusive -> N = board.length;
  2. Get the sequence of the numbers from 0 to M(Upper limit exclusive -> M = board[i].length;
  3. For each of the couples (i, j) set the value of board[i][j] using the func (i + j * step + 1) where step is defined as 5.

Output for int[][] board = new int[4][2];

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

Output for int[][] board = new int[4][3];

[[1, 6, 11], [2, 7, 12], [3, 8, 13], [4, 9, 14]]

int step = 5;

IntStream.range(0, board.length).forEach(i ->
    IntStream.range(0, board[i].length).forEach(j ->
        board[i][j] = i + j * step + 1
    )
);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM