Hi! Previously in my articles, we've discussed single objects (and primitive types). But what if we need to work with a whole group of objects instead of just one?
For example, say we want to create a list of birthdays of all employees at our company.
It should contain 30 strings formatted as follows: "Sarah Huffman, January 25"
We'll benefit from a special data structure called an array.
If we compare an array to a real object, it is very similar to a bank vault with safety deposit boxes:
An array also consists of "boxes". You can put something (an element) into each box. To access an element, you need to know its box number (index).
This is how an array is created:
public class Main {
public static void main(String[] args) {
String [] birthdays = new String[10];
}
}
Here we create an array that holds 10 elements.
You can immediately note some features of the array:
It stores elements of a well-defined data type. If we create a String array, we can't store anything else in it. The data type is specified when the array is created. This is where it differs from a safety deposit box (in which a customer can store what he or she wants).
Its size must be specified when the array is created. You can't indicate it later or change its size after the array is created.
The fact that we are creating an array is indicated by the square brackets on both sides of the expression. They can be specified before or after the name of the reference variable. Either way will work:
String [] birthdays = new String[10];
String birthdays [] = new String[10];
If you want to write something into an array, you need to specify the index of the box where the value will be written. The boxes in an array are numbered starting from 0.
Counting starting from zero is a very common practice in programming. The faster you get used to it, the better :)
This means, if you want to put some value in the first box, you do this:
public class Main {
public static void main(String[] args) {
String birthdays [] = new String[10];
birthdays[0] = "Jana Russell, March 12";
}
}
Now Jana's birthday is stored in the first cell of our array of employee birthdays:
You can add other values in a similar manner:
public class Main {
public static void main(String[] args) {
String birthdays [] = new String[10];
birthdays[0] = "Jana Russell, March 12";
birthdays[1] = "Landon Chan, May 18";
birthdays[7] = "Rosie Mills, January 3";
}
}
Note that we added Rosie's birthday to the eighth box (you haven't forgotten why Box No. 7 is the eighth box, have you?). You can see that we haven't filled all the other cells. We don't have to write values into an array in order. There's no such requirement. Of course, writing elements in order makes it much easier to keep track of how many boxes are free and how many are occupied, and it prevents the array from having "holes".
If you want to get the contents of one of the boxes, then (just like with a safety deposit box) you need to know its number.
This is how it's done:
public class Main {
public static void main(String[] args) {
String birthdays [] = new String[10];
birthdays[0] = "Jana Russell, March 12";
birthdays[1] = "Landon Chan, May 18";
birthdays[7] = "Rosie Mills, January 3";
String rosieBirthday = birthdays[7];
System.out.println(rosieBirthday);
}
}
Console output:
Rosie Mills, January 3
We created a String
variable and told the compiler: "Find the box with index 7 in the birthdays array, and assign the value contained there to the String
variable rosieBirthday".
And that's exactly what it did.
When working with arrays, we can easily find their length using a special property: length.
public class Main {
public static void main(String[] args) {
String birthdays [] = new String[10];
birthdays[0] = "Jana Russell, March 12";
birthdays[1] = "Landon Chan, May 18";
birthdays[7] = "Rosie Mills, January 3";
int birthdaysLength = birthdays.length;
System.out.println(birthdaysLength);
}
}
Console output:
10
Note: The length
property stores the array size, not the number of boxes that are full. Our array only stores 3 values, but we indicated its size as 10 when we created it. And this is exactly the value that the length
field returns.
Why would this come in handy? Well, suppose you want to display a list of all the birthdays (to verify that no one is forgotten). You can do this in one simple loop:
public class Main {
public static void main(String[] args) {
String birthdays [] = new String[10];
birthdays[0] = "Jana Russell, March 12";
birthdays[1] = "Landon Chan, May 18";
birthdays[2] = "Jeremiah Leonard, July 12";
birthdays [3] = "Kenny Russo, September 7";
birthdays[4] = "Tommie Barnes, November 9";
birthdays [5] = "Roman Baranov, August 14";
birthdays [6] = "Chanice Andersen, April 1";
birthdays[7] = "Rosie Mills, January 3";
birthdays [8] = "Keenan West, October 19";
birthdays [9] = "Abraham McArthur, May 3";
for (int i = 0; i < birthdays.length; i++) {
System.out.println(birthdays[i]);
}
}
}
In the loop, we declare the variable i
, which is initialized to zero. At each pass, we get the element with index i from our array and display its value. The loop will do 10 iterations, and i will increase from 0 to 9—and numbers happen to be the indices of the elements of our array! As a result, we will display all values from birthdays[0] to birthdays[9]
Actually, there's another way you can create an array.
For example, you can create an array of int
s like this:
public class Main {
public static void main(String[] args) {
int numbers [] = {7, 12, 8, 4, 33, 79, 1, 16, 2};
}
}
This technique is called "shortcut initialization". It's quite convenient, because we simultaneously create an array and fill it with values. We don't have to explicitly specify the array size: with shortcut initialization, the length
field is set automatically.
public class Main {
public static void main(String[] args) {
int numbers [] = {7, 12, 8, 4, 33, 79, 1, 16, 2};
System.out.println(numbers.length);
}
}
Console output:
9
Now, a little bit about how arrays are stored in memory.
Let's say we have an array of three Cat
objects:
public class Cat {
private String name;
public Cat(String name) {
this.name = name;
}
public static void main(String[] args) {
Cat[] cats = new Cat[3];
cats[0] = new Cat("Thomas");
cats[1] = new Cat("Behemoth");
cats[2] = new Cat("Lionel Messi");
}
}
You need to understand a few things here:
In the case of primitives, an array stores a set of specific values (e.g.
int
s). In the case of objects, an array stores a set of references. Thecats
array consists of three elements, each of which is a reference to aCat
object. Each of the references points to the memory address where the corresponding object is stored.Array elements are arranged in a single block in memory. This is done to allow them to be accessed quickly and efficiently.
Thus, cats
references the block of memory where all the objects (array elements) are stored. Cats[0]
references a specific address within this block.
It's important to understand that an array doesn't just store objects: it is an object itself.
This leads us to question whether we can create not only array of strings or numbers, but also arrays of arrays.
And the answer is yes, we can! An array can store any objects, including other arrays.
Such an array is called two-dimensional. If we were to represent it visually, it will be very similar to an ordinary table.
Suppose, we want to create an array of 3 arrays that can each store 10 int
s. It would look like this:
Each line represents an int
array. The first array contains numbers from 1 to 10, the second array — from -1 to -10, and the third — a set of random numbers. Each of these arrays is stored in the boxes of our two-dimensional array.
In code, initialization of a two-dimensional array looks like this:
public static void main(String[] args) {
Cat[][] cats = new Cat[3][5];
}
Our two-dimensional array cats stores 3 arrays with 5 boxes in each array. If we want to put an object in the third box of the second array, we would do this:
public static void main(String[] args) {
Cat[][] cats = new Cat[3][5];
cats[1][2] = new Cat("Fluffy");
}
[1]
indicates the second array, and [2]
indicates the third box of that array.
Because a two-dimensional array consists of several arrays, in order to iterate through it and display all its values (or populate all its elements), we need a nested loop:
for (int i = 0; i < cats.length; i++) {
for (int j = 0; j < cats[i].length; j++) {
System.out.println(cats[i][j]);
}
}
In the outer loop (variable i
), we iterate over all the arrays in our two-dimensional array. In the inner loop (variable j
), we pass through all the elements of each array.
As a result, cats[0][0] (first array, first element) will be displayed first, followed by cats[0][1] (first array, second element). After we've gone through the first array, we'll display cats[1][0], cats[1][1], cats[1][2], etc.
By the way, two-dimensional arrays also support shorthand initialization:
int[][] numbers = {{1,2,3}, {4,5,6}, {7,8,9}};
Ordinarily, we would declare the two-dimensional array numbers
as an int[3][3]
, but this shorthand lets us specify the values immediately.
Why would you need a two-dimensional array? Well, you could use one to easily recreate the famous "Battleship" game:
In "Battleship", the structure of the playing field can be described easily: a two-dimensional array of 10 arrays with 10 elements each.
You create two of these arrays (one for you and one for your opponent)
int battleshipBoard1 = new int[10][10];
int battleshipBoard2 = new int[10][10];
use some values (e.g. numbers or symbols) to populate the elements corresponding to the location of your ships, and then take turns calling out the coordinates for specific elements:
- battleshipBoard1[0][2]!
- Miss! battleshipBoard2[2][4]!
- Hit!
- battleshipBoard2[2][5]!
- Hit!
- battleshipBoard2[2][6]!,
- Sunk!
This concludes our first introduction of arrays, but it's only the beginning of our interaction with them. In the following lessons, we'll see interesting ways they can be used, and also learn what built-in functions Java has to let us work with this data structure more conveniently :)
Was published on CodeGym blog .