[CM202] 03. 배열 (Array)
이제 본격적으로 데이터 구조에 대해 다뤄봅시다. 가장 먼저 살펴볼 데이터 구조는 우리가 가장 흔히 사용하는 형태, 배열입니다.
배열 (Array)
배열(Array)은 동일한 자료형의 값들을 연속된 메모리 공간에 저장하는 구조입니다. 배열의 크기는 고정되어 있으며, 연속된 메모리 공간들은 고유한 번호인 색인(Index, 이하 인덱스)으로 식별이 가능합니다. 색인은 반드시 0부터 시작합니다(1이 아니고요).
1
2
// 모든 요소의 값이 0인 길이 n의 배열이 만들어집니다.
double[] a = new double[n];
대부분의 프로그래밍 언어에서 배열의 이름은 곧 그 배열의 첫 번째 요소가 저장된 주소값을 가집니다. 즉, a라는 배열의 n번째 요소를 찾고 싶다면 간단히 $a[n-1]$이라고 표기하면 됩니다(0번째부터니까). 그러면 컴퓨터는 아래와 같이 곧바로 해당 값에 접근할 수 있습니다. 원하는 값을 얻기 위해 배열의 처음부터 순회할 필요가 없다는 것이죠.
\[a + (n-1) \times (\text{요소 하나가 차지하는 메모리 크기})\]정리하자면, 배열의 대표적인 특징은 다음과 같습니다.
- 메모리 공간이 연속적이다
- 크기가 고정되어 있다
- 인덱스를 사용한 접근이 빠르다
Python의 경우 동적 배열인 list를 사용하기 때문에, 요소의 개수에 따라 배열의 크기가 유동적으로 변할 수 있습니다. 그 외에도 Java의 ArrayList, JavaScript의 Array 처럼 각 언어마다 동적 배열을 따로 제공하기도 합니다.
배열의 연산
배열의 가장 큰 특징은 그 위치와 크기가 고정되어 있다는 것입니다. 그렇기 때문에 배열을 통해 연산을 할 때는 조금 번거로운 과정을 거쳐야 합니다. 예를 들어 기존의 배열 a를 배열 b라는 이름으로 복사한다고 생각해봅시다.
아쉽게도, 배열은 숫자처럼 대입한다고 복사가 되지는 않습니다. 배열의 이름은 배열의 첫 번째 주소값을 의미하기 때문입니다. 따라서 $b = a$와 같이 대입을 한다면 두 값은 같은 배열의 첫 번째 주소값을 가리키게 되고, a와 b는 같은 배열을 공유하게 됩니다. 똑같은 배열이 두 개 만들어지는게 아니에요.
배열을 복사하려면 직접 값들을 하나씩 옮겨줘야 합니다. 새로운 배열 객체를 만들고, 동일한 인덱스에 동일한 값을 대입시키는 것이죠.
1
2
3
4
5
double[] b = new double[n];
for (int i=0; i<n; i++) { // 0부터 n-1까지
b[i] = a[i]; // a의 i번째 값을 b의 i번째 자리에 놓는다
}
배열의 요소를 새로 삽입하거나 제거할 때도 순회가 필요합니다. 할당된 공간이 한정되어 있기 때문에, 중간에 값이 생기거나 사라지면 나머지 값들이 자리를 한 칸씩 움직여줘야 하기 때문입니다.
1
2
3
4
5
6
// 배열의 k번째 값에 삽입할 때
for (int i=n-1; i>=k; i--) { // k부터 n-1까지
a[i+1] = a[i]; // a의 i번째 값을 i-1번째 값으로 이동
}
a[k] = x; // k번째에 요소 x 삽입
n++; // 배열의 크기 1 증가
1
2
3
4
5
// 배열의 k번째 값을 삭제할 때
for (int i=k+1; i<n; i++) { // k부터 n-1까지
a[i-1] = a[i]; // a의 i번째 값을 i-1번째 값으로 이동
}
n--; // 배열의 크기 1 감소
배열의 여러 가지 변형
2차원 배열
배열의 특성을 활용하면 2차원 배열도 어렵지 않게 만들 수 있습니다. 2차원 배열의 경우 축 이 두 개이니, 참조할 인덱스 또한 두 개가 있어야 합니다. 따라서 배열의 한 요소가 또 다른 배열을 참조하도록 만들면 됩니다.
1
2
// 모든 요소의 값이 0인 행 길이 n, 열 길이 m의 2차원 배열이 만들어집니다.
double[][] a = new double[n][m];
2차원 배열을 만드는 방법을 알았다면, 자연적으로 그 이상의 차원들, 그러니까 3차원이나 4차원 등의 배열도 만들 수 있을 것입니다.
객체를 담는 배열
배열의 요소가 꼭 숫자나 문자가 되라는 법은 없습니다. 배열엔 메모리에 담을 수 있는 것은 무엇이든 넣을 수 있어요. 다만 이 때는 요소 자체가 아닌, 그 요소가 저장되어 있는 주소값을 담게 됩니다. 앞서 이야기한 다차원 배열에서 배열 안에 배열을 담을 때도 사실은 그 배열의 주소값을 담는 것입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 새로 만든 Dog 클래스
public class Dog {
private String name; // 이름
private int age; // 나이
public Dog(String n, int a) { // 생성자
name = n;
age = a;
}
public String getName() { return name; } // 이름 접근 함수
public int getAge() { return age; } // 나이 접근 함수
}
// Dog 클래스의 객체들을 담는 배열
Dog[] catalog = new Dog[n];
배열은 단순하지만, 앞으로 살펴볼 복잡한 자료구조들의 기반이 되는 출발점입니다. 이제 이 가장 단한간 구조를 이용해 온갖 기상천외한 구조들을 만들어보게 될 거에요.





