You are currently viewing Java Arrays
java_logo

Java Arrays

Arrays in Java provide a way to store and manipulate collections of elements. They offer a structured approach to handling data, allowing developers to organize information efficiently.

1. Anatomy of an Arrray

An array in Java is a contiguous block of memory that stores elements of the same data type. The elements can be primitive data types, objects, or even other arrays. The basic structure of an array can be visualized as follows:

+---+---+---+---+---+
| 0 | 1 | 2 | 3 | 4 |
+---+---+---+---+---+
  |   |   |   |   |
[10, 20, 30, 40, 50]
  • Index: Each element in the array is assigned a unique index, starting from 0 and incrementing by 1 for each subsequent element.
  • Size: The total number of elements an array can hold is determined at the time of declaration and cannot be changed afterward.
  • Initialization: Elements can be initialized individually or collectively using various syntaxes.

2. Creating an Array of Primitives

Arrays can be declared and initialized in several ways:

// Declaration with size
int[] numbers = new int[5];

// Initialization with values
int[] scores = new int[]{42, 66, 100};

// Anonymous array
int[] values = {10, 20, 30, 40, 50};

// [] after the name
int otherValues[] = {10, 20, 30, 40, 50};

Declaration with size

  • Declaration: This line declares an array named numbers that can hold 5 integers.
  • Initialization: The new int[5] allocates memory for an array of integers with a size of 5.
  • Default Values: Since no explicit values are assigned, the elements of numbers are initialized to the default value of 0 for integers.

Initialization with values

  • Declaration: This line declares an array named scores that can hold integers.
  • Initialization: The new int[]{42, 66, 100} creates an array and initializes it with the specified values.
  • No Size Specified: The size of the array is inferred from the number of provided values.

Anonymous array

  • Declaration and Initialization Combined: This line declares and initializes an array named values with the specified values.
  • Concise Syntax: The use of {} is a shorthand for creating an array without explicitly mentioning the keyword new and the data type.
  • No Size nor Type Specified: The size and type of the array is inferred from the provided values.

[] after the name

Alternate Syntax: In Java, both int[] numbers and int numbers[] are valid ways to declare an array. However, the former is more widely used and considered good practice.

3. Creating an Array with Reference Variables

In Java, arrays can hold elements of any type, including reference variables to objects. This flexibility allows developers to create arrays of user-defined classes or any built-in classes.

It is important to understand that array does not allocate space for the object itself. Instead, it allocates space for a reference to where the objects are really stored. For the example: String[] animals = {"lion", "pig", "dog"}.

+-----------+
|  animals  |       
+-----------+       
|   [0]     | -----------------------------------------------------> "lion"
|   [1]     | -----------------------------------------------------> "pig"
|   [2]     | -----------------------------------------------------> "dog"
+-----------+ 
  • animals is the array variable.
  • The array has three slots ([0], [1], and [2]), each capable of holding a reference.
  • Each slot in the array holds a reference to the corresponding String object (“lion”, “pig”, and “dog”).

3.1. Declaration and Initialization

String[] words;  // Declaration (not yet instantiated)
  • Declaration: The array is declared with a variable name. At this point, it doesn’t point to any memory location.
words = new String[2];  // Instantiation (allocates memory for two references)
  • Initialization with default values: Memory is allocated for the number of references that are declared in the instantiation. However, each reference currently points to null.
String[] array1 = new String[]{"hello", "world"};
  • Initialization with specific values: An array variable array1 is declared and instantiated with an array of two String objects (“hello”, “world”). Memory is allocated for the array and the actual String objects.

3.2. Casting

You can assign an array of a more specific type to an array of a more general type without the need for explicit casting. However, if you want to assign it back to the more specific type, you’ll need to use explicit casting.

