Array

About

An array is a container object that holds a fixed number of elements of a single data type. Each element can be accessed by its index.

  • Fixed Size: The size of the array is defined when it is created and cannot be changed later.

  • Indexed Access: Elements are accessed using zero-based indexing.

  • Homogeneous Data: Arrays can only store elements of the same data type.

  • Memory Representation

    1. 1D Array: Contiguous block of memory.

    2. 2D Array: Memory is allocated row by row, with each row being an array itself.

  • Arrays are Objects: Arrays are treated as objects and inherit from java.lang.Object. We can call methods like getClass() on an array:

int[] numbers = {1, 2, 3};
System.out.println(numbers.getClass().getName());  // Output: [I
  • Default Values in Arrays: When an array is initialized, its elements are assigned default values:

    • 0 for numeric types (int, float, etc.).

    • false for boolean.

    • null for reference types (String, objects).

  • Multidimensional Arrays: Java supports arrays of arrays. Example:

int[][] matrix = new int[3][3];
matrix[0][0] = 1;
  • Exception

ArrayIndexOutOfBoundsException: Occurs when accessing an index outside the valid range.

int[] arr = {1, 2, 3};
System.out.println(arr[3]);  // Throws exception

NullPointerException: Accessing an uninitialized array.

int[] arr = null;
System.out.println(arr.length);  // Throws exception

Consider Memory Usage: Large arrays can lead to OutOfMemoryError. Monitor memory with tools like JVisualVM.

  • Performance Characteristics: Arrays in Java are faster than many collection types like ArrayList due to:

    • Contiguous Memory Allocation: Accessing elements via index is O(1).

    • No Boxing/Unboxing for Primitive Types: Arrays of primitives avoid the overhead of wrapping values in objects.

Declaration and Initialization of Arrays

Declaration

dataType[] arrayName;  // Recommended
dataType arrayName[];  // Also valid but less common

Creating Arrays with Reflection

Class<?> clazz = int.class;
Object array = java.lang.reflect.Array.newInstance(clazz, 5);
java.lang.reflect.Array.set(array, 0, 42);
System.out.println(java.lang.reflect.Array.get(array, 0));  // Output: 42

Initialization

1. Separate Declaration and Initialization:

int[] numbers;
numbers = new int[5];  // Allocates memory for 5 integers

2. Combined Declaration and Initialization:

int[] numbers = new int[5];

3. Array Literals:

int[] numbers = {1, 2, 3, 4, 5};  // Automatically initializes and determines size

Accessing and Modification of Array Elements

Accessing

System.out.println(numbers[0]);  // Access the first element

Modifying

numbers[0] = 10;  // Set the first element to 10

Iterating Through an Array

  • For Loop:

for (int i = 0; i < numbers.length; i++) {
    System.out.println(numbers[i]);
}
  • Enhanced For Loop:

for (int number : numbers) {
    System.out.println(number);
}

Properties and Methods of Arrays

Array Properties

length

  • Represents the number of elements in the array.

  • Syntax:

    int[] numbers = {1, 2, 3, 4, 5};
    System.out.println(numbers.length);  // Output: 5

It is not a method, so parentheses () are not used.

Array Methods

Java's built-in arrays do not have methods like objects of classes. However, the java.util.Arrays class provides many static methods to work with arrays.

Methods in java.util.Arrays

Method

Description

Example

copyOf

Copies the specified array, truncating or padding it to the specified length.

int[] newArray = Arrays.copyOf(original, newLength);

copyOfRange

Copies a range of elements from the original array.

int[] rangeArray = Arrays.copyOfRange(original, start, end);

sort

Sorts the elements of the array in ascending order.

Arrays.sort(array);

binarySearch

Searches for a specified value in a sorted array using the binary search algorithm.

int index = Arrays.binarySearch(array, value);

equals

Compares two arrays for equality (both length and content).

boolean isEqual = Arrays.equals(array1, array2);

deepEquals

Compares two multidimensional arrays for deep equality.

boolean isDeepEqual = Arrays.deepEquals(array1, array2);

toString

Returns a string representation of the array.

