배열 — 길이가 고정된 상자
Article
값 세 개를 담아두고 그대로 출력해보자.
int[] scores = { 90, 80, 70 };
System.out.println(scores);
- [I@3af49f1c
[I@3af49f1c. 우리가 넣은 숫자는 어디에도 없다.
에러가 아니다. 자바는 지금 정확히 아는 것을 말했다. 이 이상한 글자를 이해하면 4강에서 배운 참조형이 눈에 보이기 시작한다.
만들고 꺼낸다
배열은 같은 타입의 값을 여러 개 담는다. 만드는 방법은 두 가지다.
int[] a = { 90, 80, 70 }; // 값을 바로 넣는다
int[] b = new int[3]; // 빈 상자 3칸을 만든다
칸에는 0 부터 번호가 붙는다. 6강에서 charAt(1) 이 두 번째 글자였던 것과 같은 규칙이다.
칸 번호는 0부터 시작한다
- a[0]
- 90
- a[1]
- 80
- a[2]
- 70
꺼낼 때도 넣을 때도 대괄호를 쓴다.
System.out.println(a[0]); // 90
a[0] = 100;
System.out.println(a[0]); // 100
빈 상자에는 기본값이 들어 있다
new int[3] 으로 만든 상자를 그냥 열어보자.
int[] a = new int[3];
System.out.println(a[0]);
String[] s = new String[2];
System.out.println(s[0]);
- 0
- null
3강에서 지역 변수는 값을 넣기 전에 꺼내면 컴파일 에러였다. 배열은 다르다. 만드는 순간 모든 칸이 기본값으로 채워진다.
4강의 표에 있던 그 기본값이다. 기본형 칸은 0 이나 false, 참조형 칸은 null.
length 에는 괄호가 없다
칸의 개수는 length 로 안다.
int[] a = { 90, 80, 70 };
System.out.println(a.length); // 3
6강에서 문자열의 길이는 s.length() 였다. 배열은 a.length 다. 괄호가 없다.
헷갈리기 딱 좋은데 이유가 있다. String 은 클래스이고 length() 는 그 클래스의 메서드다. 반면 배열은 클래스가 아니다. 자바가 언어 차원에서 만들어주는 특별한 참조형이라, length 는 메서드가 아니라 그냥 값이다.
반복문과 함께 쓰면 이렇게 된다.
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + " ");
}
i <= a.length 라고 쓰면 안 된다. 8강의 off-by-one 이다. 그리고 그 결과가 다음에 나올 예외다.
없는 칸을 꺼내면
int[] a = { 90, 80, 70 };
System.out.println(a[3]);
- Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException:
- Index 3 out of bounds for length 3
- at Main.main(Main.java:4)
"길이가 3인데 3번을 달라고 했다"고 정확히 말해준다. 마지막 칸은 2 번이다.
2강에서 java Hello 를 아무 인자 없이 실행하고 args[0] 을 꺼내면 죽는다고 했던 게 이 예외다. 그때는 이름만 미뤄뒀다.
컴파일러는 이걸 막아주지 못한다. a[3] 의 3 이 실행 중에 계산되는 값일 수도 있기 때문이다. 실행 에러다. 2강에서 나눈 두 부류 중 아래쪽이다.
길이는 늘어나지 않는다
배열의 가장 큰 제약이다.
int[] a = new int[3];
// a[3] = 100; ← 칸을 하나 더 만들 방법이 없다
a.length 는 만들 때 정해지고 절대 바뀌지 않는다. 값을 하나 더 넣고 싶으면 더 큰 배열을 새로 만들어서 옮겨 담아야 한다.
6강의 String 과 닮았다. 고칠 수 없어서 새로 만든다. 그리고 6강에 StringBuilder 가 있었듯, 배열에도 "늘어나는 상자"가 따로 있다. 16강의 ArrayList 다.
그때까지는 개수가 정해진 값만 배열로 다룬다.
배열을 출력하면 왜 저런 글자가 나오나
이제 맨 처음 문제로 돌아간다.
- [I@3af49f1c
세 토막으로 쪼개서 읽는다.
| 토막 | 뜻 |
|---|---|
[ | 배열이라는 표시 |
I | 담긴 타입이 int 라는 표시 |
@3af49f1c | 이 객체가 놓인 자리를 나타내는 값 |
주소다. 4강에서 참조형 상자에는 주소가 들어 있다고 했다. println 은 상자 안에 든 것을 그대로 보여준 것뿐이다.
내용을 보고 싶으면 자바가 준비해둔 도구를 쓴다.
import java.util.Arrays;
int[] a = { 90, 80, 70 };
System.out.println(Arrays.toString(a));
- [90, 80, 70]
import 는 "다른 곳에 있는 것을 가져다 쓰겠다"는 선언이다. 21강에서 제대로 다룬다. 지금은 파일 맨 위에 적어둔다고만 알면 된다.
배열끼리 비교하면
내용이 같은 배열 두 개를 비교해보자. 6강에서 배운 대로 == 말고 .equals() 를 쓴다.
int[] b = { 1, 2, 3 };
int[] c = { 1, 2, 3 };
System.out.println(b == c);
System.out.println(b.equals(c));
System.out.println(Arrays.equals(b, c));
- false
- false
- true
.equals() 마저 false 다. 6강에서 .equals() 는 내용을 비교한다고 했는데 거짓말이었나?
아니다. String 의 .equals() 가 내용을 비교하도록 만들어져 있었을 뿐이다. 배열의 .equals() 는 그런 대접을 받지 못해서, 그냥 == 와 똑같이 주소를 비교한다.
내용을 비교하려면 Arrays.equals() 를 쓴다.
배열의 배열
배열의 각 칸에 또 배열을 넣을 수 있다.
int[][] g = { {1, 2}, {3, 4, 5} };
System.out.println(g[1][2]); // 5
System.out.println(g[1].length); // 3
- 5
- 3
g[1] 은 {3, 4, 5} 라는 배열이고, 거기서 [2] 번을 꺼내니 5 다.
칸마다 길이가 달라도 된다. 표처럼 반듯할 필요가 없다. 각 칸에 배열의 주소가 들어 있을 뿐이기 때문이다.
정리하면
배열은 길이가 고정된 참조형이고, 출력하면 주소가 나온다.
- 01
번호는 0부터, 마지막은
length - 1넘어가면 ArrayIndexOutOfBoundsException 이다. 실행 에러다.
- 02
length에는 괄호가 없다배열은 클래스가 아니라서 그렇다. 문자열의 length() 와 헷갈리지 않도록.
a.length // 배열 s.length() // 문자열 - 03
만들면 기본값으로 채워진다
int[] 은 0 으로, String[] 은 null 로. 지역 변수와 다르다.
- 04
길이는 늘어나지 않는다
늘어나는 상자가 필요하면 16강의 ArrayList 를 쓴다.
- 05
출력과 비교에는
Arrays를 쓴다println 은 주소를 찍고, equals 는 주소를 비교한다.
System.out.println(Arrays.toString(a)); System.out.println(Arrays.equals(b, c));
이제 값을 담을 줄 안다. 그런데 지금까지 우리가 쓴 코드는 전부 main 안에 있었다.
코드가 길어지면 main 하나가 수백 줄이 된다. 같은 계산을 두 곳에서 하려면 복사해서 붙여야 한다.
다음 강의에서 코드에 이름을 붙인다. 그리고 2강에서 미뤄둔 단어 하나를 마침내 회수한다. void 다.