Multidimensional Arrays

Su Suriya Ravichandran Updated 14 Nov 2026
7 min read ·Lesson 67 of 133

A multidimensional array stores data in more than one dimension: a table of rows and columns, a grid of cells, a stack of tables. Java implements these as arrays of arrays. A two-dimensional int[][] is an array whose elements are themselves int[] arrays. This design is flexible (rows can have different lengths), and understanding it explains how such arrays are created, indexed, and looped over.

Declaring and Creating a 2D Array

int[][] grid = new int[3][4];     // 3 rows, 4 columns, all 0

Read the type int[][] as "array of int[]". The expression new int[3][4] creates:

grid ──► [ row0, row1, row2 ]          an array of 3 references
           │     │     │
           ▼     ▼     ▼
         [0,0,0,0] [0,0,0,0] [0,0,0,0]  three separate int[4] arrays

There are four objects here: one outer array and three row arrays. The outer array holds references to the rows.

With an initializer

int[][] matrix = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};

Each inner brace group becomes one row. The dimensions are taken from the data.

Accessing Elements

Use one index per dimension: first the row, then the column.

int center = matrix[1][1];      // 5
matrix[2][0] = 70;              // row 2, column 0
int[] secondRow = matrix[1];    // the whole row: {4, 5, 6}

matrix[1] is an ordinary int[], so you can pass a row to any method that accepts a one-dimensional array:

System.out.println(Arrays.toString(matrix[1]));   // [4, 5, 6]

Each index is checked separately. matrix[3][0] fails on the row index; matrix[0][3] fails on the column index; both throw ArrayIndexOutOfBoundsException.

Dimensions: length at Each Level

int[][] grid = new int[3][4];
System.out.println(grid.length);      // 3: number of rows
System.out.println(grid[0].length);   // 4: columns in row 0

There is no single "column count" stored for the whole array. Each row knows its own length.

Looping Through a 2D Array

Nested loops visit every cell. The outer loop goes over rows; the inner loop goes over the columns of the current row:

int[][] matrix = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};

for (int r = 0; r < matrix.length; r++) {
    for (int c = 0; c < matrix[r].length; c++) {
        System.out.print(matrix[r][c] + " ");
    }
    System.out.println();
}

Output:

1 2 3 
4 5 6 
7 8 9 

The inner condition uses matrix[r].length, the length of the current row, so the same loop also works when rows differ in length.

When indexes are not needed, nested for-each loops are shorter:

int sum = 0;
for (int[] row : matrix) {
    for (int value : row) {
        sum += value;
    }
}
System.out.println(sum);   // 45

Printing a 2D Array

Arrays.toString prints only the references of the inner arrays. Use Arrays.deepToString for nested arrays:

System.out.println(Arrays.toString(matrix));
// [[I@7a81197d, [I@5ca881b5, [I@24d46ca6]

System.out.println(Arrays.deepToString(matrix));
// [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

Similarly, use Arrays.deepEquals to compare nested arrays by content.

Row-by-Row and Column-by-Column Operations

Summing a row uses one fixed row index; summing a column uses one fixed column index across all rows:

int[][] sales = {
    {120, 135, 150},     // region 0: Jan, Feb, Mar
    {200, 180, 210},     // region 1
    {90, 110, 95}        // region 2
};

int region1Total = 0;
for (int m = 0; m < sales[1].length; m++) {
    region1Total += sales[1][m];
}

int febTotal = 0;
for (int r = 0; r < sales.length; r++) {
    febTotal += sales[r][1];
}

System.out.println(region1Total + " " + febTotal);   // 590 425

Row operations are naturally fast in Java because each row is a contiguous array. Column operations jump between separate row arrays in memory. For large numeric work, looping row by row in the outer loop is noticeably faster than column by column.

Jagged Arrays

Because a 2D array is an array of arrays, rows can have different lengths. Create the outer array with only the first dimension, then allocate each row separately:

int[][] triangle = new int[5][];        // 5 rows, not yet allocated (all null)
for (int r = 0; r < triangle.length; r++) {
    triangle[r] = new int[r + 1];       // row r has r + 1 elements
    triangle[r][0] = 1;
    triangle[r][r] = 1;
    for (int c = 1; c < r; c++) {
        triangle[r][c] = triangle[r - 1][c - 1] + triangle[r - 1][c];
    }
}

for (int[] row : triangle) {
    System.out.println(Arrays.toString(row));
}

