简体   繁体   中英

Iterating a 2D Array using Java 8

This is an implementation of the 0-1 Knapsack Problem. The problem statement goes like this,

You're given two arrays, one containing the weights of a set of items and the other containing values for the respective weights. You're provided a max weight. Within the constraint of staying under the max weight, determine the maximum value that you can obtain by selecting or not selecting the set of items. The values and the weight list will always be of the same size.

This is my solution, which generally works fine(not on edge cases).

  public static int getCombination(int[] weights, int[] values, int maxWeight){

        int[][] memo = new int[weights.length][maxWeight + 1];
        for (int i = 0; i < memo.length; i++) {
          for(int j=0; j < memo[i].length; j++){
            if(j == 0){
              memo[i][j] = 0;
            }else if(weights[i] > j){
              if(i == 0) memo[i][j] = 0;
              else memo[i][j] = memo[i-1][j];
            }else{
              if(i == 0){
                memo[i][j] = values[i];
              }
              else{
                memo[i][j] = Integer.max((values[i] + memo[i-1][j- weights[i]]), memo[i-1][j]);
              }
            }
          }
        }
        return memo[weights.length -1][maxWeight];
      }

Now I want to re-write this complete logic in a declarative manner using Java 8 Streams and lambdas. Can someone help me with that.

Since your for loop based solution is completely fine, streams do not add much value here if you only convert the for loops to forEach streams.

You get more out of using streams, if you use IntStream and the toArray method, because you can concentrate on calculating the value based on row and column index, and not care about filling it into the array.

int[][] memo = IntStream.range(0, rows)
                        .mapToObj(r -> IntStream.range(0, cols)
                                                .map(c -> computeValue(r, c))
                                                .toArray())
                        .toArray(int[rows][cols]::new);

Here, we create an array for each row, and then put those into a 2D-array at the end. As you can see, the toArray() methods take care of filling the arrays.


Actually, now that I looked at your method to calculate the values more closely, I realize that streams might be difficult if not impossible to use in this case. The problem is that you need values from previous columns and rows to calculate the current value. This is not possible in my solution precisely because we only create the arrays at the end. More specifically, my approach is stateless, ie you do not remember the result of previous iterations.

You could see if you can use Stream.reduce() to achieve your goal instead.

BTW, your approach is fine. If you don't want to parallelize this, you are good to go.

Here's a possible starting point to create the indices into your array:

        int rows = 3;
        int cols = 4;
        int[][] memo = new int[rows][cols];
        IntStream.range(0, rows * cols).forEach(n -> {
            int i = n / cols;
            int j = n % cols;

            System.out.println("(" + i + "," + j + ")");
        });

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