Arrays are a fundamental data structure in Java, used to store collections of elements of the same type. This article provides a comprehensive guide to working with arrays, covering everything from basic declaration and initialization to multidimensional arrays, searching, sorting, comparing and copying. In addition, the material provides essential insights whether you’re preparing for an interview or looking to brush up on your knowledge.
Table of Contents
In Java, an array is a container object that holds a fixed number of elements of the same data type. These elements can be either primitive types (like int
, char
, boolean
, etc.) or objects (like String
or custom classes) and arrays implementing interfaces (like CharSequence
, etc.). Array functionality is part of the java.util.Arrays package.
Key characteristics of Java arrays include:
- The length of an array is established upon creation and cannot be changed.
- All elements in an array must be of the same type.
- Arrays can store duplicate values.
- Arrays can be one-dimensional, two-dimensional, or multi-dimensional by representing a matrix of data. Note that "true support" of multi-dimensional arrays is not supported, but Java offers an array of arrays to support a multi-dimensional structure.
- An array declared with a superclass or interface type can store subclass objects, supporting polymorphism. For example, an array of
CharSequence
can store references toString
andStringBuilder
objects. - Array elements are accessed using an index, starting at
0
. Therefore, the last element in an array is at indexlength - 1
.
1. Array Declarations
Arrays in Java must adhere to specific declaration rules to be properly defined. The following rules outline the correct syntax for declaring arrays:
1️⃣. Data Type: Arrays can store elements of any valid Java data type, including primitive, class, and interface types.
2️⃣. Brackets: The square brackets []
indicate that a variable is an array.
- One-Dimensional Arrays: One set of brackets is used. The brackets can be placed either after the data type or after the variable name.
int[] numbers; // brackets next to the type
String fruits[]; // brackets next to the variable name
- Multidimensional Arrays: Sets of brackets are used on both sides to create a multidimensional array, like a table.
int[][] matrix; // 2D array
int matrix[][]; // 2D array
int[] matrix[]; // 2D array
int[] matrix [], space [][]; // 2D array and 3D array
- No Size in Declaration: The size of the array is not specified during declaration. The size is determined later, during array creation.
int[] numbers; // Declaration
numbers = new int[5]; // Initialization with size
To consolidate these rules, consider the following examples of valid and invalid array declarations.
Valid Array Declarations:
- Multiple variables of the same type can be defined on one line, including both arrays and non-array variables. Here,
a
andc
areint
variables, whileb
is an array of integers (i.e.,int[]
):
int a, b[], c;
- The following declaration implies that
d
ande
are of typeint[]
whereas, the variablef
is a two-dimensional array (i.e.,int[][]
):
int[] d, e, f[];
Invalid Array Declarations:
- Size is not part of the declaration:
int[2] intArray; // compilation error
int intArray[2]; // compilation error
- You cannot define multiple variables of different types in the same line, separated by commas. Java is strongly typed and this is invalid:
int a, float b[]; // compilation error
2. Creating an Array
Once an array is declared, it must be created. Java provides several ways to create and initialize arrays, each with its own characteristics:
1️⃣. The new
Operator and Size: The new
operator can be used to define the size of the array within brackets without specifying the initial values:
int[] intArray = new int[10]; // creates an array of int with 10 elements initialised to 0
String[] atringArray = new String[10]; // creates an array of String with 10 elements, all initialised to null
❗ Once an array's size is defined during creation, it cannot be changed. This means an existing array object cannot be resized directly.
int[] numbers = new int[5];
numbers = new int[10]; // not resizing! This creates a new array
In this example, the size of the original array object is not changed. Instead, it creates a new int[]
array with a size of 10 and assigns its reference to the numbers
variable. The original array (with a size of 5) is now eligible for garbage collection, assuming it is no longer referenced elsewhere. If it had some data, it's lost.
When an array is declared and created, but no values have been assigned, each slot in the array has a default value:
-
Numeric primitives are set to
0
. -
Boolean primitives are set to
false
. -
References, including primitive data type wrappers, are set to
null
.
String names[];
This array points to null
. The code never instantiated the array, so it is just a reference variable to null
.
String names[] = new String[6];
It is an array because it has brackets. It is an array of type String
since that is the type mentioned in the declaration. It has six elements because the length is 6. Each of those six slots currently is null
but can potentially point to a String
object.
2️⃣. The new
Operator and Values: The new
operator can be used without specifying the size in brackets, provided the initial values are specified directly within curly braces {}
:
int[] numbers = new int[] {42, 55, 99}; // int array of size 3
In this example, an int
array of size 3 is created, and its elements are immediately initialized with the given values. The size of the array is implicitly determined by the number of values provided within the curly braces. Again, it is important to emphasize that the initial definition and size of an array cannot be changed once created.
3️⃣. Array Initializer: An array initializer, a shortcut that allows an array to be created and initialized in one statement. The initial values of the array elements are specified directly using curly braces {}
. This approach is called an anonymous array:
int[] intArray = {4, 7, 9};
String[] stringArray = {"one", null, "two"};
4️⃣. And finally, let's consider how to create multidimensional arrays. The most important thing to remember about multidimensional arrays in Java is that they are essentially arrays of arrays. This allows you to represent data in a tabular format (rows and columns) or in higher-dimensional structures. While Java doesn't have true built-in multidimensional arrays, it provides the ability to create arrays where each element holds a reference to another array.
- Declaration and Initialization with Size: The size of a multidimensional array can be specified during declaration and initialization:
String[][] matrix = new String[3][2]; // сreates a 2D array of 3 rows and 2 columns
This creates an array named matrix
with three elements. Each of these three elements is a reference to an array of two String
objects. It's helpful to think of the addressable range as [0][0]
through [2][1]
. To visualize this matrix, let's take a look at the code.
For example, suppose one of these values is set as:
matrix[0][1] = "set";
Schematically, this can be depicted as follows:
[0,0] [0,1] //Here we set a value for matrix[0][1]
[1,0] [1,1]
[2,0] [2,1]
- Asymmetric Arrays: Multidimensional arrays don't have to be rectangular. Asymmetric arrays can be created where each row has a different number of columns:
int[][] matrix = {{1, 4}, {3}, {9,8,7}};
Schematically, this can be depicted as follows:
[0,0] [0,1] => {1,4}
[1,0] => {3}
[2,0] [2,1] [2,2] => {9,8,7}
Another way to create an asymmetric array is to initialize just an array’s first dimension and define the size of each array component in a separate statement:
int[][] matrix = new int[3][];
matrix[0] = new int[]{4, 9, 7};
matrix[1] = new int[2];
And here an example visual how does work:
[0,0] [0,1] [0,2] => {4, 9, 7}
[1,0] [1,1] => {0, 0}
[2,0] => null
- You can easy create matrix in tabular format like example:
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
To consolidate these rules, consider the following examples of valid and invalid array creation statements.
Valid Array Creation Statements:
- The
new
operator is used to allocate memory for the array, specifying its data type and size:
int[] myIntArray = new int[30]; // Brackets associated with the type
short myShortArray[] = new short[20]; // Brackets associated with the variable name
- An array can be declared first, and then initialized later using the
new
operator:
String[] myStringArray;
myStringArray = new String[12];
- The initial values of the array elements are directly specified using
{}
:
String[] myArray = {"one", "two", null, "three"}; // An array of strings with 4 elements
- An array with a length of zero can be created:
String[] myArray = {}; // This array has a length of 0
- You can declare and initialize variables like array and non array, but you need to know that both can have same type, it's also you can chain create variables like that as:
int[] mySecondIntArray, myIntArray = mySecondIntArray = new int[50]; // Initializes both arrays
But if you want to create two array, and you give to only one parameter this as will work as:
int[] myIntArray, mySecondIntArray = new int[50]; // The second array is initialized to size 50, but the "first" array is not initialized.
- A reference-type array can be explicitly set to
null
:
int[] mySecondIntArray = null;
- As arrays are ultimately objects in Java, they can be assigned to variables of type
Object
:
Object o = new int[5]; // Valid initialization and assignment to an Object
- The
new
operator can be combined with the array initializer, but the size cannot be specified in the brackets:
int a[] = new int[]{1, 2, 3, 4, 5}; // Size is determined by the initializer
- This creates a two-dimensional array:
String[] stringArray[] = {{"one", "two"}, {"three", "four"}};
- This is a valid two-dimensianal array declaration and initialization. Only the first dimension is required to have a size, because you can have an array of arrays, and the arrays referenced by the element indices can be of different sizes:
int[][] matrix = new int[2][];
Invalid Array Creation Statements:
- The parentheses in this statement generate a compile error and size needs to be on the right side of the equation:
String[] stringArray = new String(){3}; // compilation error
- Size needs to be on the right side of the equation:
String[3] stringArray = new String[]; // compilation error
- An array initializer doesn't require a restatement of the array type:
int a[] = int[]{1, 2, 3, 4, 5}; // compilation error
- This is invalid because you are stating the size, but the array initilizer already defines the size:
int b[] = new int[5] {1, 2, 3, 4, 5}; // compilation error
- You cannot use the array initializer on a separate statement line from the declaration of the array:
String[] myArray;
myArray = {"one", "two", null, "three”}; // compilation error
- This statement is invalid, unlike using the
new
operator on the right hand side of this equation. You cannot use array initializer in a compound statement:
short[] mySecondShortArray, myShortArray = mySecondShortArray = {1, 2, 3, 4, 5}; // compilation error
- The type of second argument is incorrect. Only the first dimension in two-dimensional array is required to have a size. And this initialization does not give it a size, so it is incorrect:
int[][] matrix = new int[][2]; // compilation error
- You cannot initialize a two-dimensional array and assign it to a one-dimensional array:
int c[] = new int[5][3]; // compilation error
3. Working with Arrays
Once you've declared and created an array, it's time to use it. This section covers common operations and functionalities associated with working with arrays in Java.
- Arrays in Java are objects, which means they inherit certain characteristics from the
Object
class. So, arrays can callequals()
method. Remember, this would work even on anint[]
, too. Sinceint
is a primitive, thereforeint[]
is an object.
String[] bugs = {"cricket", "beetle", "ladybug”};
String[] alias = bugs;
bugs.equals(alias); // true
- Java has provided the
Arrays.toString()
method that prints an array:
System.out.println(bugs.toString()); // Output: [Ljava…
System.out.println(Arrays.toString(bugs)); // Output: [cricket, beetle, ladybug]
- The length property gives the number of slots have been allocated. This property is a field, not a method, it should not contain parentheses:
String[] birds = new String[6];
System.out.println(birds.length); // Output: 6
Sorting Arrays
Java provides a convenient Arrays.sort()
method for sorting arrays in ascending order. When sorting arrays containing different data types (numbers, strings, etc.), it's important to be aware of the rules Java uses to compare these types, as this will determine the final order of the elements.
Syntax:
void Arrays.sort(T[] array)
The easy way with numbers:
int[] numbers = { 6, 9, 1 };
Arrays.sort(numbers);
System.out.println(Arrays.toString(numbers)); // Output: [1, 6, 9]
Try this again with String
types:
String[] strings = {"10", "9", "a", "100", "M", "0”};
Arrays.sort(strings);
System.out.println(Arrays.toString(strings)); // Output: [0, 10, 100, 9, M, a]
String sorts in alphabetic order, and 1 sorts before 9. The numbers are sorted before letters, and uppercase sorts before lowercase.
Searching Arrays
Java provides a convenient method, Arrays.binarySearch()
, to efficiently search for a specific element within an array. This method requires that the array already be sorted for the results to be accurate.
Syntax:
int Arrays.binarySearch(int[] array, int key)
int Arrays.binarySearch(Object[] array, Object key)
The rules for searching:
- If the target element is found in the sorted array, the method returns the index of the matching element (a positive integer).
- If the target element is not found in the sorted array, the method returns a negative value. The returned value can be used to determine the insertion point for the element to maintain the sorted order. The formula is [-n] - 1, where n is an imaginary index to insert to keep the sorted order.
int[] numbers = {2, 4, 6, 8};
Arrays.binarySearch(numbers, 2); // 0: match
Arrays.binarySearch(numbers, 4); // 1: match
Arrays.binarySearch(numbers, 1); // -1: insertion point would be index 0 => [-0] - 1 = -1
Arrays.binarySearch(numbers, 3); // -2: [-1] - 1 = -2
Arrays.binarySearch(numbers, 9); // -5: [-4] - 1 = -5
It is critical to understand that Arrays.binarySearch()
only works correctly on sorted arrays. If you use it on an unsorted array, the results are unpredictable and likely incorrect. The only way can make sort (if it's necessary):
int[] numbers = new int[]{3, 2, 1};
Arrays.binarySearch(numbers, 2); // 1: match
Arrays.binarySearch(numbers, 3); // output not predictable: -4
String[] strings = new String[]{"9", "100", "10"};
Arrays.sort(strings); // Now: [10, 100, 9]
Arrays.binarySearch(strings, "10"); // 0: match
Arrays.binarySearch(strings, "8"); // -3: [-2] - 1 = -3
Comparing Arrays
Java provides two methods, Arrays.compare()
and Arrays.mismatch()
, to compare two arrays to determine which is smaller.
Syntax: Arrays.compare()
<T> int Arrays.compare(T[] array1, T[] array2)
The method returns:
- A negative integer is if the first array is less than the second array.
- Zero if the arrays are equal.
- A positive integer is if the first array is greater than the second array.
The rules that define a smaller value:
-
null
is smaller than any other value. - For numbers, normal numeric order applies.
- For strings, one is smaller if it is a prefix of another.
- For strings/characters, numbers are smaller than letters.
- For strings/characters, uppercase is smaller than lowercase.
Arrays.compare(
new int[] {1, 2}, new int[] {1}); // 1
Result: Positive number
Reason: The first element is the same, but the first array is longer.
Arrays.compare(
new int[] {1, 2}, new int[] {1, 2}); // 0
Result: Zero
Reason: Exact match
Arrays.compare(
new String[] {"a"}, new String[] {"aa"}); // -1
Result: Negative number
Reason: The first element is a substring of the second.
Arrays.compare(
new String[] {"a"}, new String[] {"A"}); // 32
Result: Positive number
Reason: Uppercase is smaller than lowercase.
Arrays.compare(
new String[] {"a"}, new String[] {null}); // 1
Result: Positive number
Reason: null
is smaller than a letter.
Arrays.compare(
new String[] {"1"}, new String[] {"a"}); // -48
Result: Negative number
Reason: Numbers are smaller than letters
Finally, this code does not compile because the types are different. When comparing two arrays, they must be the same array type.
Arrays.compare(
new int[] {1}, new String[] {"a"}); // DOES NOT COMPILE
Syntax: Arrays.mismatch()
<T> int mismatch(T[] array1, T[] array2)
If the arrays are equal, mismatch()
returns -1. Otherwise, it returns the first index where they differ.
The method returns:
- If the arrays are equal, it returns -1
- If one array is
null
, it returns 0 - If the arrays are not equal, it returns the first index where they differ
Arrays.mismatch(
new int[] {1}, new int[] {1}); // -1
Arrays.mismatch(
new String[] {"a"}, new String[] {"A"}); // 0
Arrays.mismatch(
new String[] {"a"}, new String[] {null}); // 0
Arrays.mismatch(
new int[] {1, 2}, new int[] {1}); // 1
4. Shallow Copy vs. Deep Copy
Each type of array implements the Cloneable
and Serializable
interfaces. Therefore, it's logical to discuss the clone()
method, which is related to two important concepts: shallow and deep copy.
Shallow Copy
A shallow copy creates a new object (or array) but does not clone the objects contained within. Instead, it copies the references to the same objects in memory. As a result, changes made to the objects through one reference will be reflected in the other reference.
@Setter
@Getter
public class Friend implements Cloneable {
private String name;
private int age;
public Friend(String name, int age){
this.name = name;
this.age = age;
}
@Override
public Friend clone() {
return new Friend(this.name, this.age);
}
}
- Shallow Copy of Primitive Array:
private static void shallowCopyOfPrimitive() {
int[] a = {1, 2, 3};
int[] copy = a.clone();
copy[1] = 4;
System.out.println("Origin: " + Arrays.toString(a));
System.out.println("Copy: " + Arrays.toString(copy));
}
Output:
Origin: [1, 2, 3]
Copy: [1, 4, 3]
As primitives are copied directly, the original array remains unchanged, illustrating that primitive types are independent when cloned.
- Shallow Copy of Object Array:
private static void shallowCopyOfObject() {
Friend[] a = {new Friend("Monica", 28), new Friend("Ross", 26)};
Friend[] copy = a.clone();
copy[0].setName("Chandler");
System.out.println("Origin: " + Arrays.toString(a));
System.out.println("Copy: " + Arrays.toString(copy));
}
Output:
Origin: [Name: Chandler, Age: 28, Name: Ross, Age: 26]
Copy: [Name: Chandler, Age: 28, Name: Ross, Age: 26]
This shows the behavior of shallow copying, where both arrays point to the same underlying Friend
objects. Changes in one affect the other because they share the same references.
Deep Copy
A deep copy creates a new object (or array) and also clones the objects contained within. This means that two independent objects are created in memory, so changes made to one do not affect the other.
private static void deepCopy() {
Friend[] a = {new Friend("Monica", 28), new Friend("Ross", 26)};
Friend[] copy = new Friend[a.length];
for (int i = 0; i < a.length; i++) {
copy[i] = a[i].clone(); // Calling clone method to create independent copies
}
copy[0].setName("Chandler");
System.out.println("Origin: " + Arrays.toString(a));
System.out.println("Copy: " + Arrays.toString(copy));
}
Output:
Origin: [Name: Monica, Age: 28, Name: Ross, Age: 26]
Copy: [Name: Chandler, Age: 28, Name: Ross, Age: 26]
This illustrates deep copying, where modifications in one array do not impact the other because they hold separate object instances in memory.
Top comments (0)