System.out.println(Arrays.toString(array));

deepToString

Returns a string representation of a multidimensional array.

System.out.println(Arrays.deepToString(array));

fill

Fills the array with a specified value.

Arrays.fill(array, value);

setAll

Sets all elements in the array based on a generator function.

Arrays.setAll(array, i -> i * 2);

asList

Converts an array to a List.

List<Integer> list = Arrays.asList(array);

hashCode

Returns the hash code of the array.

int hash = Arrays.hashCode(array);

deepHashCode

Returns the hash code of a multidimensional array.

int hash = Arrays.deepHashCode(array);

stream

Converts the array into a Stream for functional programming.

IntStream stream = Arrays.stream(array);

parallelSort

Sorts the array using parallel sorting algorithms for large datasets.

Arrays.parallelSort(array);

spliterator

Returns a Spliterator for the array, which can be used for parallel processing.

Spliterator<Integer> split = Arrays.spliterator(array);

Types of Arrays

1D Array

A single row of elements.

int[] numbers = {1, 2, 3, 4, 5};

2D Array

An array of arrays, useful for matrices

int[][] matrix = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};
System.out.println(matrix[1][2]);  // Accesses element in 2nd row, 3rd column (6)

Jagged Array

An array with rows of varying lengths.

int[][] jaggedArray = {
    {1, 2},
    {3, 4, 5},
    {6}
};
System.out.println(jaggedArray[1][2]);  // Output: 5

Array is Not thread-safe

Arrays in Java do not enforce synchronization for read or write operations. If two or more threads modify an array concurrently, the behavior depends on the timing of these modifications and may cause data corruption.

// Concurrent Modification Scenarios of Thread-Safety Issues

int[] numbers = {1, 2, 3};

Runnable task = () -> {
    for (int i = 0; i < numbers.length; i++) {
        numbers[i]++;  // Concurrent modification
    }
};

Thread t1 = new Thread(task);
Thread t2 = new Thread(task);

t1.start();
t2.start();

// Issue: The operations like numbers[i]++ are not atomic, and threads may interfere with each other.
// Result: The array values may not be incremented correctly.

Making Arrays Thread-Safe

Use Synchronization

Wrap array operations in synchronized blocks or methods.

int[] numbers = {1, 2, 3};

synchronized (numbers) {
    for (int i = 0; i < numbers.length; i++) {
        numbers[i]++;
    }
}

Use java.util.concurrent Utilities

Use thread-safe alternatives like CopyOnWriteArrayList .

CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>(Arrays.asList(1, 2, 3));
list.add(4);  // Thread-safe addition

Use Atomic Variables

For arrays of integers or longs, use AtomicIntegerArray or AtomicLongArray

AtomicIntegerArray atomicArray = new AtomicIntegerArray(new int[] {1, 2, 3});

Runnable task = () -> {
    for (int i = 0; i < atomicArray.length(); i++) {
        atomicArray.incrementAndGet(i);  // Thread-safe increment
    }
};

Thread t1 = new Thread(task);
Thread t2 = new Thread(task);

t1.start();
t2.start();

Immutability

Make arrays immutable by copying them and sharing only the copy.

int[] immutableArray = Arrays.copyOf(originalArray, originalArray.length);

Arrays with Generics

Java arrays and generics don’t integrate quite well because arrays are covariant and generics are invariant. This mismatch introduces potential type-safety issues.

What Is Allowed

  1. Creating Generic Arrays with a Wildcard

We can create arrays of wildcards (?).

List<?>[] listArray = new List<?>[10];  // Allowed
  1. Creating Generic Arrays with Raw Types

We can create arrays of raw types (though it is not recommended due to loss of type safety).

List[] listArray = new List[10];  // Allowed
  1. Using Reflection to Create Generic Arrays

Reflection can bypass generic array restrictions.

import java.lang.reflect.Array;

public class GenericArray {
    public static <T> T[] createArray(Class<T> clazz, int size) {
        return (T[]) Array.newInstance(clazz, size);
    }
}
  1. Generic Arrays as Fields

