One Name for Many Values
Storing the marks of five students in five separate variables works. Storing the marks of sixty students that way does not. An array gives you one name and a numbered slot for each value, so marks[0] through marks[59] replaces sixty variable names, and a loop can visit every one of them.
Two properties define a Java array and both matter. It is fixed in length: the size is decided when you create it and can never change afterwards. There is no add() and no remove(). And it is single-typed: an int[] holds only int values, which the compiler enforces.
There are two ways to create one. If you already know the values, list them in braces: int[] marks = {90, 85, 78};. If you know only the size, use new with a length: int[] marks = new int[60];. In the second case Java fills every slot with a default — 0 for numeric types, false for boolean, and null for any object type including String.
That last default causes a specific bug. A new String[3] contains three nulls, not three empty strings. Calling any method on one of those elements before assigning it throws a NullPointerException, on a line that looks like it is only reading from an array.
// Values known up front
int[] marks = {90, 85, 78, 92, 65};
String[] subjects = {"Maths", "Physics", "Chemistry"};
// Size known, values filled in later
int[] scores = new int[5]; // {0, 0, 0, 0, 0}
boolean[] present = new boolean[3]; // {false, false, false}
String[] names = new String[3]; // {null, null, null}
// Reading and writing by index — counting starts at 0
System.out.println(marks[0]); // 90 — the FIRST element
System.out.println(marks[4]); // 65 — the LAST element
marks[2] = 80; // overwrite index 2
// length is a FIELD, with no brackets
System.out.println(marks.length); // 5
System.out.println(marks[marks.length - 1]); // last element, safely
// The null default bites here
// System.out.println(names[0].length());
// NullPointerException — names[0] is null, not ""
names[0] = "Ananya";
System.out.println(names[0].length()); // 6 arr.lengthhas no brackets;str.length()does. Writingarr.length()givescannot find symbol: method length(), and writingstr.lengthgives a similar error about a field. There is no rule to derive this from — it is simply worth memorising once.
Traversing an Array, and the Exception You Will Meet
Almost everything you do with an array is a loop. Use the counted for loop when you need the index — to print serial numbers, to write values back into the array, or to compare an element with the one beside it. Use the enhanced for-each loop when you only want to read each value, because there is no index to get wrong.
The exception you will meet is ArrayIndexOutOfBoundsException. It means you asked for a slot that does not exist, and the message tells you exactly which: Index 5 out of bounds for length 5. Since valid indices run from 0 to length - 1, index 5 in an array of 5 is one past the end.
Three causes account for nearly all of them. Writing i <= arr.length in the loop condition. Using a number that came from user input or from indexOf without checking it first. And starting at 1 instead of 0 out of habit, which silently skips the first element and then overruns by one at the far end.
Below are the three algorithms every first-year course asks for: sum and average, finding the maximum, and a linear search. Note the detail in the maximum: start with the first element, not with 0. Starting at 0 gives the wrong answer for an array of all-negative numbers, and that is a favourite exam trap.
int[] marks = {90, 85, 78, 92, 65};
// Sum and average
int total = 0;
for (int m : marks) {
total += m;
}
double average = (double) total / marks.length; // cast, or you get int division
System.out.printf("Total %d, average %.2f%n", total, average);
// Maximum — start from the first element, never from 0
int max = marks[0];
for (int i = 1; i < marks.length; i++) {
if (marks[i] > max) {
max = marks[i];
}
}
System.out.println("Highest: " + max); // 92
// Linear search — return the index, or -1 when absent
int target = 78;
int found = -1;
for (int i = 0; i < marks.length; i++) {
if (marks[i] == target) {
found = i;
break;
}
}
System.out.println(found >= 0 ? "Found at " + found : "Not present");
// The exception, and how to avoid it
int idx = 7;
if (idx >= 0 && idx < marks.length) { // short-circuit guard
System.out.println(marks[idx]);
} else {
System.out.println("No such index");
} - An empty array and a null array are different problems.
arr.length == 0is a valid array with nothing in it, and looping over it simply does nothing.arr == nullmeans there is no array at all, and touchingarr.lengththrows aNullPointerException. A method receiving an array from outside should check for null first, then for length.
The Arrays Utility Class
java.util.Arrays is a class of static helper methods that save you from writing loops for the obvious tasks. Import it once and several problems disappear.
Arrays.toString(arr) is the one you will use immediately and constantly. Printing an array directly gives something like [I@1b6d3586 — that is the default toString() every object inherits, showing the type and a hash, not the contents. Arrays.toString() gives you [90, 85, 78]. For a two-dimensional array use Arrays.deepToString(), because the plain version would print the inner arrays as those same cryptic codes.
Arrays.sort(arr) sorts in place, ascending, and returns nothing — so arr = Arrays.sort(arr) does not compile. Arrays.binarySearch(arr, key) is much faster than a linear search but has a hard precondition: the array must already be sorted. Run it on unsorted data and it will not error; it will simply return a wrong answer, which is far worse.
For comparison, Arrays.equals(a, b) checks the elements one by one, which is what you want. a == b compares references and is false for two separate arrays even when their contents are identical. This is the same identity-versus-contents distinction as with Strings, and it catches people just as often.
import java.util.Arrays;
int[] arr = {5, 3, 1, 4, 2};
// Printing
System.out.println(arr); // [I@1b6d3586 — not useful
System.out.println(Arrays.toString(arr)); // [5, 3, 1, 4, 2]
// Sorting — in place, returns void
Arrays.sort(arr);
System.out.println(Arrays.toString(arr)); // [1, 2, 3, 4, 5]
// Binary search — ONLY on a sorted array
System.out.println(Arrays.binarySearch(arr, 3)); // 2
// Copying and resizing (arrays cannot grow, so you make a new one)
int[] first3 = Arrays.copyOf(arr, 3); // [1, 2, 3]
int[] bigger = Arrays.copyOf(arr, 8); // [1,2,3,4,5,0,0,0]
int[] middle = Arrays.copyOfRange(arr, 1, 4); // [2, 3, 4] end exclusive
// Filling
int[] blank = new int[5];
Arrays.fill(blank, -1); // [-1,-1,-1,-1,-1]
// Comparing contents, not identity
int[] a = {1, 2, 3};
int[] b = {1, 2, 3};
System.out.println(a == b); // false — two different objects
System.out.println(Arrays.equals(a, b)); // true — same contents
// Quick aggregates without writing a loop
System.out.println(Arrays.stream(arr).sum()); // 15
System.out.println(Arrays.stream(arr).max().getAsInt()); // 5 Arrays.sorton an array of your own objects requires those objects to be comparable — either the class implementsComparable, or you pass aComparatoras a second argument. Without either you get aClassCastExceptionat run time.
Two-Dimensional and Jagged Arrays
A two-dimensional array models a grid: a marksheet with students down the side and subjects across the top, a matrix, a game board. In Java it is really an array of arrays, and understanding that explains everything about how it behaves.
int[][] matrix = new int[3][4]; creates three rows, each holding four columns. Access an element with two indices, row first: matrix[1][2]. The row count is matrix.length, and the width of a particular row is matrix[i].length. Use the second form inside your inner loop rather than hard-coding a number — it keeps working if the rows differ in length.
Because it is an array of arrays, the rows need not be the same length at all. That is a jagged array, and it is genuinely useful: a list of the subjects each student has opted for, where different students take different numbers of subjects. Create it with the second dimension left empty, then assign each row separately.
Traversing a 2D array takes nested loops: the outer loop walks the rows and the inner loop walks the columns of the current row. Getting the two indices the wrong way round is the standard mistake, and it usually shows up as an ArrayIndexOutOfBoundsException the moment the grid is not square.
import java.util.Arrays;
// A 3x3 grid with values
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
System.out.println(matrix[1][2]); // 6 — row 1, column 2
System.out.println(matrix.length); // 3 — number of rows
System.out.println(matrix[0].length); // 3 — columns in row 0
// Traversing: outer loop = rows, inner loop = columns
for (int r = 0; r < matrix.length; r++) {
for (int c = 0; c < matrix[r].length; c++) { // note matrix[r].length
System.out.print(matrix[r][c] + "\t");
}
System.out.println();
}
// Or read-only with for-each: each row is itself an int[]
for (int[] row : matrix) {
System.out.println(Arrays.toString(row));
}
// Printing the whole thing
System.out.println(Arrays.deepToString(matrix));
// [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
// Jagged: rows of different lengths
String[][] optedSubjects = new String[3][];
optedSubjects[0] = new String[]{"Maths", "Physics", "Chemistry"};
optedSubjects[1] = new String[]{"Biology", "Chemistry"};
optedSubjects[2] = new String[]{"Maths"};
for (String[] row : optedSubjects) {
System.out.println(row.length + " subjects: " + String.join(", ", row));
} Arrays.toString()on a 2D array prints the inner arrays as hash codes. UseArrays.deepToString()for anything nested. The same pairing exists for comparison:Arrays.equalsfor one dimension,Arrays.deepEqualsfor more.
Arrays Are Passed by Reference — and When to Use ArrayList Instead
Java always passes arguments by value, but for an object the value being copied is the reference, not the object. So when you pass an array to a method, the method gets its own copy of the arrow pointing at the same array. Changing an element through that arrow changes the caller's array. Reassigning the parameter to a whole new array does not, because you only redirected the copy of the arrow.
This surprises people in both directions. A method that "just reads" an array can accidentally modify the caller's data, and a method that tries to hand back a new array by assigning to its parameter appears to do nothing. If a method needs to produce a different array, return it.
Finally, the practical question: array or ArrayList? An array's fixed length is its defining limitation. If you need to add items as you go, or remove them, an array forces you to allocate a new one and copy, which is exactly what ArrayList does for you internally. Use an array when the size is genuinely fixed and known, when you are storing primitives and want to avoid the memory cost of boxing, or when a DSA exercise requires one. Use ArrayList for almost everything else.
public class PassByValue {
static void doubleAll(int[] data) {
for (int i = 0; i < data.length; i++) {
data[i] *= 2; // modifies the CALLER's array
}
}
static void tryToReplace(int[] data) {
data = new int[]{99, 99, 99}; // only redirects the local copy
}
public static void main(String[] args) {
int[] nums = {1, 2, 3};
doubleAll(nums);
System.out.println(java.util.Arrays.toString(nums)); // [2, 4, 6]
tryToReplace(nums);
System.out.println(java.util.Arrays.toString(nums)); // [2, 4, 6] — unchanged
}
}
// Converting between the two
import java.util.*;
String[] arr = {"a", "b", "c"};
List<String> list = new ArrayList<>(Arrays.asList(arr)); // array -> resizable list
list.add("d");
String[] back = list.toArray(new String[0]); // list -> array - Fixed size that never changes, and you know it in advance — array
- Items added or removed at run time —
ArrayList - Storing primitives in bulk with no boxing overhead — array (
int[], notList<Integer>) - You want
contains,indexOf, sorting and removal built in —ArrayList - Multi-dimensional grids and matrices — arrays are simpler and read better
- Anything you will pass around a larger program —
Listas the declared type, so the implementation can change later
Arrays.asList(arr)returns a fixed-size list backed by the original array — callingadd()on it throwsUnsupportedOperationException. Wrap it innew ArrayList<>(...), as above, whenever you intend to modify it.
