목차
<배열이란?>
자료를 순차적으로 관리하는 구조, 배열.
만약 수십개의 순차적인 변수를 관리해야 한다고 했을 때, 일일히 변수를 대입하기 힘들 것이다.
이때 사용하는 자료형이 배열(array)이다. 배열은 자료 구조의 가장 기초 내용이다.
배열을 사용하면 자료형이 같은 자료 여러 개를 한 번에 관리가 가능하다.
배열은 자료가 연속으로 나열된 자료 구조.
<배열 선언과 초기화>
배열 선언
배열을 사용하려면 먼저 배열 선언을 해야함. 배열도 변수와 마찬가지로 자료형을 함께 선언한다.
int[] array1 = new int[5];
//자료형[] 배열이름 = new 자료형[개수];
int array1 [] = new int[5];
//자료형 배열이름 [] = new 자료형[개수];
배열을 이루는 각각의 자료를 배열 요소라고 한다. 이 배열 요소들은 자료형이 모두 같다.
먼저 저장하려는 자료의 성격에 맞게 자료형을 정하고(위에서는 intager), 선언하려는 배열 요소 개수만큼 []안에 넣는다.
new예약어는 배열을 새로 만들라는 의미.
int[] studentIDs = new int[10];
// int형 요소가 10개인 배열 선언
위 문장은 int형 요소가 10개인 배열을 선언한 것.
이렇게 선언했을 때 메모리의 상태를 보면.
배열을 선언하면 선언한 자료형과 배열 길이에 따라 메모리가 할당됨.
위 그림을 보면 자료형이 int형이므로 배열 요소를 저장할 수 있는 공간의 크기는 전부 4바이트로 동일하며, 배열 요소를 저장할 수 있는 공간이 총 10개이므로 이 배열을 위해 총 40바이트의 메모리가 할당이 되었다.
배열 초기화
자바에서 배열을 선언하며 그와 동시에 각 요소의 값이 초기화된다.
배열의 자료형에 따라
정수는 0, 실수는 0.0, 객체 배열은 null로 초기화 되며, 다음처럼 배열 선언과 동시에 특정 값으로 초기화할 수도 있다.
배열이 초기화 요소의 개수만큼 생성되므로 []안의 개수는 생략해야 한다.
int[] studentIDs = new int[]{101, 102, 103}; // 개수는 생략 가능
int[] studentIDs = new int[3]{101, 102, 103}; // 오류 발생
선언과 동시에 초기화할 때 다음과 같이 new int[]부분을 생략할 수도 있다.
int형 요소가 3개인 배열을 생성한다는 의미로 new int[]를 생략해도 된다.
int[] studentIDs = {101, 102, 103}; //int형 요소가 3개인 배열 생성
하지만 다음과 같이 배열의 자료형을 먼저 선언하고 초기화 하는 경우엔 new int[]를 생략할 수 없다.
int[] studentIDs; // 배열 자료형 선언
studentIDs = new int[]{101, 102, 103}; // new int[]를 생략할 수 없다.
studentIDs = {101, 102, 103}; // 오류 발생 (new int[]를 생략함)
<배열 사용하기>
선언한 배열의 각 요소에 값을 넣을 때나 배열 요소에 있는 값을 가져올 때는 []를 사용한다.
만약 배열의 첫 번째 요소에 값 10을 저장한다면 다음처럼 코드를 작성.
studentIDs[0] = 10; //배열의 첫 번째 요소에 값 10을 저장
첫 번째 요소에 값을 저장하는데 []안에는 0이 들어간다.
인덱스 연산자 []
[]는 배열을 처음 선언할 때 사용한 연산자다.
배열 이름에 []을 사용하는 것을 '인덱스 연산자'라고 하는데, 인덱스 연산자의 기능은 배열 요소가 저장된 메모리 위치를 찾아주는 역할을 한다. 변수 이름으로 변수가 저장된 메모리의 위치를 찾는 것 처럼, 배열에서 [i] 인덱스 연산을 하면 i번째 요소의 위치를 찾아 해당 위치의 메모리에 값을 넣거나 이미 저장되어 있는 값을 가져와서 사용할 수 있다.
num[3] = 25;
//num배열의 네 번째 요소에 값 25를 저장 (배열의 요소에 값 저장하기)
age = num[3];
//age변수에 num배열의 네 번째 요소 값을 저장 (배열 요소의 값 가져오기)
배열의 순서는 0부터
배열 길이가 n이라고 하면, 배열 순서는 0번부터 n-1번까지다.
0번이 배열의 첫 번째 요소. for반복문으로 확인해보자.
package array;
public class arrayTest {
public static void main(String[] args) {
int[] num = new int[] {1,2,3,4,5,6,7,8,9,10};
for(int i = 0; i < num.length; i++) {
System.out.println(i+"번째 요소 값은 "+num[i]);
} // 배열의 첫 요소(num[0]부터 num[9]까지 10개 요소 값 출력
}
}
Run하면 :
자바의 배열은 배열 길이를 나타내는 length속성을 가진다. 자바에서 배열 길이는 처음에 선언한 배열 전체 요소 개수를 의미.
for문의 조건에서 얼만큼 반복할지 결정해야 하는데, 배열 요소 끝까지 반복하기 위해서는 배열 전체 길이(length)를 넣으면 된다.
따라서 num.length값은 10이 되는 것.
전체 배열 길이와 유효한 요소 값
우리가 배열을 사용할 때 처음 선언한 배열 길이만큼 값을 저장해서 사용하는 경우는 많지 않다. 따라서 전체 배열 길이와 현재 배열에 유효한 값이 저장되어 있는 배열 요소 개수가 같다고 혼동하면 큰 일.
package array;
public class ArrayTest2 {
public static void main(String[] args) {
double[] data = new double[5];
//double형으로 길이 5인 배열 선언.
data[0] = 10.0; //0번 자리에 10.0 대입
data[1] = 20.0; //1번 자리에 20.0 대입
data[2] = 30.0; //2번 자리에 30.0 대입
for(int i = 0; i < data.length; i++) {
System.out.println(data[i]);
} //전체 길이(data.length)만큼 반복
}
}
Run하면 :
double형으로 길이가 5인 배열을 선언함.
자바에서 정수 배열과 실수 배열을 별도로 초기화 하지 않고 선언하면 요소 값은 0임.
data[0] ~ data[2]까지만 값을 저장하여 data[3]~[4]값은 0으로 출력됨.
그럼 유효한 값만 출력하려면 어떻게 해야 할까?
package array;
public class ArrayTest3 {
public static void main(String[] args) {
double[] data = new double[5];
int size = 0;
//유효한 요소 개수를 저장할 변수 선언
data[0] = 10.0; size++; //0번 자리에 10.0 대입, size변수 값 증가.
data[1] = 20.0; size++; //1번 자리에 20.0 대입, size변수 값 증가.
data[2] = 30.0; size++; //2번 자리에 30.0 대입, size변수 값 증가.
for(int i = 0; i < size; i++) {
System.out.println(data[i]);
} //유효한 요소 개수 만큼(size)만큼 반복
}
}
Run하면 :
유효한 값이 저장된 요소 개수를 저장할 size변수를 선언.
배열 요소에 값이 정해질 때마다 size++;
그리고 반복문에 i<size;로 하여 유효한 요소 수만큼 반복하도록 함.
문자 저장 배열 만들기
문자 자료형 배열을 만들고 알파벳 대문자를 A~Z까지 저장.
각 요소 값을 알파벳 문자와 정수값(아스키 코드 값)으로 출력해보자.
package array;
public class CharArray {
public static void main(String[] args) {
char[] alphabets = new char[26];
char ch = 'A'; //아스키코드로 65
for(int i = 0; i < alphabets.length; i++, ch++) {
alphabets[i] = ch; //아스키 값으로 각 요소에 저장
}
for(int i = 0; i < alphabets.length; i++) {
System.out.println(alphabets[i] + ", " + (int)alphabets[i]);
}
}
}
Run :
문자형 요소 26개짜리 배열을 선언하고,
char ch = 'A'; 로 문자형 'A'도 선언함.
그리고 첫 번째 for문을 사용해 문자형 배열 0~25자리 까지 A~Z를 대입했다.
( alphabets[i] = ch; )
<객체 배열 사용하기>
참조 자료형으로 선언하는 '객체 배열'에 대해 알아보자.
동일한 기본 자료형 변수 여러개를 배열로 사용할 수 있듯 참조 자료형 변수도 여러 개를 배열로 사용할 수 있다.
객체 배열은 int, char등 기본 자료형 배열과 사용 방법이 다름.
객체 배열 생성
package array;
public class Book {
private String bookName;
private String author;
public Book() {} //default const.
public Book(String bookName, String author) {
this.bookName = bookName;
this.author = author;
} // bookName, author을 매개 변수로 받는 const.
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public void bookShowInfo() {
System.out.println(bookName+", "+author);
} //책 정보를 출력하는 메서드
}
Book클래스는 책 이름과 저자를 멤버 변수로 가지는 클래스.
디폴트 생성자, 책 이름과 저자를 매개 변수로 받는 생성자
다른 코드에서 이 클래스를 사용할 때 멤버 변수 값을 가져오거나 지정할 수 있도록 get(), set()메서드도 구현.
책의 정보를 출력해주는 showBookInfo()메서드도 구현.
package array;
public class BookArray {
public static void main(String[] args) {
Book[] library = new Book[5];
//Book클래스형으로 객체 배열 생성
for(int i =0; i < library.length; i++) {
System.out.println(library[i]);
}
}
}
Run :
코드의 내용만 보면 인스턴스 5개가 생성된 것처럼 보이지만, 아니다.
여기서는 인스턴스 주소 값을 담을 공간이 5개가 생긴 것이다.
즉 이 문장을 실행하면 Book 주소 값을 담을 공간이 5개 만들어지고, 자동으로 각 공간은 '비어있다'로 초기화 된 것.
인스턴스를 배열에 저장
package array;
public class BookArray2 {
public static void main(String[] args) {
Book[] library = new Book[5];
library[0] = new Book("태백1","조정래");
library[1] = new Book("태백2","조정래");
library[2] = new Book("태백3","조정래");
library[3] = new Book("태백4","조정래");
library[4] = new Book("태백5","조정래");
// 인스턴트 생성 후 배열에 저장
for(int i =0; i < library.length; i++) {
library[i].showBookInfo();
} // showBookInfo()메서드를 이용
for(int i =0; i < library.length; i++) {
System.out.println(library[i]);
}
}
}
Run :
배열의 각 요소에 Book인스턴스를 만들어 직접 저장.
각 Book의 인스턴스값들과 Book인스턴스를 저장한 메모리 주소가 함께 출력되었다.
<배열 복사하기>
System.arraycopy()메서드
기존 배열과 자료형 및 배열 크기가 똑같은 배열을 새로 만들거나, 배열의 모든 요소에 자료가 꽉차서 더 큰 배열을 만들어 기존 배열에 저장된 자료를 가져오려 할 때 배열을 복사하게 된다.
배열의 복사 방법은 두 가지가 있다.
1. 기존 배열과 배열 길이가 같거나 더 긴 배열을 만들고 for문을 사용해 각 요소 값을 반복해서 복사하는 방법. (간단)
2. System.arraycopy()메서드를 사용하는 방법.
System.arraycopy(src, srcPos, dest, destPos, length)메서드에서 각 매개변수의 의미는 다음과 같다.
System.arraycopy()메서드를 사용해 배열을 복사해 보자.
package array;
public class Arraycopy {
public static void main(String[] args) {
int[] array1 = {10, 20, 30, 40, 50};
int[] array2 = {1, 2, 3, 4, 5};
System.arraycopy(array1, 0, array2, 1, 4);
for(int i = 0; i < array2.length; i++) {
System.out.println(array2[i]);
}
}
}
run :
System.arraycopy(array1, 0, array2, 1, 4);은
array1배열의 요소 0번 부터 4개(10, 20, 30, 40)을 복사하여
array2배열의 요소 1번 자리 부터(2 ~ 5) 차례로 값을 변경한다.
만일 복사할 대상 배열의 전체길이가 복사할 요소 개수보다 작거나, 붙여넣기 할 자리가 부족?하면 오류가 난다.
객체 배열 복사하기
객체 배열도 마찬가지로 복사해서 사용 가능하다.
기존 만들어 둔 Book클래스를 활용해 코드를 써보자. (같은 패키지)
package array;
public class ObjectCopy1 {
public static void main(String[] args) {
Book[] bookArray1 = new Book[3];
Book[] bookArray2 = new Book[3];
bookArray1[0] = new Book("태백산맥1","조정래");
bookArray1[1] = new Book("태백산맥2","조정래");
bookArray1[2] = new Book("태백산맥3","조정래");
System.arraycopy(bookArray1, 0, bookArray2, 0, 3);
for(int i = 0; i < bookArray2.length; i++) {
bookArray2[i].showBookInfo();
//Book클래스에서 생성한 메서드
}
}
}
run:
bookArray1 배열에서 bookArray2배열로 요소 값이 잘 복사된 것을 알 수 있다.
그런데,
bookArray2배열의 인스턴스를 따로 만들지 않아도 요소가 잘 출력된다. 왜 그런 것일까?
얕은 복사
위 예제에서 bookArray1배열 요소 값을 하나 변경해 보도록 하자.
package array;
public class ObjectCopy2 {
public static void main(String[] args) {
Book[] bookArray1 = new Book[3];
Book[] bookArray2 = new Book[3];
bookArray1[0] = new Book("태백산맥1","조정래");
bookArray1[1] = new Book("태백산맥2","조정래");
bookArray1[2] = new Book("태백산맥3","조정래");
System.arraycopy(bookArray1, 0, bookArray2, 0, 3);
for(int i = 0; i < bookArray2.length; i++) {
bookArray2[i].showBookInfo();
}
bookArray1[0].setBookName("나목");
bookArray1[0].setAuthor("박완서");
// bookArray1의 0번 요소값 변경
System.out.println("===bookArray1===");
for(int i = 0; i < bookArray1.length; i++) {
bookArray1[i].showBookInfo();
}
System.out.println("===bookArray2===");
for(int i = 0; i < bookArray2.length; i++) {
bookArray2[i].showBookInfo();
}
}
}
run :
bookArray1 배열 요소 값을 변경했는데,
bookArray2 배열 요소 값도 같이 변경되었다.
그 이유는 객체 배열의 요소에 저장된 값은 인스턴스가 아닌, 인스턴스 주소 값이기 때문이다.
따라서 객체 배열을 복사할 때 인스턴스를 따로 생성하는 게 아니라 기존 인스턴스의 주소 값만 복사를 하는 것이다.
(윈도우의 바로가기 느낌쓰)
이와 같은 복사를 주소 값만 복사한다고 해서 '얕은 복사(shallow copy)'라고 한다.
그럼 인스턴스 값만 복사하고, bookArray1 배열과 bookArray2배열의 각 요소가 서로 다른 인스턴스를 가리키게 하려면 어떻게 해야 할까?
깊은 복사
반복문을 사용하거나, System.arraycopy() 메서드를 사용하거나 객체 배열을 복사하면 항상 인스턴스 주소가 복사된다.
하지만 인스턴스를 따로 관리하고 싶다면 직접 인스턴스를 생성하고 그 값을 복사해야 한다.
이것을 '깊은 복사(deep copy)'라고 부른다.
package array;
public class ObjectCopy3 {
public static void main(String[] args) {
Book[] bookArray1 = new Book[3];
Book[] bookArray2 = new Book[3];
bookArray1[0] = new Book("태백산맥1","조정래");
bookArray1[1] = new Book("태백산맥2","조정래");
bookArray1[2] = new Book("태백산맥3","조정래");
bookArray2[0] = new Book();
bookArray2[1] = new Book();
bookArray2[2] = new Book();
//default 생성자로 bookArray2의 배열 인스턴스 생성(null)
for(int i = 0; i < bookArray1.length; i++) {
bookArray2[i].setBookName(bookArray1[i].getBookName());
bookArray2[i].setAuthor(bookArray1[i].getAuthor());
} //bookArray1 배열 요소를 새로 생성된 bookArray2배열 인스턴스에 복사
for(int i = 0; i < bookArray2.length; i++) {
bookArray2[i].showBookInfo();
} // bookArray2 배열 요소 값 출력
bookArray1[0].setBookName("나목");
bookArray1[0].setAuthor("박완서");
// bookArray1의 0번 요소값 변경
System.out.println("===bookArray1===");
for(int i = 0; i < bookArray1.length; i++) {
bookArray1[i].showBookInfo();
} //bookArray1 배열 요소 값 출력
System.out.println("===bookArray2===");
for(int i = 0; i < bookArray2.length; i++) {
bookArray2[i].showBookInfo();
} //bookArray2 배열 요소 값 출력
}
}
run:
<향상된 for문과 배열>
자바 5부터 제공되는 향상된 'for문(enhanced for loop)'은 배열의 처음에서 끝까지 모든 요소를 참조할 때 사용하면 편리한 반복문.
for(변수 : 배열){
반복 실행문;
}
배열 요소 값을 순서대로 하나씩 가져와 변수에 대입한다.
따로 초기화와 종료 조건이 없어서 처음부터 끝 요소까지 실행한다.
package array;
public class EnhancedForLoop {
public static void main(String[] args) {
String[] strArray = {"Java","Android","C","JavaScript","Python"};
for(String lang : strArray) {
System.out.println(lang);
} //변수 lang에 strArray 배열의 각 요소가 대입된다.
}
}
run:
String형으로 선언된 strArray배열에 문자열 5개를 저장했다.
향상된 for문을 사용해서 String형 lang변수에 strArray 배열 요소값을 순서대로 가져와 대입하게 된다.
[Do it! 자바 프로그래밍 입문] 도서로 공부하며 정리한 글입니다.