Multidimensional and Jagged Arrays in Java
Java has no true two dimensional array. It has arrays of arrays, which is what makes jagged shapes possible.
-
Java Basics
- Introduction to Java
- Setting Up Java and Writing Your First Program
- Variables, Data Types and Literals in Java
- Type Casting and Type Conversion in Java
- Operators and Expressions in Java
- Input and Output in Java
- Comments, Keywords and Naming Conventions in Java
- Control Flow in Java: if, else and switch
- Loops in Java: for, while and do-while
- Methods
- Arrays and Strings
-
OOP
- Classes and Objects in Java
- Constructors in Java
- The this Keyword in Java
- The static Keyword in Java
- Encapsulation in Java
- Access Modifiers in Java
- Inheritance in Java
- Method Overriding and super in Java
- Polymorphism in Java
- Abstraction, Abstract Classes and Interfaces in Java
- Composition, Aggregation and Association in Java
- The Object Lifecycle in Java
- Core Java
- Exception Handling
-
Collections
- The Java Collections Framework
- List in Java: ArrayList, LinkedList, Vector and Stack
- Set in Java: HashSet, LinkedHashSet and TreeSet
- Map in Java: HashMap, LinkedHashMap and TreeMap
- How HashMap Works Internally in Java
- Queue and Deque in Java: ArrayDeque and PriorityQueue
- Iterators in Java
- Comparable and Comparator in Java
- Collections Utilities and Choosing the Right Collection
- Generics
- Java 8+
- Stream API
- Date and Time
- File and I/O
-
Multithreading
- Threads in Java: Processes, Runnable and Thread
- Thread Lifecycle in Java
- Synchronization in Java: synchronized and volatile
- Locks and Atomic Classes in Java
- Race Conditions and Deadlocks in Java
- The Executor Framework and Thread Pools in Java
- Future and CompletableFuture in Java
- Concurrent Collections in Java
- The Java Memory Model
- JVM and Memory
- Advanced Java
- Networking
- JDBC
- Testing
The key idea
Java does not have a rectangular multidimensional array type. A int[][] is an array whose elements are themselves arrays. Every rule about arrays applies at each level, and that single fact explains all the behaviour below.
Declaring and creating
int[][] grid = new int[3][4]; // 3 rows, each row a new int[4]
int[][] table = {
{1, 2, 3},
{4, 5, 6}
};
System.out.println(table.length); // 2 - number of rows
System.out.println(table[0].length); // 3 - length of the first rowTraversing
int[][] sales = {
{120, 340, 210},
{90, 150, 400}
};
int total = 0;
for (int row = 0; row < sales.length; row++) {
for (int col = 0; col < sales[row].length; col++) { // per row length
total += sales[row][col];
}
}
for (int[] row : sales) { // the enhanced form nests the same way
for (int value : row) {
total += value;
}
}Always usesales[row].lengthfor the inner bound rather thansales[0].length. The rows are independent arrays and may differ in length.
Jagged arrays
int[][] triangle = new int[4][]; // rows allocated, columns not
for (int row = 0; row < triangle.length; row++) {
triangle[row] = new int[row + 1]; // each row a different length
for (int col = 0; col <= row; col++) {
triangle[row][col] = (col == 0 || col == row) ? 1 : 0;
}
}Leaving the second dimension empty is legal and creates an array of null references, each waiting for its own row. This is exactly why the shape can be irregular.
Printing
import java.util.Arrays;
int[][] table = {{1, 2}, {3, 4}};
System.out.println(Arrays.toString(table)); // shows two array references
System.out.println(Arrays.deepToString(table)); // [[1, 2], [3, 4]]The same distinction applies to comparison: Arrays.equals compares the top level references, while Arrays.deepEquals compares the contents at every level.
A worked example: matrix addition
public static int[][] add(int[][] a, int[][] b) {
if (a.length != b.length) {
throw new IllegalArgumentException("Row counts differ");
}
int[][] result = new int[a.length][];
for (int row = 0; row < a.length; row++) {
if (a[row].length != b[row].length) {
throw new IllegalArgumentException("Row " + row + " lengths differ");
}
result[row] = new int[a[row].length];
for (int col = 0; col < a[row].length; col++) {
result[row][col] = a[row][col] + b[row][col];
}
}
return result;
}Three dimensions and beyond
int[][][] cube = new int[2][3][4];
System.out.println(cube[1][2][3]); // 0The nesting continues indefinitely, but beyond two dimensions a small class or record usually models the data better than an index soup.
Common mistakes
- Using
grid[0].lengthas the inner bound on a jagged array and reading out of range. - Swapping the row and column index.
grid[row][col]is the conventional order. - Forgetting to allocate the inner arrays after
new int[4][], which gives aNullPointerException. - Using
Arrays.toStringwheredeepToStringwas needed. - Copying with
Arrays.copyOfand assuming the rows were copied too. They are shared, because only the outer array is duplicated.
Best practices
- Name the loop variables
rowandcolrather thaniandjwhen the meaning is positional. - Validate the shape at the start of a method that takes a matrix.
- For a genuinely rectangular grid, consider a single flat array with computed indices, which is friendlier to the cache.
- Prefer a record or a small class once each cell means more than a number.
Practice
- Build a jagged array where row
nholds the firstnmultiples of 3, and print it withdeepToString. - Write a method that transposes a rectangular
int[][]. - Why does
new int[3][]compile whilenew int[][3]does not? - Predict the output of
Arrays.equals(a, b)for two separate arrays with identical contents at both levels, and then ofArrays.deepEquals(a, b). - Write a method that returns the sum of the main diagonal of a square matrix, validating that it is square.
Conclusion
Treat every level as an ordinary array and multidimensional arrays lose their mystery. Rows are independent objects, which is why lengths can differ and why deep copies and deep comparisons need their own methods.