Multidimensional arrays in Java
What's an array? It is a collection that contains and structures different kinds of data in individual cells—a one-dimensional array stores other elements: numbers, text, or items. A multidimensional (or n-dimensional array) contains arrays of arrays (matrices). To understand multidimensional arrays better, let's look at one-dimensional ones and stream first.
In Java programming, the simplest arrays are one-dimensional (1d) or two-dimensional arrays (2d), but they can have more dimensions. Arrays can be three-dimensional (3d), 4d, 5d, or more. Multidimensional can be used for vectors or table-oriented data. You can also use it for methods returning more than one array or a board in the tic-tac-toe game.
In Java, each field can contain only one type of data. You can’t make an array containing numbers and text simultaneously. However, each subarray can have different sizes.
We would focus primarily on 1-, 2-, and 3d arrays in the following examples.
Creating arrays
You can create an array by specifying all sizes without data. 1D would have only one size. In that situation, fields are filled with default values. Each data type has its default values:
- 0 for int
- 0,0 for double
- ‘\u0000’ for chars
- null for String, custom objects, nullable objects
You don’t have to write the whole type (for instance, int[][]). Instead, you can use the var keyword. This keyword was introduced in Java 10. It can be helpful for an array with a long name data type, for instance, custom objects or classes.
In the example above, we have created one-dimensional, three 2d arrays, and one 3d arrays. You can see below what it contains.
For a one-dimensional empty array, you must indicate the size. However, you don’t have to write all sizes for multidimensional ones. Especially when you would like to have subarrays in different sizes. You have to specify at least one size, but not all. In that situation, fields would be filled with nulls. It doesn’t matter which type of data. Even a double array would get null.
In the example above, we are creating two arrays that would contain only nulls. If you try to get “nullNumbers[0][0]”, you will get NullPointerException. It’s because nullNumbers contains a null array.
You can also create an array with specified data. Specify the type, for example, int[][]. However, you can use the var keyword, but in this case, add a type next to data, for example, new int[][]. And also, subarrays can have different sizes. As you can see in the numbers array below, the first has two array elements, the second has three, and the last contains four.
Accessing data
You can access every field or whole subarray. You need indexes to get a specific field. Remember that indexes start on 0 in Java. The table below displays fields from the texts array. It is a 1D, so we need only one index to access the field.
texts[0]= ”test”
texts[1] = “sth”
texts[2] = “here”
The next table displays what the a array contains.
a[0][0] = 0
a[0][1] = 1
a[0][2] = 2
a[1][0] = 3
a[1][1] = 4
a[1][2] = 5
a[2][0] = 6
a[2][1] = 7
a[2][2] = 8
We need two indexes to get the field. The bigger the array dimension, the more complex the array indexing you need. For a 3-dimensional array, you would need 3, and 4d would require 4. If we use only one index for the a array, we will get the row size of a one-dimensional array. That’s why “a[0]” equals {0, 1, 2}.
The above code will output the following numbers. 3 is the value from the field. And 2 is the subarray’s size.
You must be very careful when accessing the field or subarrays. You will get an ArrayIndexOutOfBoundsException when you use an incorrect index. It can be a number less than 0, more prominent, or equal to the array size.
Loops and streams
You would need only one loop or stream to access one-dimensional array values.
For 2d arrays, you need two loops. For 3d one, more loops are required. The more dimensions an array contains, the more loops are needed. However, a stream could also be helpful. This will shorten the code.
In loops, we have used the length method. The code is more transparent and will work for different size subarrays.
Output
For streams, we use the forEach method to execute code for every element/subarray in the array. Many methods can help you map or filter data in it.
Output
You can transfer arrays to a stream when you want more methods to manipulate arrays. For the example above, we use the forEach method to execute code for every element/subarray in the array.
like this
Updating data
You can update a specific field, a subarray, or the whole array. Updated ones can have different sizes.
Output
Now, we can update the 2d array. It looks similar to a 1d array. Updated arrays can have different sizes.
Output
In the example above, you can see the final keyword. The whole array can be final even if you update a field or subarray. You can’t replace the whole final one, though. You must delete the final keyword to replace the entire array.
Copy array
Arrays in Java are reference types. It means that the variable is not an actual array but an address to it. That’s why assigning wouldn’t be enough to copy the whole array. As you can see in the previous example, updates to the table variable were also applied to the numbers variable. It works similarly to a 1d array.
Output
As you can see in newNumbers, we have updated the field with Arrays.copyOf. This method has two arguments. The first is an array that we want to copy. The second one is the length of our new array. That’s what we can do when we want to change the size of our array. We have to copy it to the array with a different size. The lessNumber is shorter and doesn’t contain the last field. MoreNumbers has two extra fields at the end filled by default values.
Let’s try to copy the 2d array. Use two different methods.
As you can see, all arrays have been updated. That’s why we need to copy each subarray separately.
You can use a loop for it. First, you must create an empty array with a specified first size. Then you would go through every subarray and copy it. You can use the clone or copyOf method. This would prevent updating the array assigned to the copy variable.
As you can see, only the first array has been updated. That means that we were able to copy the array successfully.
You can also use stream instead of loop. It will shorten the code, but you can't ignore the readability.
Output
This works perfectly for primitive data, like numbers or text. However, it wouldn’t work so well for custom objects. In that situation, you must go through every object and copy them.
Output
As you can see in cloneSamples, we haven't copied each object separately. As a result, updating the object affects both arrays: samples and cloneSamples. In cloneBSamples, we have created a perfect copy. We copied each object separately and didn’t have the updated object.
Sorting data
First, let’s look at how we can sort a 1d array.
Output
Sorting a multidimensional array would look very similar. You can easily sort by subarrays. You can use an existing comparator, as in the example below, or write your comparator.
This is how the array would look after first sorting. We sorted only the first subarray, not the whole array.
Then, we sorted the whole array by the subarray size.
The array would be updated after the subsequent sorting. We sorted by a sum of all subarrays’ elements.
Conclusion
In this article, we have shown you how to create, update, and copy arrays. Many operations on multidimensional arrays are similar to 1d arrays. Remember, you should be very careful when you try to copy an array. It can be very tricky because of the reference nature of arrays. Loops and streams can be beneficial.