배열과 인덱스
같은 타입의 여러 값을 배열에 저장하고 길이와 마지막 인덱스를 구분해 회원가입의 고정 용량 저장소를 만듭니다.
회원이 늘 때마다 email1, email2, email3 변수를 추가하면 같은 합계·출력 코드도 함께 늘어납니다.
배열은 같은 타입의 원소를 하나의 참조와 인덱스로 관리합니다.
길이는 원소 수이고 마지막 인덱스는 길이보다 하나 작습니다.
배열 경계 예외
public final class LengthAsIndex {
public static void main(String[] args) {
int[] age = {20, 30, 40};
System.out.println("length=" + age.length);
System.out.println(age[age.length]);
}
}length=3
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3정상 인덱스는 0, 1, 2입니다.
length는 원소 수 3이지 마지막 위치가 아닙니다.
반복 조건은 index < age.length로 둡니다.
배열의 선언·생성·초기화
public final class ArrayCreation {
public static void main(String[] args) {
int[] age;
age = new int[3];
System.out.println(age[0]);
System.out.println(age[1]);
System.out.println(age[2]);
}
}0
0
0int[] age는 int 배열을 가리킬 변수 선언이고 new int[3]은 길이 3의 배열을 생성합니다.
숫자 원소는 0, boolean은 false, 참조 원소는 null 기본값을 받습니다.
기본값이 저장된 회원을 뜻하지는 않으므로 실제 데이터 수를 별도로 관리해야 합니다.
배열 리터럴로 값과 길이를 함께 정할 수도 있습니다.
int[] age = {20, 30, 40};
String[] emails = {"variables", "conditions", "loops"};생성 뒤 길이는 바뀌지 않습니다.
더 큰 용량이 필요하면 새 배열을 만들고 값을 옮기거나 뒤에서 컬렉션을 사용합니다.
인덱스 기반 반복
public final class ArrayTraversal {
public static void main(String[] args) {
int[] age = {20, 30, 40};
int total = 0;
for (int index = 0; index < age.length; index++) {
total += age[index];
System.out.println(index + "=" + age[index]);
}
System.out.println("total=" + total);
}
}0=20
1=30
2=40
total=90인덱스는 원소를 읽고 바꾸는 위치입니다.
age[1] = 35는 둘째 원소만 변경합니다.
배열 변수에 새 배열을 대입하지 않는 한 같은 배열의 나머지 원소는 유지됩니다.
빈 배열도 유효합니다.
int[] empty = new int[0];길이는 0이고 정상 인덱스가 하나도 없습니다.
index < empty.length 반복은 0회 실행하지만 empty[0]은 예외입니다.
배열 참조 대입
배열 변수에는 배열을 가리키는 참조 값이 들어 있습니다.
다른 배열 변수에 대입하면 원소 전체가 복제되는 것이 아니라 같은 배열을 가리키는 참조가 복사됩니다.
import java.util.Arrays;
public final class ArrayAliasing {
public static void main(String[] args) {
int[] original = {20, 30, 40};
int[] alias = original;
alias[1] = 99;
System.out.println("original=" + Arrays.toString(original));
System.out.println("alias=" + Arrays.toString(alias));
System.out.println("same=" + (original == alias));
}
}original=[20, 99, 40]
alias=[20, 99, 40]
same=true두 변수는 서로 다른 이름이지만 같은 배열 객체를 가리킵니다.
따라서 alias를 통해 둘째 원소를 바꾸면 original로 읽어도 99입니다.
기본형 int 변수의 값 복사와 배열 참조 복사를 같은 방식으로 생각하면 이 동작을 놓치기 쉽습니다.
독립된 배열이 필요하면 새 배열을 만들고 원소를 복사해야 합니다.
import java.util.Arrays;
public final class IndependentArrayCopy {
public static void main(String[] args) {
int[] original = {20, 30, 40};
int[] copied = Arrays.copyOf(original, original.length);
copied[1] = 99;
System.out.println("original=" + Arrays.toString(original));
System.out.println("copied=" + Arrays.toString(copied));
System.out.println("same=" + (original == copied));
}
}original=[20, 30, 40]
copied=[20, 99, 40]
same=falseArrays.copyOf는 지정한 새 길이의 배열을 만들고 기존 원소를 옮깁니다.
새 길이가 더 크면 남은 칸은 기본값 0으로 채워지고, 더 작으면 뒤 원소가 잘립니다.
배열 자체의 length가 바뀐 것이 아니라 다른 길이의 새 배열이 생긴 것입니다.
int[] expanded = Arrays.copyOf(original, 5);
// [20, 30, 40, 0, 0]회원가입의 용량을 늘릴 때 emails와 age를 각각 새 배열로 복사할 수 있지만, 두 배열의 새 길이를 같게 유지해야 합니다.
또한 size는 실제 회원 수 그대로 두어 새로 생긴 기본값 칸을 회원 정보로 취급하지 않습니다.
복사 전후에 다음 불변식을 확인합니다.
0 <= size && size <= array.lengthindex0부터size- 1까지는 실제 회원입니다.- 복사 뒤 같은
index의 기존 값은 그대로입니다. size부터 새length- 1까지는 아직 사용하지 않는 공간입니다.
병렬 배열의 관계
클래스를 배우기 전에는 이메일과 나이를 같은 길이의 두 배열에 저장할 수 있습니다.
public final class ParallelArrays {
public static void main(String[] args) {
String[] emails = {"kim@example.com", "lee@example.com"};
int[] age = {40, 50};
if (emails.length != age.length) {
throw new IllegalStateException("parallel lengths differ");
}
for (int index = 0; index < emails.length; index++) {
System.out.println(emails[index] + "=" + age[index]);
}
}
}kim@example.com=40
lee@example.com=50인덱스 0의 두 값이 첫 회원이고 인덱스 1의 두 값이 둘째 회원입니다.
정렬·삭제·삽입 때 한 배열만 움직이면 관계가 깨지는 약점이 있습니다.
ch4에서 하나의 객체로 묶기 전까지 모든 변경이 같은 인덱스를 유지해야 합니다.
용량과 저장 개수
public final class FixedMemberRegistry {
public static void main(String[] args) {
String[] emails = new String[3];
int[] age = new int[3];
int size = 0;
emails[size] = "kim@example.com";
age[size] = 40;
size++;
emails[size] = "lee@example.com";
age[size] = 50;
size++;
for (int index = 0; index < size; index++) {
System.out.println(emails[index] + "=" + age[index]);
}
System.out.println("size=" + size + ", capacity=" + emails.length);
}
}kim@example.com=40
lee@example.com=50
size=2, capacity=3emails.length는 세 칸의 용량이고 size는 의미 있는 두 회원의 수입니다.
length까지 순회하면 마지막 빈 칸이 null=0으로 출력됩니다.
추가할 때는 먼저 size < emails.length를 확인하고 두 배열을 같은 size 위치에 쓴 뒤 마지막에 size를 증가시킵니다.
연습 문제
{25, 55, 35, 40} 배열에서 최댓값과 인덱스를 찾으세요.
첫 원소를 현재 최댓값으로 시작해 빈 가짜 초기값을 만들지 않습니다.
해설 보기
public final class OldestMember {
public static void main(String[] args) {
int[] age = {25, 55, 35, 40};
int maxIndex = 0;
for (int index = 1; index < age.length; index++) {
if (age[index] > age[maxIndex]) {
maxIndex = index;
}
}
System.out.println("index=" + maxIndex);
System.out.println("age=" + age[maxIndex]);
}
}index=1
age=55빈 배열에서는 index 0도 없으므로 이 알고리즘 전에 length가 0인지 검사해야 합니다.