학습 자료

배열 — 길이가 고정된 상자


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) 이 두 번째 글자였던 것과 같은 규칙이다.

int[] a = { 90, 80, 70 };

칸 번호는 0부터 시작한다

a[0]
90
a[1]
80
a[2]
70
칸이 세 개면 마지막 번호는 2 다. 3이 아니다.

꺼낼 때도 넣을 때도 대괄호를 쓴다.

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 다.

칸마다 길이가 달라도 된다. 표처럼 반듯할 필요가 없다. 각 칸에 배열의 주소가 들어 있을 뿐이기 때문이다.

정리하면

배열은 길이가 고정된 참조형이고, 출력하면 주소가 나온다.

다시 짚어보기
  1. 01

    번호는 0부터, 마지막은 length - 1

    넘어가면 ArrayIndexOutOfBoundsException 이다. 실행 에러다.

  2. 02

    length 에는 괄호가 없다

    배열은 클래스가 아니라서 그렇다. 문자열의 length() 와 헷갈리지 않도록.

    a.length      // 배열
    s.length()    // 문자열
  3. 03

    만들면 기본값으로 채워진다

    int[] 은 0 으로, String[] 은 null 로. 지역 변수와 다르다.

  4. 04

    길이는 늘어나지 않는다

    늘어나는 상자가 필요하면 16강의 ArrayList 를 쓴다.

  5. 05

    출력과 비교에는 Arrays 를 쓴다

    println 은 주소를 찍고, equals 는 주소를 비교한다.

    System.out.println(Arrays.toString(a));
    System.out.println(Arrays.equals(b, c));

이제 값을 담을 줄 안다. 그런데 지금까지 우리가 쓴 코드는 전부 main 안에 있었다.

코드가 길어지면 main 하나가 수백 줄이 된다. 같은 계산을 두 곳에서 하려면 복사해서 붙여야 한다.

다음 강의에서 코드에 이름을 붙인다. 그리고 2강에서 미뤄둔 단어 하나를 마침내 회수한다. void 다.

배열 — 길이가 고정된 상자 — 디코드랩(DCODELAB)