String[] words = {"word"};
Object[] objects = words;
String[] words2 = (String[]) objects;
  1. String[] words = {"word"};:
    • Initializes an array of String with a single element.
  2. Object[] objects = words;:
    • Assigns the words array (array of String) to an array of Object.
    • This is allowed because Object is more general than String.
  3. String[] words2 = (String[]) objects;:
    • Explicitly casts the array of Object back to an array of String.
    • This casting is required because the original array was cast to a more general type (Object[]), and you need to explicitly tell the compiler that you’re treating it as an array of String again.

4. Using an Array

Arrays are fundamental data structures in Java that allow you to store and access multiple values of the same type.

String[] fruits = {"Apple", "Banana", "Orange"};

Arrays are zero-indexed, meaning the first element is at index 0, the second at index 1, and so on. To access elements, simply specify the index inside square brackets:

String firstFruit = fruits[0];  // Access the first element
String secondFruit = fruits[1]; // Access the second element
String thirdFruit = fruits[2];  // Access the third element

To determine the length of an array, you can use the length property. Note that length is not a method, so no parentheses are needed:

int arrayLength = fruits.length; // Get the length of the array

It’s important to highlight that array indices start at 0, but the length property represents the total count of elements, starting from 1. This means that for our fruits array, arrayLength will be 3.

A common task is to iterate through the elements of an array. You can achieve this using a loop, such as a for loop:

for (int i = 0; i < fruits.length; i++) {
    System.out.println("Fruit at index " + i + ": " + fruits[i]);
}

In this example, the loop iterates from 0 to fruits.length - 1, printing each element along with its index.

4.1. Sorting

Sorting is a common operation in programming, and Java provides convenient methods to sort arrays efficiently.

import java.util.Arrays;

public class ArraySortingExample {
    public static void main(String[] args) {
        int[] numbers = {42, 18, 127};
        System.out.println("Before sorting: " + Arrays.toString(numbers));

        Arrays.sort(numbers);
        System.out.println("After sorting: " + Arrays.toString(numbers));
    }
}

output:

Before sorting: [42, 18, 127]
After sorting: [18, 42, 127]

The result is a sorted array of numbers. Now lets have a look what happens when we use String types

        String[] numbers = {"42", "18", "127"};
        System.out.println("Before sorting: " + Arrays.toString(numbers));

        Arrays.sort(numbers);
        System.out.println("After sorting: " + Arrays.toString(numbers));

output:

Before sorting: [42, 18, 127]
After sorting: [127, 18, 42]

String sorts in alphabetic order: 1 sorts before 4 and 2 sorts before 8. It is possible to create custom sort orders using something called a comparator

4.2. Searching

Searching in Java becomes notably efficient when dealing with sorted arrays, and one of the most powerful algorithms for this purpose is the binary search.

Binary search relies on the assumption that the array is already sorted. It follows a “divide and conquer” approach, repeatedly dividing the search range in half until the target element is found or the search space is exhausted.

Here are the scenarios and results of binary search:

ScenarioResult
Target element found in sorted arrayIndex of the match
Target element not found in sorted arrayNegative value indicating one smaller than the negative of the index where a match needs to be inserted to preserve sorted order
Unsorted arrayThe result is unpredictable

examples:

int[] array = {70, 10, 30, 20, 50, 60, 80, 40, 90};
int[] sortedArray = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100};

