Contents

An array is declared with the element type followed by [] and then the variable name. Its size is fixed at the moment new is called and cannot change afterward. Primitive elements are zero-initialized (0, false, 0.0); object elements default to null. You can supply the values inline with an initializer shorthand { v1, v2, ... } — the compiler infers the size. Arrays themselves are objects in Java, so they are always allocated on the heap and assigned by reference.

// Declaration — type followed by [] int[] numbers; // preferred style String[] names; double[] prices; // Initialization with new numbers = new int[5]; // default values: 0 for int, null for objects names = new String[3]; // [null, null, null] // Declaration + initialization together int[] scores = new int[]{95, 87, 72, 68, 91}; // Shorthand initializer (only valid at declaration) int[] scores2 = {95, 87, 72, 68, 91}; String[] days = {"Mon", "Tue", "Wed", "Thu", "Fri"}; // Length — fixed at creation, cannot change System.out.println(scores.length); // 5 (note: length not length()) // Array of objects Person[] people = new Person[3]; people[0] = new Person("Alice", 30); people[1] = new Person("Bob", 25); // people[2] is null until assigned // Array of arrays (not necessarily rectangular) int[][] jagged = new int[3][]; jagged[0] = new int[]{1}; jagged[1] = new int[]{2, 3}; jagged[2] = new int[]{4, 5, 6};

Elements are accessed by their zero-based index using bracket notation: arr[0] is the first element, arr[arr.length - 1] is the last. Accessing an out-of-bounds index at runtime throws ArrayIndexOutOfBoundsException. Use a classic for loop when you need the index, or a for-each loop when you only need the values. The Arrays.stream() method exposes the array as an IntStream (or typed stream for objects), giving access to sum, filter, map, and the rest of the Stream API.

int[] nums = {10, 20, 30, 40, 50}; // Index access — zero-based System.out.println(nums[0]); // 10 System.out.println(nums[nums.length-1]); // 50 (last element) nums[2] = 99; // mutate // ArrayIndexOutOfBoundsException if index < 0 or >= length // nums[5]; // throws ArrayIndexOutOfBoundsException // Classic for loop for (int i = 0; i < nums.length; i++) { System.out.print(nums[i] + " "); } // Enhanced for-each (no index available) for (int n : nums) { System.out.print(n + " "); } // Stream API int sum = Arrays.stream(nums).sum(); int max = Arrays.stream(nums).max().orElseThrow(); long count = Arrays.stream(nums).filter(n -> n > 20).count(); // Convert array to List (backed by array — fixed size) List<String> list = Arrays.asList("a", "b", "c"); // list.add("d"); // throws UnsupportedOperationException // For a mutable List: List<String> mutable = new ArrayList<>(Arrays.asList("a", "b", "c"));

Java multi-dimensional arrays are arrays of arrays — each row is itself an independent array object. A 2D array int[][] m is indexed as m[row][col]. Because each row is a separate array, rows can have different lengths: this is called a jagged (ragged) array and is useful for triangular data or variable-length rows. For a rectangular matrix, declare all dimensions upfront: new int[rows][cols]. Three-dimensional and higher arrays follow the same pattern.

// 2D array — matrix representation int[][] matrix = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} }; // Access: [row][col] System.out.println(matrix[1][2]); // 6 (row 1, col 2) // Iterate a 2D array for (int row = 0; row < matrix.length; row++) { for (int col = 0; col < matrix[row].length; col++) { System.out.printf("%3d", matrix[row][col]); } System.out.println(); } // Using streams with 2D array Arrays.stream(matrix) .flatMapToInt(Arrays::stream) .forEach(System.out::println); // 3D array int[][][] cube = new int[3][3][3]; cube[0][1][2] = 42; // Jagged (ragged) array — rows of different lengths int[][] triangle = new int[5][]; for (int i = 0; i < triangle.length; i++) { triangle[i] = new int[i + 1]; // row 0 has 1 element, row 4 has 5 }

