[JAVA] 이것이 자바다_3
Updated:
JAVA의 배열 타입에 대해 알아보자.
배열이란?
변수는 한 개의 데이터만 저장할 수 있다.
따라서 저장해야 할 데이터의 수가 많아지면 그만큼 많은 변수가 필요하다.
하지만 변수를 많이 선언하면 매우 비효율적이고 지루한 코딩이 된다.
같은 타입의 많은 양의 데이터를 다루는 프로그램에서는 좀 더 효율적인 방법이 필요한데 이것이 배열이다.
배열은 같은 타입의 데이터를 연속된 공간에 나열시키고, 각 데이터에 인덱스를 부여해 놓은 자료구조이다.
배열 선언
배열을 사용하기 위해서는 우선 배열 변수를 선언해야 한다.
배열 변수 선언은 다음과 같이 두 가지 형태로 작성할 수 있다.
타입[] 변수;
타입 변수[];
대괄호 []는 배열 변수를 선언하는 기호로 사용되는데, 타입 뒤에 붙을 수도 있고 변수 뒤에 붙을 수도 있다.
타입은 배열에 저장될 데이터의 타입이다.
배열 변수는 참조 변수에 속한다.
배열도 객체이므로 힙 영역에 생성되고 배열 변수는 힙 영역의 배열 객체를 참조하게 된다.
참조할 배열 객체가 없다면 배열 변수는 아래와 같이 null 값으로 초기화될 수 있다.
타입[] 변수 = null;
만약 배열 변수가 null 값을 가진 상태에서 변수[인덱스]로 값을 읽거나 저장하게 되면 NullPointerException이 발생한다.
배열 변수는 배열을 생성하고 참조하는 상태에서 값을 저장하거나 읽어야 한다.
배열 생성
배열을 생성하는 방법에는 두 가지 방법이 있다.
- 값 목록으로 배열 생성
- new 연산자로 배열 생성
배열 항목에 저장될 값의 목록이 있다면, 아래와 같이 간단하게 배열 객체를 만들 수 있다.
데이터타입[] 변수 = {값0, 값1, 값2, 값3, ...};
String[] names = {"박동현", "박동순", "박동민"};
값 목록으로 배열 객체를 생성할 때 주의할 점이 있다.
배열 변수를 이미 선언한 후에 다른 실행문에서 중괄호를 사용한 배열 생성은 허용되지 않는다.
타입[] 변수:
변수 = {값0, 값1, 값2, 값3, ...} // 컴파일 에러
배열 변수를 미리 선언한 후, 값 목록들이 나중에 결정되는 상황이라면 아래와 같이 new 연산자를 사용해서 값 목록을 지정한다.
new 연산자 바로 뒤에는 배열 변수 선언에서 사용한 “타입[]”를 붙여주고 중괄호 {}에는 값들을 나열한다.
변수 = new 타입[] {값0, 값1, 값2, 값3, ...};
예를 들어 배열 names를 아래와 같이 생성할 수 있다.
String[] names = null;
names = new String[] {"박동현", "박동순", "박동민"};
메소드의 매개값이 배열일 경우에도 마찬가지다.
아래와 같이 매개 변수로 int[] 배열이 선언된 add() 메소드가 있을 경우, 값 목록으로 배열을 생성함과 동시에 add() 메소드의 매개값으로 사용하고자 할 때는 반드시 new 연산자를 사용한다.
int add(int[] scores){...}
-----------------------------
int result = add( {95, 85, 90} ); // 컴파일 에러
int result = add( new int[] {95, 85, 90} );
값의 목록을 가지고 잇지 않지만, 향후 값들을 저장할 배열을 미리 만들고 싶다면 new 연산자로 다음과 같이 배열 객체를 생성한다.
타입[] 변수 = new 타입[길이];
길이는 배열이 저장할 수 있는 값의 수를 말한다.
new 연산자로 배열을 생성할 경우에는 이미 배열 변수가 선언된 후에도 가능하다.
타입[] 변수 = null;
변수 = new 타입[길이];
new 연산자로 배열을 처음 생성할 경우, 배열은 자동적으로 기본값으로 초기화된다.
학생 30명의 점수를 저장할 배열을 다음과 같이 생성한다고 가정한다.
int[] scores = new int[30];
scores 배열은 int 타입 배열이므로 다음과 같이 scores[0] ~ scores[29]까지 모두 기본값 0으로 초기화된다. 만약 String 배열을 생성했다면 names[0]에서 names[29]까지 모두 null 값으로 초기화된다.
String[] names = new String[30];
배열 길이
배열의 길이란 배열에 저장할 수 있는 전체 항목 수를 말한다.
코드에서 배열의 길이를 얻으려면 다음과 같이 배열 객체의 length 필드를 읽으면 된다.
참고로 필드는 객체 내부의 데이터를 말한다.
배열변수.length;
length 필드는 읽기 전용 필드이기 때문에 값을 바꿀 수 없다. 그래서 다음과 같이 작성하면 안된다.
intArray.length = 10; // 잘못된 코드
다차원 배열
지금까지 살펴본 배열은 1차원 배열이다.
이와는 달리 값들이 행과 열로서 구성된 배열을 2차원 배열이라 한다.
예를 들어 2행 3열의 행렬을 만들기 위해서는 다음과 같은 코드를 사용한다.
int[][] scores = new int[2][3];
이 코드는 메모리에 다음과 같이 세 개의 배열 객체를 생성한다.
배열 변수인 scores는 길이 2인 배열 A를 참조한다.
배열 A의 scores[0]은 다시 길이 3인 배열 B를 참조한다.
그리고 scores[1] 역시 길이 3인 배열 C를 참조한다.
scores[0]과 scores[1]은 모두 배열을 참조하는 변수 역할을 한다.
따라서 각 배열의 길이는 다음과 같이 얻을 수 있다.
scores.length // 2(배열 A의 길이)
scores[0].length // 3(배열 B의 길이)
scores[1].length // 3(배열 C의 길이)
JAVA는 일차원 배열이 서로 연결된 구조로 다차원 배열을 구현하기 때문에 수학 행렬 구조가 아닌 아래와 같은 계단식 구조를 가질 수 있다.
int[][] scores = new int[2][];
scores[0] = new int[2];
scores[1] = new int[3];
위 코드는 메모리에 다음과 같이 배열 객체를 생성시킨다.
이 경우 배열 항목의 수를 조사해보면 다음과 같다.
scores.length // 2(배열 A의 길이)
scores[0].length // 2(배열 B의 길이)
scores[1].length // 3(배열 C의 길이)
이런 형태의 배열에서 주의할 점은 정확한 배열의 길이를 알고 인덱스를 사용해야 한다.
scores[0][2]는 ArrayIndexOutOfBoundsException을 발생시킨다.
이유는 배열 B 객체의 마지막 인덱스는 1이기 때문이다.
객체를 참조하는 배열
기본 타입 배열은 각 항목에 직접 값을 갖고 있지만, 참조 타입 배열은 각 항목에 객체의 번지를 가지고 있다.
예를 들어 String은 클래스 타입이므로 String[] 배열은 각 항목에 문자열이 아니라, String 객체의 주소를 가지고 있다.
즉 String 객체를 참조하게 된다.
String[] strArray = new String[3];
strArray[0] = "Java";
strArray[1] = "C++";
strArray[2] = "C#";
위 코드는 배열 변수 strArray를 선언하고 3개의 문자열을 참조하는 배열을 생성한다.
그림으로 표현하면 다음과 같다.
따라서 String[] 배열의 항목도 결국 String 변수와 동일하게 취급되어야 한다.
예를 들어 String[] 배열 항목 간에 문자열을 비교하기 위해서는 == 연산자 대신 equals() 메소드를 사용해야 한다.
==는 객체의 번지 비교이기 때문에 문자열 비교에 사용할 수 없다.
배열 복사
배열은 한 번 생성하면 크기를 변경할 수 없기 때문에 더 많은 저장 공간이 필요하다면 보다 큰 배열을 새로 만들고 이전 배열로부터 항목 값들을 복사해야 한다.
배열 간의 항복 값들을 복사하려면 for문을 사용하거나 System.arraycopy() 메소드를 사용하면 된다.
for문으로 배열을 복사하는 코드는 다음과 같다.
public class ArrayCopyByForExample{
public static void main(String[] args){
int[] oldIntArray = {1, 2, 3};
int[] newIntArray = new int[5];
for(int i = 0; i<oldIntArray.length; i++){
newIntArray[i] = oldIntArray[i];
}
for(int i = 0; i<newIntArray.length; i++){
System.out.print(newIntArray[i] + ", ");
}
}
}
복사되지 않은 항목은 int[] 배열의 기본 초기값 0이 그대로 유지된다.
이번에는 System.arraycopy() 메소드를 이용해서 배열을 복사해본다.
System.arraycopy()를 호출하는 방법은 다음과 같다.
System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
src 매개값은 원본 배열이고, srcPos는 원본 배열에서 복사할 항목의 시작 인덱스이다.
dest 매개 값은 새 배열이고, destPos는 새 배열에서 붙여넣을 시작 인덱스이다.
마지막으로 length는 복사할 개수이다.
예를 들어 원본 배열 arr1의 모든 항목을 arr2에 복사하려면 다음과 같이 System.arraycopy() 메소드를 호출한다.
System.arraycopy(arr1, 0, arr2, 0, arr1.length);
System.arraycopy() 메소드로 배열을 복사하는 코드는 다음과 같다.
public class ArrayCopyExample{
public static void main(String[] args){
String[] oldStrArray = {"java", "array", "copy"};
String[] newStrArray = new String[5];
System.arraycopy(oldStrArray, 0, newStrArray, 0, oldStrArray.length);
for(int i = 0; i<newStrArray.length; i++){
System.out.print(newStrArray[i] + ", ");
}
}
}
복사되지 않은 항목은 String[] 배열의 기본 초기값 null이 그대로 유지된다.
참조 타입 배열일 경우, 배열 복사가 되면 복사되는 값이 객체의 번지이므로 새 배열의 항목은 이전 배열의 항목이 참조하는 객체와 동일하다.
이것을 얕은 복사(shallow copy) 라고 한다.
반대로 깊은 복사(deep copy) 는 참조하는 객체도 별도로 생성하는 것을 말한다.
JAVA의 배열 타입을 정리해보았다.
C나 C++의 배열과 유사한 구조긴 하지만 메소드에 대해서도 알게 되었고, 배열에 대해 깊은 이해를 할 수 있었다.
배열은 기본 중에 기본이므로 확실히 짚고 넘어가야겠다.
Leave a comment