// Using binary search
System.out.println(Arrays.binarySearch(sortedArray, 20)); // 1
System.out.println(Arrays.binarySearch(sortedArray, 10)); // 0
System.out.println(Arrays.binarySearch(sortedArray, 75)); // -8
System.out.println(Arrays.binarySearch(sortedArray, 35)); // -4
System.out.println(Arrays.binarySearch(sortedArray, 2));  // -1
System.out.println(Arrays.binarySearch(array, 20)); // Unpredictable result 
System.out.println(Arrays.binarySearch(array, 35)); // Unpredictable result 
  1. Arrays.binarySearch(sortedArray, 20): The target element 20 is found at index 1. The output is 1.
  2. Arrays.binarySearch(sortedArray, 10): The target element 10 is found at index 0. The output is 0.
  3. Arrays.binarySearch(sortedArray, 75): The target element 75 is not found. The negative value -8 indicates the insertion point (one smaller than the negative of the index where a match needs to be inserted to preserve sorted order).
  4. Arrays.binarySearch(sortedArray, 35): The target element 35 is not found. The negative value -4 indicates the insertion point.
  5. Arrays.binarySearch(sortedArray, 2): The target element 2 is not found. The negative value -1 indicates the insertion point.
  6. Arrays.binarySearch(array, 20): The result is unpredictable because the array is not sorted.
  7. Arrays.binarySearch(array, 35): The result is unpredictable due to the unsorted array.

4.3. Comparing

4.3.1. compare()

The Arrays.compare() method in Java returns different outcomes based on the comparison between two arrays. Here’s a breakdown of possible outcomes:

  1. Negative Value (-1):
    • Indicates that the first non-equal element in the first array has a lower value than the corresponding element in the second array.
    • Both arrays are the same, but the second array has extra elements at the end
  2. Zero (0):
    • Both arrays are the same length and each corresponding pair of elements in both arrays are equal.
  3. Positive Value (1):
    • Indicates that the first non-equal element in the first array has a higher value than the corresponding element in the second array.
    • Both arrays are the same, but the first array has extra elements at the end

what does smaller mean:

  • null is smaller than any other value
  • for numbers, normal numeric order applies
  • for strings/characters, numbers are smaller than letters
  • for strings/characters, uppercase is smaller than lowercase

Here’s a table illustrating possible outcomes of comparing two arrays (array1 and array2) using Arrays.compare():

array1array2ResultReason
{1, 2, 3}{1, 2, 3}0Both arrays are lexicographically equal.
{1, 2, 3}{1, 2, 4}-1First non-equal element in array1 is smaller.
{1, 2, 3}{1, 2, 2}1First non-equal element in array1 is larger.
{1, 2, 3}{1, 2, 3, 4}-1Both arrays are equal up to the length of array1.
{1, 2, 3, 4}{1, 2, 3}1Both arrays are equal up to the length of array2.

4.3.2. mismatch()

The Arrays.mismatch() method in Java is used to find the index of the first mismatch between two arrays. If the arrays are equal, it returns -1. Here are some examples to illustrate its usage:

import java.util.Arrays;

public class ArrayMismatchExample {
    public static void main(String[] args) {
        int[] array1 = {1, 2, 3, 4, 5};
        int[] array2 = {1, 2, 3, 4, 5}; 

        int mismatchIndex1 = Arrays.mismatch(array1, array2);
        System.out.println("Mismatch Index 1: " + mismatchIndex1); // Output: -1

        int[] array3 = {1, 2, 3, 4, 5};
        int[] array4 = {1, 2, 3, 9, 5}; 

        int mismatchIndex2 = Arrays.mismatch(array3, array4);
        System.out.println("Mismatch Index 2: " + mismatchIndex2); // Output: 3

        String[] words1 = {"apple", "banana", "orange"};
        String[] words2 = {"apple", "banana", "grape"}; 

        int mismatchIndex3 = Arrays.mismatch(words1, words2);
        System.out.println("Mismatch Index 3: " + mismatchIndex3); // Output: 2
    }
}
  • mismatchIndex1 is -1 because array1 and array2 are equal.
  • mismatchIndex2 is 3 because the arrays differ at index 3 (array3[3] != array4[3]).
  • mismatchIndex3 is 2 because the arrays differ at index 2 (words1[2] != words2[2]).

4.3.3. Summary

MethodUsed ForOutcome (Arrays are the same)Outcome (Arrays are different)
equals()Equality Comparisontruefalse
compare()Numeric Comparison0Negative/Positive integer
mismatch()Element Mismatch-1Index of first mismatch