Output (Pascal's triangle):

[1]
[1, 1]
[1, 2, 1]
[1, 3, 3, 1]
[1, 4, 6, 4, 1]

new int[5][] creates the outer array with five null slots. Accessing triangle[r][0] before assigning triangle[r] would throw NullPointerException. Jagged arrays save memory when rows genuinely differ in size, such as a list of students per class, or the lower half of a symmetric distance table.

You can only leave out trailing dimensions: new int[5][] is legal, new int[][5] is not.

Copying a 2D Array

clone() and Arrays.copyOf on the outer array copy only the row references, so the copy shares its rows with the original:

int[][] original = {{1, 2}, {3, 4}};
int[][] shallow = original.clone();
shallow[0][0] = 99;
System.out.println(original[0][0]);      // 99: same row array

A deep copy must copy each row:

int[][] deep = new int[original.length][];
for (int r = 0; r < original.length; r++) {
    deep[r] = original[r].clone();
}
deep[0][0] = 1;
System.out.println(original[0][0]);      // still 99

Three or More Dimensions

The same idea extends to any number of dimensions:

// [building][floor][room]
int[][][] occupancy = new int[2][3][4];
occupancy[1][2][3] = 2;                   // building 1, floor 2, room 3 has 2 people

int total = 0;
for (int[][] building : occupancy) {
    for (int[] floor : building) {
        for (int room : floor) {
            total += room;
        }
    }
}
System.out.println(total);                // 2

Arrays beyond two or three dimensions become hard to read. When dimensions have meanings (building, floor, room), a small class or record hierarchy often expresses the data more clearly.

Common Uses

UseTypical shape
Tables of numbers (sales by region and month, marks by student and subject)int[][], double[][]
Game boards (tic-tac-toe, chess, Sudoku)char[][], int[][]
Images (pixel grids, one row per scan line)int[height][width]
Matrices in math and graphicsdouble[rows][cols]
Distance or adjacency tables between locationsint[n][n]

Example: a tic-tac-toe winner check

static boolean wins(char[][] b, char p) {
    for (int i = 0; i < 3; i++) {
        if (b[i][0] == p && b[i][1] == p && b[i][2] == p) return true;   // row i
        if (b[0][i] == p && b[1][i] == p && b[2][i] == p) return true;   // column i
    }
    return (b[0][0] == p && b[1][1] == p && b[2][2] == p)                // diagonals
        || (b[0][2] == p && b[1][1] == p && b[2][0] == p);
}

char[][] board = {
    {'X', 'O', 'X'},
    {'O', 'X', 'O'},
    {' ', ' ', 'X'}
};
System.out.println(wins(board, 'X'));   // true: main diagonal

Practical Example: Gradebook

import java.util.Arrays;

public class Gradebook {
    public static void main(String[] args) {
        String[] students = {"Ada", "Linus", "Grace"};
        String[] subjects = {"Java", "SQL", "Docker", "Bash"};
        int[][] marks = {
            {88, 92, 75, 81},
            {67, 74, 90, 58},
            {95, 85, 88, 92}
        };

        System.out.printf("%-8s", "");
        for (String sub : subjects) System.out.printf("%7s", sub);
        System.out.printf("%7s%n", "Avg");

        for (int r = 0; r < marks.length; r++) {
            System.out.printf("%-8s", students[r]);
            int sum = 0;
            for (int c = 0; c < marks[r].length; c++) {
                System.out.printf("%7d", marks[r][c]);
                sum += marks[r][c];
            }
            System.out.printf("%7.1f%n", (double) sum / marks[r].length);
        }

        System.out.printf("%-8s", "Top");
        for (int c = 0; c < subjects.length; c++) {
            int best = 0;
            for (int r = 1; r < marks.length; r++) {
                if (marks[r][c] > marks[best][c]) best = r;
            }
            System.out.printf("%7s", students[best]);
        }
        System.out.println();

        int[][] transposed = new int[subjects.length][students.length];
        for (int r = 0; r < marks.length; r++) {
            for (int c = 0; c < marks[r].length; c++) {
                transposed[c][r] = marks[r][c];
            }
        }
        System.out.println("By subject: " + Arrays.deepToString(transposed));
    }
}

Output:

           Java    SQL Docker   Bash    Avg
Ada          88     92     75     81   84.0
Linus        67     74     90     58   72.3
Grace        95     85     88     92   90.0
Top       Grace    Ada  Linus  Grace
By subject: [[88, 67, 95], [92, 74, 85], [75, 90, 88], [81, 58, 92]]

Rows represent students and columns represent subjects, with the parallel arrays students and subjects supplying the labels. The row loop computes each student's average by fixing r and moving across c. The "Top" line swaps the roles: it fixes a column c and moves down the rows to find the highest mark in that subject. The transpose creates a new array with the dimensions reversed ([subjects][students]) and copies marks[r][c] into transposed[c][r], turning a per-student table into a per-subject one.

0 Comments

Reviewed before they appear

No comments yet.

Java
Java Introduction Java Features Java Syntax Java Comments Java Statements Java Output Java Variables Java Data Types Java Type Casting Java Operators Java Strings Java Numbers Java Math Java Booleans Java User Input Java Conditions Java If Else Java Switch Java While Loop Java For Loop Java Break Java Continue Java Arrays Java Methods Java Parameters Java Return Values Java Scope Java Recursion Install Java Install JDK Java IDEs Java JDK Java JRE Java JVM Java Compiler Java File Compile Java Run Java JShell Basics Java Packages Java OOP Java Classes Java Objects Class Methods Constructors Constructor Overload this Keyword static Keyword final Keyword Encapsulation Inheritance Polymorphism Abstraction Access Modifiers super Keyword Method Overload Method Override Instanceof Operator Nested Classes Inner Classes Anonymous Classes Enums Records Array Basics Array Length Loop Arrays Multidimensional Arrays Array Copy Array Sorting Array Search ArrayList LinkedList HashSet TreeSet HashMap TreeMap Queue Interface Deque Interface Stack Class Iterator List Interface Set Interface Map Interface Collections Class Comparable Comparator Generics Basics Generic Classes Generic Methods Wildcards String Basics String Length String Methods String Concatenation String Compare String Search String Replace String Split String Format StringBuilder StringBuffer Text Blocks Regular Expressions Number Types Integer Class Float Class Double Class BigInteger BigDecimal Math Class Random Numbers Number Format Date Basics LocalDate LocalTime LocalDateTime ZonedDateTime Date Formatting Date Parsing Period Class Duration Class Java Instant Class Java Exception Basics Java try...catch Java Multiple Catch Blocks Java finally Block Java throw Keyword Java throws Keyword Java Checked Exceptions Java Unchecked Exceptions Java Custom Exceptions Java try-with-resources Java Exception Chaining
Ask about this post
AI Ask about this post

Ask questions about Multidimensional Arrays and get answers drawn from it.

Signed-in readers only.