for와 향상된 for 반복문
반복문은 같은 코드를 여러 번 실행하지만 어떤 값을 순회하고 언제 끝나는지를 명확히 해야 합니다.
전통적인 for는 초기화, 조건, 증감을 한 줄에 모아 인덱스 제어에 적합합니다.
향상된 for는 배열이나 컬렉션의 각 원소를 읽을 때 범위 오류를 줄입니다.
원소를 몇 번째로 처리하는지 필요한지에 따라 형태를 고를 수 있습니다.
이 절에서는 반복 횟수, 인덱스, 원소라는 세 관점을 구분합니다.
전통 for는 인덱스가 필요할 때 쓴다
현재 위치, 역순 순회, 일정 간격 이동처럼 인덱스 자체가 의미 있을 때 적합합니다.
조건에는 배열 길이를 사용해 유효 범위를 벗어나지 않게 합니다.
향상된 for는 원소 중심으로 읽는다
배열의 모든 값을 순서대로 읽을 때 인덱스 변수를 없애 의도를 단순하게 합니다.
현재 순회 변수에 새 값을 대입해도 배열 원소가 바뀌는 것은 아닙니다.
ForIterations.java는 같은 배열을 인덱스 중심과 원소 중심으로 각각 순회합니다. 출력이 같더라도 반복문이 제공하는 정보가 다르므로, 다음 단계에서 위치가 필요한지부터 판단합니다.
배열의 생성과 복사는 5장에서 이어서 배웁니다. 이 절에서는 scores.length가 원소 수를 제공하고 scores[index]가 현재 위치의 값을 읽는다는 두 동작만 가지고 반복 경계를 살펴봅니다.
import java.util.Arrays;
public class ForIterations {
public static void main(String[] args) {
int[] scores = {70, 80, 90};
int indexedTotal = 0;
for (int index = 0; index < scores.length; index++) {
indexedTotal += scores[index];
System.out.println(index + ":" + scores[index]);
}
int valueTotal = 0;
for (int score : scores) {
valueTotal += score;
}
System.out.println(Arrays.toString(scores));
System.out.println(indexedTotal == valueTotal);
}
}합계만 필요하다면 향상된 for가 배열 원소를 직접 드러내 가장 읽기 쉽습니다. 반대로 이전 원소와 비교하거나 현재 위치를 출력해야 한다면 인덱스를 가진 전통 for가 자연스럽습니다.
0:70
1:80
2:90
[70, 80, 90]
true인덱스가 필요한 출력과 원소만 필요한 합계를 서로 다른 반복문으로 표현했습니다.
반복 불변식을 말로 확인한다
매 반복 시작에서 항상 참이어야 하는 조건을 정하면 초기화와 증감 오류를 찾기 쉽습니다.
예를 들어 0 <= i && i <= length가 언제 유지되는지 확인합니다.
합계를 구하는 동안 유지되는 불변식은 “total에는 지금까지 지난 원소의 합이 들어 있다”입니다. 반복 전, 한 번 실행한 뒤, 마지막 원소 뒤에 이 문장이 모두 참인지 확인하면 초기값과 갱신식 오류를 찾기 쉽습니다.
int[] values = {1, 2, 3};
for (int value : values) {
value *= 10; // 순회 변수만 변경, 배열 원소는 그대로
}
for (int i = 0; i < values.length; i++) {
values[i] *= 10; // 인덱스로 실제 원소 교체
}
// values[values.length] = 0; // 실행 시 범위 오류빈 배열과 마지막 원소를 확인한다
길이가 0일 때 한 번도 실행되지 않는지, 마지막 유효 인덱스가 length - 1인지 실행해 봅니다.
반복문의 오류는 양 끝에서 가장 잘 드러납니다.
빈 배열에서는 본문이 한 번도 실행되지 않아야 하고, 길이 1에서는 인덱스 0만 처리해야 합니다. 이 두 입력과 마지막 인덱스 length - 1을 확인하면 <= length처럼 경계를 하나 넘긴 오류를 빠르게 잡을 수 있습니다.
반복문 형태는 코드 길이가 아니라 인덱스를 제어해야 하는지 원소만 읽으면 되는지에 따라 고릅니다.