We can declare a generic array field, but the actual instantiation must avoid direct generic array creation.

class Example<T> {
    private T[] elements;

    public Example(Class<T> clazz, int size) {
        elements = (T[]) Array.newInstance(clazz, size);  // Allowed
    }
}
  1. Using Arrays as Generic Collections

Although we cannot create a generic array directly, we can use collections like List or ArrayList.

List<String> list = new ArrayList<>();  // Use this instead of a generic array

What Is Not Allowed

  1. Direct Creation of Generic Arrays

We cannot directly create arrays with a generic type.

T[] array = new T[10];  // Not Allowed
  1. Generic Arrays of Specific Parameterized Types

You cannot create an array of a specific generic type (e.g., List<String>).

List<String>[] stringListArray = new List<String>[10];  // Not Allowed
  1. Mixing Arrays and Generics

You cannot mix arrays and generic types in assignments without warnings.

List<Integer>[] arrayOfLists = (List<Integer>[]) new List[10];  // Allowed with warning

Reason for Restrictions

  • Covariance of Arrays: Arrays in Java are covariant, meaning String[] is a subtype of Object[].

Object[] objArray = new String[10];
objArray[0] = 10;  // Throws ArrayStoreException at runtime
  • Invariance of Generics: Generics are invariant, so List<String> is not a subtype of List<Object>. This ensures type safety at compile time.

  • Type Erasure: Generic types are erased at runtime, leaving no information about the actual type parameter. This makes it impossible to enforce type safety for generic arrays.

Example

Arrays can be created for almost all primitive and reference data types.

Primitive Data Types

Data Type
Description
Example Declaration

byte

8-bit integer

byte[] byteArray = new byte[5];

short

16-bit integer

short[] shortArray = new short[5];

int

32-bit integer

int[] intArray = {1, 2, 3};

long

64-bit integer

long[] longArray = new long[5];

float

32-bit floating-point number

float[] floatArray = new float[5];

double

64-bit floating-point number

double[] doubleArray = new double[5];

char

16-bit Unicode character

char[] charArray = {'A', 'B'};

boolean

1-bit, can store true or false values

boolean[] boolArray = new boolean[5];

Reference Data Types

Data Type
Description
Example Declaration

String

Stores sequences of characters

String[] strArray = {"A", "B", "C"};

Object

Parent class of all Java classes

Object[] objArray = new Object[5];

Custom Class

Any user-defined class can be used as an array type

Student[] studentArray = new Student[5];

Generic Types

Data Type
Description
Example Declaration

List<?>

Array of wildcard lists (requires unchecked cast)

List<?>[] listArray = new List<?>[5];

Map<?, ?>

Array of maps

Map<String, Integer>[] mapArray = new Map[5];

public class ArrayExamples {
    public static void main(String[] args) {
        // Declaring and initializing a one-dimensional array
        int[] numbers = {10, 20, 30, 40, 50};
        System.out.println("1D Array: ");
        for (int number : numbers) {
            System.out.print(number + " ");
        }
        System.out.println();

        // Declaring and initializing a two-dimensional array
        int[][] matrix = {
            {1, 2, 3},
            {4, 5, 6},
            {7, 8, 9}
        };
        System.out.println("\n2D Array (Matrix): ");
        for (int[] row : matrix) {
            for (int col : row) {
                System.out.print(col + " ");
            }
            System.out.println();
        }

        // Using arrays of strings
        String[] fruits = {"Apple", "Banana", "Cherry"};
        System.out.println("\nString Array: ");
        for (String fruit : fruits) {
            System.out.println(fruit);
        }

        // Accessing and modifying elements
        numbers[2] = 35; // Modify the third element
        System.out.println("\nModified Array: ");
        for (int number : numbers) {
            System.out.print(number + " ");
        }

        // Dynamic initialization of an array
        double[] prices = new double[5]; // Create an array with a size of 5
        prices[0] = 10.99;
        prices[1] = 5.49;
        prices[2] = 3.79;
        prices[3] = 12.99;
        prices[4] = 7.49;
        System.out.println("\n\nPrices Array: ");
        for (double price : prices) {
            System.out.print(price + " ");
        }

        // Finding the length of an array
        System.out.println("\n\nLength of Numbers Array: " + numbers.length);

        // Copying arrays
        int[] copiedNumbers = java.util.Arrays.copyOf(numbers, numbers.length);
        System.out.println("\nCopied Array: ");
        for (int num : copiedNumbers) {
            System.out.print(num + " ");
        }

        // Using Arrays class utility methods
        java.util.Arrays.sort(numbers); // Sort the array
        System.out.println("\n\nSorted Array: ");
        for (int number : numbers) {
            System.out.print(number + " ");
        }

        // Multidimensional array with irregular (jagged) dimensions
        int[][] jaggedArray = {
            {1, 2},
            {3, 4, 5},
            {6}
        };
        System.out.println("\n\nJagged Array: ");
        for (int[] row : jaggedArray) {
            for (int col : row) {
                System.out.print(col + " ");
            }
            System.out.println();
        }

        // Array of objects
        Student[] students = new Student[3];
        students[0] = new Student("Alice", 20);
        students[1] = new Student("Bob", 22);
        students[2] = new Student("Charlie", 19);
        System.out.println("\nArray of Objects (Students): ");
        for (Student student : students) {
            System.out.println(student);
        }
    }
}