java.util.Arrays is a collection of static helper methods that cover the most common array operations. Arrays.sort() sorts in-place using dual-pivot quicksort for primitives and timsort for objects. Arrays.binarySearch() requires a sorted array and returns the index (or a negative value if not found). Arrays.copyOf() and Arrays.copyOfRange() create new arrays without manual loops. Arrays.fill() initializes all elements to a value. Arrays.equals() compares element-by-element; use Arrays.deepEquals() for multi-dimensional arrays. Arrays.toString() / Arrays.deepToString() give readable string output.

import java.util.Arrays; int[] arr = {5, 3, 8, 1, 9, 2, 7}; // sort — in-place, uses dual-pivot quicksort for primitives (O(n log n)) Arrays.sort(arr); // [1, 2, 3, 5, 7, 8, 9] Arrays.sort(arr, 1, 4); // sort only indices 1-3 (exclusive end) // sort with Comparator (object arrays only) String[] words = {"banana", "apple", "cherry"}; Arrays.sort(words); // alphabetical Arrays.sort(words, Comparator.reverseOrder()); // reverse Arrays.sort(words, Comparator.comparingInt(String::length)); // by length // binarySearch — array must be sorted first int idx = Arrays.binarySearch(arr, 7); // returns index of 7, or negative if absent // fill — set all elements to a value int[] zeros = new int[5]; Arrays.fill(zeros, 42); // [42, 42, 42, 42, 42] Arrays.fill(zeros, 1, 3, 99); // [42, 99, 99, 42, 42] (indices 1-2) // copyOf — creates a new array (truncates or pads with defaults) int[] copy = Arrays.copyOf(arr, 3); // first 3 elements int[] copy2 = Arrays.copyOf(arr, 10); // padded with 0s // copyOfRange — copy a slice int[] slice = Arrays.copyOfRange(arr, 2, 5); // indices 2,3,4 // equals / deepEquals int[] a = {1, 2, 3}; int[] b = {1, 2, 3}; System.out.println(Arrays.equals(a, b)); // true System.out.println(a == b); // false (different references) int[][] m1 = {{1,2},{3,4}}; int[][] m2 = {{1,2},{3,4}}; System.out.println(Arrays.deepEquals(m1, m2)); // true // toString / deepToString System.out.println(Arrays.toString(arr)); // [1, 2, 3, 5, 7, 8, 9] System.out.println(Arrays.deepToString(matrix)); // [[1, 2, 3], [4, 5, 6], ...]

Arrays are fixed-size and can hold primitive types directly (int[], double[]), which avoids boxing overhead and gives better cache locality. ArrayList<T> is dynamically resizable but stores boxed objects. Choose arrays when the size is known upfront and performance matters; choose ArrayList when you need add/remove or want to work with the Collections API. You can convert between the two: Arrays.asList() wraps an array in a fixed-size list; list.toArray() goes the other direction.

// Arrays: fixed size, can store primitives, slightly faster int[] arr = new int[10]; // ArrayList: dynamic size, only object types, more convenient methods List<Integer> list = new ArrayList<>(); list.add(1); list.add(2); list.add(3); // Convert array ↔ List Integer[] boxed = {1, 2, 3}; List<Integer> asList = Arrays.asList(boxed); // fixed-size backed list List<Integer> mutable = new ArrayList<>(asList); // fully mutable // List back to array Integer[] backToArray = list.toArray(new Integer[0]); // idiomatic int[] primitive = list.stream().mapToInt(Integer::intValue).toArray(); // When to use each: // - Use arrays for fixed-size collections of primitives (int[], double[]) // for performance-critical code (avoids boxing overhead) // - Use List<T> for dynamic collections, when you need add/remove, // or when interacting with Collections API Use Arrays.copyOf() or System.arraycopy() (lower-level, faster) to "resize" an array — they actually create a new array. System.arraycopy(src, srcPos, dest, destPos, length) is the most performant option for bulk copies.