2D Arrays

The goal of this lab period is to practice using 2D arrays in Java.


Tasks

  1. Setup

    Create a working directory for today's lab. Then start a Java file called Grid.java and make a public class called Grid.

  2. Explore Command-line Parameters

    Create a main function in the Grid class. The args parameter of the main() function contains strings from the command line. Use a for loop to print out all of the elements of the args array. Then run your Grid class a few different times with additional strings on the command line. For example, try the following:

    $ java Grid 42 96 81 hut hut

    Note what your program displays. Change around the command-line arguments and see the effect. What data type are the command line parameters?

  3. Usage Statements

    Many programs you will write will require command line arguments in order to work properly. To be kind to other people using your program you should always check if the user provided enough arguments and print out a usage statement if they don't. A usage statement prints out an example of how to run the program, a description of what it does, and what the arguments mean. For example, if you type java on the command line with no arguments, it prints out the following.

        Usage: java [options]  [args...]
            (to execute a class)
    or  java [options] -jar  [args...]
            (to execute a jar file)
    or  java [options] -m [/] [args...]
        java [options] --module [/] [args...]
            (to execute the main class in a module)
    or  java [options]  [args]
            (to execute a single source-file program)
    ...

    In your main function, before doing anything else, test if the user provided at least one command line argument. If they did not, then print out a usage statement and return. Test your function with no arguments and with one or more arguments.

  4. Creating a README File

    Usage statements are one way to communicate with users about how to run your programs. README files are another. You should include both in all of your projects so that your project grader knows how to interact with your code! This is especially important for extensions, where you are making more design choices about how your program works. Without a README, we may not be able to replicate your results and give you credit for your work!

    So how do you write a README file? There are lots of conventions for how to do this, but for our purposes, we will focus on 3 main things:

    • Every project should include a file named README.txt
    • In that file, tell us which file(s) to run to replicate your important results
    • Tell us what command line arguments your program takes and how to use them
    Start by creating a README file in your lab folder. We'll come by and read this to see how to run your program for the lab checkpoint (once you've reached that step at the end of the lab).

  5. Creating a 2-D Array

    Create a 2D array of type Integer. I'll call it grid.

    Integer[][] grid;

    Start by allocating the 2D array in a single new statement, giving it 3 rows and 5 columns.

    grid = new Integer[3][5];

    If you try to print out grid, what happens? Presumably, you'll get something like Integer@somegarbage. Unfortunately, Java Arrays don't natively implement a 'pretty' toString, so you have to loop through Arrays yourself to print thing out nicely. As a follow-up, if you try to print out grid[0][0], what gets printed out?

    Now let's try the alternative of allocating the row size and column size separately:

    grid = new Integer[3][]; 
    for(int i = 0; i < 3; i++){
        grid[i] = new Integer[i+3];
    } 

    Notice that by this method, the rows of grid are actually different lengths. However, be a little careful with this method: if you try to print out grid[0][0] between the instantiation of grid and the start of the for loop, what happens?

    To fix the null entries of grid, regardless of which way you instantiated the object, you have to go through every location in the grid and create an Integer object to put in it. Do that now and giving each location in the grid a random Integer (we're leaving design choices about what kind of random number up to you).

    + (More Detail)

    When working with 2-D arrays we often use nested for loops:

    for(int i=0;i<rows;i++) {
      for(int j=0;j<cols;j++) {
         /* access the elements of the array using i and j as index variables */
      }
    }

    Remember that you have to use different names for the nested loop variables. For almost all 2-D arrays, you want to loop over rows in the outer loop and columns in the inner loop. The reason is that most 2-D arrays are stored in row-major order in memory, so looping over rows and then columns accesses memory in linear order, which is faster than jumping around.

    When creating a 2-D array, you have to first allocate the 2-D grid of Integer references, then use a nested loop, as above, to allocate each Integer element grid[i][j] individually. Assign to each grid[i][j] a new Integer by sampling a random number (you can refer back to last week for more instructions on how to do this).

  6. Print the Contents of the 2-D Array

    Print out the contents of the 2D array as a 2D grid using a nested for loop. Make it look nice. As part of the print process, print out the number of rows and columns in the grid, querying the array for its length. Hint: Test using the .length field of arrays to retrieve both the number of rows and the number of columns, try thinking of a 2D array as an "array of arrays."

    + (More Detail)

    If you have a 2-D array such as Integer[][] grid;, you can get the number of rows by using the expression grid.length. You can get the number of columns of row i by using grid[i].length. You can use these in the for loop conditions so the code adapts to the number of rows and columns in the 2-D array, whatever size it is.

    The function System.out.print does not put a newline character after the string to print. The function System.out.println() does put a newline character. When printing the contents of a 2-D array, it's nice to keep all the elements of a row on the same line.

    If you want control over how Java formats numbers, especially floating point numers, look at System.out.printf and look up printf formatting codes.

  7. Checking if two matrices are equal

    Try running the following code:

    int[][] arr1 = new int[2][2];
    int[][] arr2 = new int[2][2];
    int[][] arr3;
    for(int i = 0; i < 2; i++){
        for(int j = 0; j < 2; j++){
            arr1[i][j] = i+j;
            arr2[i][j] = i+j;
        }
    } arr3 = arr1;
    System.out.println(arr1 == arr2);
    System.out.println(arr1 == arr3);
    Obviously, all of these arrays are the same, but Java says that the first two are not equal. This is because the == operator tests memory location rather than if the two objects are actually the same. Since arr3 was instantiated by referencing arr1's literal reference, arr1 == arr3 according to Java.

    Create a method that checks if two grids have the same contents with the following declaration: public static boolean gridEquals(int[][] arr1, int[][] arr2). Create some good tests to confirm your code works. Your method should work even if your inputs aren't the same size!

  8. Rotating a matrix

    For the next tasl, create a function with the following declaration: public static int[][] rotate(int[][] arr). This function should rotate a grid by 90 degrees clockwise. For example, the grid

    1 2
    3 4
    5 6
    would return
    5 3 1
    6 4 2
    
    After creating it, use your above gridEquals method to create some tests that it's working. Note that this method should still work even if the grid isn't a square; however, you can and should assume the grid is rectangular.

  9. Adding command line arguments

    Add at least 2 command line arguments to your program that change its behavior and update your README to tell us how to use them. What you do with these is up to you (surprise us!), but some ideas include using command line parameters to:

    • Specify the size (number of rows and/or number of columns) of the grid
    • Specify the range of random numbers to sample grid values within
    • Create a boolean flag that dictates whether or not your program should rotate the matrix
    • Create a boolean flag that dictates whether the grid entries should be positive or negative
    • Pass in actual values for the grid instead of randomly generating them

    Lab checkpoint: Call us over and show us your README file. Have us try to run your program (don't tell us anything not in the README). Were we able to use the command line arguments correctly? If so you've passed the checkpoint!


When you are done with the lab, go ahead and get started on the current project.