class Student {
    String name;
    int age;

    Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{name='" + name + "', age=" + age + "}";
    }
}

Arrays Conversion to Streams

Single-Dimensional Arrays

Java provides Arrays.stream method and the Stream.of method to convert a single-dimensional array to a stream:

  1. Using Arrays.stream:

    int[] numbers = {1, 2, 3, 4, 5};
    IntStream intStream = Arrays.stream(numbers);
  2. Using Stream.of:

    String[] names = {"Alice", "Bob", "Charlie"};
    Stream<String> nameStream = Stream.of(names);

Examples

// Filter Even Numbers
int[] numbers = {1, 2, 3, 4, 5};
Arrays.stream(numbers)
      .filter(num -> num % 2 == 0)
      .forEach(System.out::println);

// Find Maximum
int[] numbers = {1, 2, 3, 4, 5};
int max = Arrays.stream(numbers).max().orElseThrow();
System.out.println(max);

// Convert to List
String[] names = {"Alice", "Bob", "Charlie"};
List<String> nameList = Arrays.stream(names).collect(Collectors.toList());

Multi-Dimensional Arrays

Directly converting multi-dimensional arrays into streams isn't supported. However, we can flatten them into a single-dimensional structure or use nested streams.

Examples

// Using Arrays.stream and flatMap
int[][] matrix = {{1, 2}, {3, 4}, {5, 6}};
IntStream flatStream = Arrays.stream(matrix)
                             .flatMapToInt(Arrays::stream);
flatStream.forEach(System.out::println);

// For Object[][]
String[][] data = {{"Alice", "Bob"}, {"Charlie", "David"}};
Stream<String> flatStream = Arrays.stream(data)
                                  .flatMap(Arrays::stream);
flatStream.forEach(System.out::println);

// Sum of All Elements
int[][] matrix = {{1, 2}, {3, 4}, {5, 6}};
int sum = Arrays.stream(matrix)
                .flatMapToInt(Arrays::stream)
                .sum();
System.out.println(sum);

// Find Distinct Elements
int[][] matrix = {{1, 2, 3}, {3, 4, 5}, {5, 6, 7}};
Arrays.stream(matrix)
      .flatMapToInt(Arrays::stream)
      .distinct()
      .forEach(System.out::println);

// Transform and Collect
String[][] data = {{"apple", "banana"}, {"carrot", "date"}};
List<String> upperCaseList = Arrays.stream(data)
                                   .flatMap(Arrays::stream)
                                   .map(String::toUpperCase)
                                   .collect(Collectors.toList());
System.out.println(upperCaseList);

Best Practices

  1. Always check the array length before accessing elements.

  2. Use enhanced for-loops for readability when traversing arrays.

  3. Prefer Arrays utility methods for common operations like copying, filling, or sorting.

Last updated

Was this helpful?