5. Multidimensional Arrays

Arrays are objects and array components can be objects. This means that arrays can hold others arrays. Such arrays are called multidimensional arrays.

There are different ways to declare a multidimensional array in Java:

int[][] multiArray;  // brackets are placed after the type.
int multiArray2[][]; // brackets are placed after the variable name.
int[] multiArray3[]; // mixed style

All of the above are valid ways of declaring a 2D array, but the first two ways are preferred.

You can specify the size of a multidimensional array during initialization:

String[][] multiArr = new String[3][2];

The result is an array with three elements, each of which refers to an array of two elements. The addressable range can be visualized as [0][0] through [2][1]. The visiualization looks like this:

[   1      |      2      |      3   ]
    ↓             ↓             ↓
[ 1 | 2 ]     [ 3 | 4 ]     [ 5 | 6 ]

Let’s print this array to the system:

        String[][] multiArr = new String[3][2];
        for(String[] arr : multiArr){
            for (String word : arr){
                System.out.print(word + " ");
            }
            System.out.println();
        }
  • The outer loop (for(String[] arr : multiArr)) iterates over each row (arr) of the 2D array.
  • The inner loop (for (String word : arr)) iterates over each element (word) in the current row.
  • Inside the inner loop, each word is printed, followed by a space.

After printing all the elements in a row, a newline character (System.out.println()) is added to move to the next line, creating a visual separation between rows.

The output will be determined by the fact that the array was initialized but not populated with actual strings. In Java, uninitialized elements in an array of objects (like strings) are initialized to null. Therefore, the output will consist of spaces for null elements:

null null 
null null 
null null 

Now let’s set the first and the last elements of this 2D array:

multiArr[0][0] = "First Element";
multiArr[2][1] = "Last Element";

The visualization now looks like this:

[               1      |      2      |       3              ]
                ↓             ↓              ↓
[ First Element | 2 ]     [ 3 | 4 ]      [ 5 | Last Element ]

If we print this to the system, the output looks like this:

First Element null 
null null 
null Last Element 

A multidimensional array can contain arrays of different sizes. Let’s consider an example:

        int[][] differentSizes={{5,6},{1},{1,2,3},{9,8,7,6}};
        for(int[] inner : differentSizes){
            for (int num : inner){
                System.out.print(num + " ");
            }
            System.out.println();
        }

the output is:

5 6 
1 
1 2 3 
9 8 7 6 

6. Converting between Array and List

6.1. From List to Array

Converting between an ArrayList and an array in Java involves using the toArray() method provided by the List interface. Additionally, the toArray(T[] a) method can be used to specify the type of the resulting array explicitly.

import java.util.ArrayList;
import java.util.List;

public class ArrayListToArray {
    public static void main(String[] args) {
               List<String> stringList = new ArrayList<>();
                stringList.add("One");
                stringList.add("Two");
                stringList.add("Three");

                Object[] array1 = stringList.toArray();
                String[] array2 = stringList.toArray(new String[0]);

                stringList.clear();

                System.out.println(stringList.size());
                System.out.println(array1.length);
                System.out.println(array2.length);
    }
}
  • The toArray() method, when called without arguments, returns an array of type Object. This means that the elements in array1 are of type Object, and you would need to cast them to the appropriate type (in this case, String) when accessing them. Since it’s of type Object[], the array can store any type of object.
  • The toArray(T[] a) method allows you to specify the type of the resulting array explicitly. In this case, new String[0] is passed as an argument, indicating that the resulting array should be of type String[]. This way, the elements in array2 are directly of type String, and you don’t need to cast them.
  • When you call clear() on the ArrayList, it removes all elements from the list, but it does not affect the arrays that were previously created. The arrays (array1 and array2) are separate objects created during the conversion, and their contents remain unchanged after clearing the list.

6.2. From Array to List

6.2.1. asList()

