التكرار على العناصر وfor المحسّنة
في هذا الدرس 11 موضوعات
هذا الدرس متاح حاليًا باللغة الإنجليزية فقط، ولم تُنشر نسخته العربية بعد.
Lesson Overview
An array is only useful if you can visit its elements. In Lesson 1 you wrote one loop for that. This lesson makes the pattern automatic and adds a second, simpler loop for the common case where you only need the values.
Back to the lockers. There are two ways to inspect a row of lockers. You can walk along with a numbered clipboard, saying "locker 0, locker 1, locker 2..." (the classic for loop). Or you can let an assistant hand you each item in turn without telling you the number (the enhanced for loop). The clipboard gives you control; the assistant gives you simplicity.
Why this matters: summing an order, finding the cheapest item, checking that every field is filled: all backend list processing is some form of "visit each element". The most common array bug, the off-by-one index, lives in exactly this loop, so we also practise the habit that prevents it.
This lesson builds on arrays (Lesson 1) and the for loop and off-by-one errors (BEG-013).
Learning Objectives
By the end of this lesson you will be able to:
- Write the classic indexed loop over an array with the correct bounds.
- Write an enhanced
forloop and say what it gives you. - Choose between the two loops for a given task.
- Explain why assigning to the enhanced-for variable does not change the array.
- Loop backwards and over part of an array.
- Check, before running, that every index in a loop stays between
0andlength - 1. - Handle an empty array without an error.
Written Explanation
The classic indexed loop
for (int i = 0; i < a.length; i++) {
// a[i] is the current element
}Read the three parts as a checklist for valid indices:
- Start at
0, the first valid index. - Keep going while
i < a.length. The last valid index islength - 1, so the loop must stop beforelength. - Step by
i++to move one locker at a time.
If you write i <= a.length, the body runs once more with i == a.length, and a[i] throws ArrayIndexOutOfBoundsException. This single character is the most common array bug there is.
You can also count down, or visit only part of an array:
for (int i = a.length - 1; i >= 0; i--) { ... } // backwards: last index down to 0
for (int i = 1; i < a.length; i++) { ... } // skip the first elementThe enhanced for loop
When you want each value and do not care about its position, Java has a shorter form (the language specification calls it the enhanced for statement):
for (int s : scores) {
// s is the current element
}Read the colon as "in": "for each int s in scores". The loop visits every element from the first to the last, in order. There is no index to get wrong, so no off-by-one is possible. It works the same way for String[], double[], and every other array type.
Which loop should I use?
| You need... | Use |
|---|---|
| Every value, nothing else (sum, count, print) | Enhanced for |
The position (print 0: ..., find where something is) | Indexed for |
To change elements (a[i] = ...) | Indexed for |
| Backwards, every second element, or part of the array | Indexed for |
| Two arrays side by side (same index in each) | Indexed for |
The enhanced loop is the right default for reading. Reach for the indexed loop when the table says so.
A trap: the loop variable is a copy
In for (int s : scores), the variable s is a copy of the current element. Assigning to s changes the copy and leaves the array alone:
for (int p : prices) {
p = p * 2; // changes only p; prices is unchanged
}To modify an array, use the indexed loop and write prices[i] = .... The same holds for String[]: n = n.toUpperCase() reassigns the local variable only. (Strings cannot be modified at all, as BEG-009 explained.)
Staying inside the bounds
Before you run a loop, ask one question about every array access: is the index at least 0 and at most length - 1? Check the first and the last iteration by hand. When the body reads a neighbour, the range must shrink:
for (int i = 0; i < a.length - 1; i++) {
// a[i] and a[i + 1] are both valid here
}The loop stops at length - 2, so i + 1 is at most length - 1.
The empty array
new int[0] or {} is a legal array of length 0. A loop over it runs zero times, with no error, because 0 < 0 is false from the start. Code that uses a[0] directly (such as starting a "maximum" at a[0]) does crash on an empty array, so test a.length == 0 first when empty input is possible.
Examples
Example 1: two loops over the same array (simple)
public class Ex1 {
public static void main(String[] args) {
int[] scores = {80, 90, 70};
int total = 0;
for (int s : scores) {
total += s;
}
System.out.println("Total: " + total);
for (int i = scores.length - 1; i >= 0; i--) {
System.out.println(i + ": " + scores[i]);
}
}
}Output (compiled and run with JDK 21):
Total: 240
2: 70
1: 90
0: 80The sum needs only values, so enhanced for fits. Printing positions in reverse order needs the index, so the indexed loop fits.
Example 2: the copy trap (practical)
public class Ex2 {
public static void main(String[] args) {
int[] prices = {10, 20, 30};
for (int p : prices) {
p = p * 2;
}
System.out.println(prices[0] + " " + prices[1] + " " + prices[2]);
for (int i = 0; i < prices.length; i++) {
prices[i] = prices[i] * 2;
}
System.out.println(prices[0] + " " + prices[1] + " " + prices[2]);
String[] names = {"Ana", "Omar", "Lina"};
for (String n : names) {
n = n.toUpperCase();
}
System.out.println(names[0]);
}
}Output:
10 20 30
20 40 60
AnaThe first loop doubled only its own copy p. The second loop wrote into the array. The third loop left names unchanged, so the first name is still Ana.
Example 3: neighbours and the off-by-one (realistic)
A common check on sorted-looking data, such as sensor readings, is "how many times is a value equal to the one after it?"
public class Ex3 {
static int countPairs(int[] a) {
int equalNeighbours = 0;
for (int i = 0; i < a.length - 1; i++) {
if (a[i] == a[i + 1]) {
equalNeighbours++;
}
}
return equalNeighbours;
}
public static void main(String[] args) {
System.out.println(countPairs(new int[]{1, 1, 2, 2, 2, 3}));
System.out.println(countPairs(new int[]{5}));
System.out.println(countPairs(new int[]{}));
int[] data = {4, 5, 6};
for (int i = 0; i <= data.length; i++) {
System.out.println(data[i]);
}
}
}Output:
3
0
0
4
5
6
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
at Ex3.main(Ex3.java:18)The bound a.length - 1 keeps a[i + 1] valid. For a one-element and a zero-element array the loop simply runs zero times; the empty array works because 0 - 1 is -1 and 0 < -1 is false. The last loop shows the planted bug: <= lets i reach 3, and the trace points at line 18 and says "Index 3 out of bounds for length 3". The three printed values 4, 5, 6 come out before the crash.
Backend Insight
Backend code spends most of its time in loops over lists: validating every field of a request, totalling every line of an invoice, writing every record of a report. Two habits scale well:
- Prefer the loop that cannot go wrong. If you do not need the index, the enhanced
forremoves a whole class of bugs. Production code is full of bounds errors that a simpler loop would have avoided. - Treat empty as a normal input. A search that returns zero rows, an empty upload, or a filter that matches nothing all produce empty arrays or lists. Code that assumes at least one element works in testing and fails on the first quiet day in production.
Common Mistakes
Mistake 1: <= instead of <
- Incorrect:
for (int i = 0; i <= a.length; i++) - Correct:
for (int i = 0; i < a.length; i++)
With <= the last pass uses index length, which does not exist.
Mistake 2: Trying to change an array through the enhanced-for variable
for (int p : prices) p = p * 2; does nothing to prices. Use prices[i] = prices[i] * 2; inside an indexed loop.
Mistake 3: Reading the neighbour without shrinking the range
for (int i = 0; i < a.length; i++) if (a[i] == a[i + 1]) fails on the last pass. Stop at a.length - 1.
Mistake 4: Starting a maximum at 0 or at a[0] without thinking
Starting at 0 gives a wrong answer when every value is negative. Starting at a[0] crashes when the array is empty. Start at a[0] and guard the empty case first.
Mistake 5: Changing the array's length inside the loop and expecting the bound to move
An array's length never changes, so there is nothing to track. If you think you need "the current size", you probably need a counter variable separate from a.length.
Exercises
Try each exercise yourself before opening the solutions.
Level A: Foundation
A1. Print every element of String[] days = {"Mon", "Tue", "Wed"} on its own line using the enhanced for.
أظهر الحل A1
A1.
for (String d : days) {
System.out.println(d);
}A2. Which loop (indexed or enhanced) fits each task? (a) Add up all values. (b) Print index: value pairs. (c) Set every element to 0.
أظهر الحل A2
A2. (a) Enhanced. (b) Indexed, because it needs the position. (c) Indexed, because it writes elements.
A3. What is wrong with for (int i = 1; i <= a.length; i++) System.out.println(a[i]); if the intention is to print all elements?
أظهر الحل A3
A3. It starts at 1 and skips the first element, and <= lets i reach a.length, which throws ArrayIndexOutOfBoundsException. The correct loop is for (int i = 0; i < a.length; i++).
Level B: Application
B1. Write a loop that adds only the even numbers of an int[] a.
أظهر الحل B1
B1.
int sum = 0;
for (int x : a) {
if (x % 2 == 0) {
sum += x;
}
}B2. Print the elements of a from last to first.
أظهر الحل B2
B2.
for (int i = a.length - 1; i >= 0; i--) {
System.out.println(a[i]);
}B3. What does this print, and why?
int[] a = {1, 2, 3};
for (int x : a) {
x = x + 10;
}
System.out.println(a[0] + a[1] + a[2]);أظهر الحل B3
B3. It prints 6. The variable x is a copy of each element, so adding 10 to x never reaches the array, and the sum is 1 + 2 + 3.
Level C: Challenge
C1. Write static int largest(int[] a) that returns the largest element, or -1 if the array is empty. Explain why -1 is risky here and name a better design in one sentence.
أظهر الحل C1
C1.
static int largest(int[] a) {
if (a.length == 0) {
return -1;
}
int best = a[0];
for (int x : a) {
if (x > best) {
best = x;
}
}
return best;
}-1 is risky because it can also be a real largest value (all numbers negative), so the caller cannot tell the cases apart. A better design is to check for emptiness before calling, or to signal "no value" in a way that cannot be mistaken for data (you will meet options for that later in the course).
C2. Write static int rises(int[] a) that counts the positions where an element is greater than the one before it. For {3, 5, 4, 6, 6, 9} the answer is 3.
أظهر الحل C2
C2.
static int rises(int[] a) {
int count = 0;
for (int i = 1; i < a.length; i++) {
if (a[i] > a[i - 1]) {
count++;
}
}
return count;
}Starting at 1 keeps a[i - 1] valid. The pairs (3,5), (4,6), (6,9) rise, so the count is 3. An empty or one-element array gives 0.
Mini Challenge
Take an int[] of at least eight exam marks. Write a program, with a separate method for each task, that prints: the total, the largest mark and its index, how many marks are below the average, and the marks in reverse order. Test it with an array of one mark and with an empty array, and make sure neither run crashes: the empty case may print a message instead of results.
Check your work: every loop has bounds you verified for the first and last iteration, and you used the enhanced for wherever you did not need an index.
Summary & Next Lesson
Key takeaways
- The indexed loop
for (int i = 0; i < a.length; i++)visits every element; the bound is<, never<=. - The enhanced
for (int x : a)gives you each value with no index, so it cannot go out of range. - The enhanced-for variable is a copy: assigning to it does not change the array.
- Use the indexed loop when you need positions, need to write, or need a partial or reverse walk.
- Before running a loop, check the first and last index it will use. An empty array makes a loop run zero times.
Next lesson
The next lesson, "Two-dimensional arrays", stacks rows of lockers into a grid and uses nested loops to visit every cell.
Self-check
- What is the first and the last index the loop
for (int i = 0; i < a.length; i++)uses? - When can you use the enhanced
for, and when must you use the indexed loop? - Does
for (int x : a) x = 0;clear the array? - How do you stop
a[i + 1]from going out of range? - What happens when a loop runs over an empty array?
أظهر الإجابات
0anda.length - 1.- Enhanced
forwhen you only read values; indexed loop when you need the position, want to write, or want a reverse or partial walk. - No,
xis a copy of each element. - Stop the loop at
i < a.length - 1. - It runs zero times and nothing fails.
References
- Oracle, Java Language Specification SE 21, chapter 10: Arrays. It supports: array indices run from
0tolength - 1, and accessing an index outside that range throwsArrayIndexOutOfBoundsException. - The enhanced
forbehaviour (the loop variable receives the element's value, not a link to its slot) and every output above were checked by compiling and running the code with JDK 21. I did not fetch the enhanced-for section of the specification itself, so the statement that it visits elements in index order from first to last comes from running the examples.
التطبيق
طبّق هذا الدرس بمختبر الوحدة وتمارينها واختبارها.
تعليم الدرس كمكتمل اختياري ويمكنك التراجع عنه في أي وقت. يبقى تقدمك محفوظًا في هذا المتصفح.