Why this matters
- Off-by-one errors and forgotten
breakstatements are the most common beginner bugs, and both are structural rather than conceptual. - Array copying has several approaches and most of them are shallow, which produces aliasing bugs that are hard to trace.
- Knowing the limits of arrays is what makes the Collections Framework feel like a solution rather than extra API to memorise.
Choosing a loop
forwhen you know the bounds or need the index. The three clauses keep initialisation, condition and update in one place.- Enhanced
forwhen you only need each element. It is harder to get wrong, but gives you no index and no safe way to remove items. whilewhen the number of iterations depends on a condition you re-check — reading until end of input, for example.do-whilewhen the body must run at least once before the condition is first checked. Genuinely rare.
int[] values = {4, 8, 15, 16, 23, 42};
for (int i = 0; i < values.length; i++) { // index available
System.out.println(i + ": " + values[i]);
}
for (int value : values) { // cleaner when index is unused
System.out.println(value);
}
break, continue and labels
break leaves the loop entirely; continue skips to the next iteration. Both affect only the
innermost loop unless you label the outer one.
outer:
for (int row = 0; row < grid.length; row++) {
for (int col = 0; col < grid[row].length; col++) {
if (grid[row][col] == target) {
System.out.println("Found at " + row + "," + col);
break outer; // leaves both loops
}
}
}
Labels are the honest way to exit nested loops. The alternative — a found flag checked in both
conditions — is longer and easier to get wrong. Use them sparingly but without guilt.
switch and the fall-through trap
The classic switch statement continues into the next case unless stopped by break.
switch (day) {
case SATURDAY:
case SUNDAY:
System.out.println("Weekend"); // deliberate fall-through: both share a body
break;
case FRIDAY:
System.out.println("Almost there");
break;
default:
System.out.println("Weekday");
}
Grouping cases is the one good use of fall-through. Everything else is usually a forgotten
break. Java 14 added arrow syntax that removes the hazard entirely, and that form is covered
in the Java 17 module.
Arrays
An array is an object on the heap with a fixed length field. Creating one initialises every
slot to the type's default.
int[] counts = new int[5]; // {0, 0, 0, 0, 0}
String[] names = new String[3]; // {null, null, null}
double[] rates = {1.5, 2.25, 3.0}; // literal form, length inferred
System.out.println(counts.length); // 5 — a field, not a method
What arrays cannot do
- Grow. The length is fixed at creation. "Adding" means allocating a bigger array and copying.
- Hold primitives generically.
int[]andInteger[]are unrelated types, and generics only work with the latter. - Print readably.
System.out.println(array)prints a type name and hash. UseArrays.toStringorArrays.deepToString. - Compare by content with
==. That compares references. UseArrays.equalsorArrays.deepEquals.
Multi-dimensional and jagged arrays
A two-dimensional array in Java is an array of arrays, so the rows need not be the same length.
int[][] grid = new int[3][4]; // rectangular: 3 rows of 4
int[][] triangle = new int[3][]; // rows allocated separately
triangle[0] = new int[] {1};
triangle[1] = new int[] {1, 2};
triangle[2] = new int[] {1, 2, 3};
for (int[] row : triangle) {
System.out.println(Arrays.toString(row));
}
[1]
[1, 2]
[1, 2, 3]
This is the key difference from languages with true rectangular arrays. grid.length is the
row count; grid[0].length is that row's own length, and other rows may differ.
Copying, and which method is safe
| Aspect | Approach | Behaviour |
|---|---|---|
| b = a | Copies the reference only | Both names point at one array — changes are shared |
| a.clone() | New array, same element references | Shallow: fine for primitives, aliased for objects |
| Arrays.copyOf(a, n) | New array of length n | Shallow; pads with defaults or truncates |
| System.arraycopy(...) | Copies a range into an existing array | Shallow; fastest, most verbose |
| Arrays.copyOfRange(a, f, t) | New array from a slice | Shallow |
b = a
ApproachCopies the reference onlyBehaviourBoth names point at one array — changes are shareda.clone()
ApproachNew array, same element referencesBehaviourShallow: fine for primitives, aliased for objectsArrays.copyOf(a, n)
ApproachNew array of length nBehaviourShallow; pads with defaults or truncatesSystem.arraycopy(...)
ApproachCopies a range into an existing arrayBehaviourShallow; fastest, most verboseArrays.copyOfRange(a, f, t)
ApproachNew array from a sliceBehaviourShallow
Every built-in copy is shallow. Only the array is new; object elements are still shared.
int[] original = {1, 2, 3};
int[] alias = original; // not a copy
int[] copy = Arrays.copyOf(original, 3); // a real copy
alias[0] = 99;
System.out.println(original[0]); // 99 — same array
System.out.println(copy[0]); // 1 — independent
For an array of mutable objects, a deep copy means copying each element yourself:
Point[] deep = new Point[source.length];
for (int i = 0; i < source.length; i++) {
deep[i] = new Point(source[i].x(), source[i].y());
}
Useful Arrays methods
Arrays.sort(numbers); // in place; dual-pivot quicksort
int index = Arrays.binarySearch(numbers, 15); // requires a sorted array
Arrays.fill(flags, true); // every slot
String text = Arrays.toString(numbers); // readable output
boolean same = Arrays.equals(first, second); // element by element
List<String> view = Arrays.asList("a", "b"); // fixed-size view, not a real list
Common misreadings
- "
lengthis a method." Arrays use the fieldarray.length. Strings and collections use methods,length()andsize(). - "
clone()deep-copies." It is shallow at every level. Forint[][]the rows are shared. - "Removing from a list inside an enhanced for loop is fine." It throws
ConcurrentModificationException. Use an explicit iterator orremoveIf. - "
binarySearchworks on any array." It requires sorted input, and returns a meaningless negative value otherwise. - "A 2D array is rectangular." It is an array of arrays, and rows can have different lengths.
Quick recall
Everything you need if you only revisit this box.
- Indices run
0tolength - 1;i <= lengthis the off-by-one that throws. breakandcontinueaffect the innermost loop only. Labels are the clean way out of nested loops.- Classic
switchfalls through withoutbreak. Grouping cases is the one legitimate use. - Arrays have a fixed length, use a
lengthfield, and needArrays.toStringto print andArrays.equalsto compare. - A 2D array is an array of arrays, so rows may be jagged.
- Every built-in copy is shallow.
b = ais not a copy at all; deep copying mutable elements is manual work. Arrays.asListis a fixed-size view over the array — wrap it innew ArrayList<>(...)to get a modifiable list.
Test yourself
Answer these before moving on — recall is what makes it stick.