When converting an array to an ArrayList, you can use Arrays.asList() to create a fixed-size List backed by the original array. When a change is made to one, it is available in the other. Here’s an example using a String array:

import java.util.Arrays;
import java.util.List;

public class ArrayToListExample {
    public static void main(String[] args) {
String[] stringArray = {"apple", "banana", "cherry"};
List<String> stringList = Arrays.asList(stringArray);

System.out.println("List size: " + stringList.size()); //  List size: 3

// Set an element in the list (backed by the original array)
stringList.set(1, "grapes");
System.out.println(stringArray[1]); // grapes
System.out.println(stringList);     // [apple, grapes, cherry]

// Modify the original array
stringArray[0] = "mango";
System.out.println(stringArray[0]); // mango
System.out.println(stringList);     // [mango, grapes, cherry]

// Attempt to remove an element from the list
stringList.remove(0); // UnsupportedOperationException
    }
}
  1. List Initialization:
    • We initialize a String array (stringArray) with three elements.
    • We use Arrays.asList(stringArray) to create a List<String> (stringList). The list is backed by the original array.
  2. Setting Element in the List:
    • We set the element at index 1 in the list to “grapes” using stringList.set(1, "grapes").
    • This modification is reflected in both the array and the list.
  3. Modifying the Original Array:
    • We modify the original array by changing the element at index 0 to “mango” (stringArray[0] = "mango").
    • The modification is also reflected in the list.
  4. Attempting to Remove:
    • We attempt to remove an element from the list using stringList.remove(0), which results in UnsupportedOperationException. This is expected since the list is fixed-size (backed by the original array).

The key takeaway is that modifications made to the array or the list are reflected in both, as they share the same underlying data. However, operations that change the size of the list (e.g., adding or removing elements) will result in an UnsupportedOperationException.

6.2.2. List.of()

Another option is to create an immutable List. That means you cannot change the values or the size of the List

public class ImmutableArrayListExample {
    public static void main(String[] args) {
        // Create a String array
        String[] array = {"apple", "orange"};

        // Create an immutable List from the array
        List<String> immutableList = List.of(array);

        // Change the original array
        array[0] = "banana";

        // Print the immutable list and the array
        System.out.println("Immutable List: " + immutableList); 
        System.out.println("Modified Array: " + Arrays.toString(array)); 

        // Attempt to modify the immutable list
        try {
            immutableList.set(1, "test");
        } catch (UnsupportedOperationException e) {
            System.out.println("UnsupportedOperationException: " + e.getMessage());
        }
    }
}

output:

Immutable List: [apple, orange]
Modified Array: [banana, orange]
UnsupportedOperationException: null
  1. List Initialization:
    • We create a String array (array) with two elements: “apple” and “orange”.
    • We create an immutable List<String> (immutableList) using List.of(array).
  2. Changing the Original Array:
    • We modify the original array by changing the element at index 0 to “banana” (array[0] = "banana").
  3. Printing Results:
    • We print the contents of the immutable list and the modified array.
    • The printed output shows that changes made to the array are not reflected in the immutable list.
  4. Attempting to Modify the Immutable List:
    • We attempt to modify the immutable list using immutableList.set(1, "test"), resulting in UnsupportedOperationException.
    • This is expected since the list is immutable, and attempting to modify it will throw an exception.

Creating an immutable list is useful when you want to ensure that the contents of the list remain constant and cannot be modified after creation. The List.of() method provides a convenient way to create such an immutable list from an array or other iterable sources.

6.3.3. Using Varargs to Create a List

Both List.of() and Arrays.asList() take varargs, which let you pass in an array or just type out the String values. This is handy to easily create and populate a List on one line.

        // Create a List using Arrays.asList with varargs
        List<String> listFromArray = Arrays.asList("apple", "orange", "banana");

        // Create a List using List.of with varargs
        List<String> listOfFruits = List.of("apple", "orange", "banana");