Welcome! Everything is fine.

[2-2] 배열과 ArrayList(1) ~ ArrayList 클래스(4) 본문

자격증 및 기타 활동/J2KB

[2-2] 배열과 ArrayList(1) ~ ArrayList 클래스(4)

개발곰발 2021. 8. 19.
728x90

💡 배열

배열 선언하기

배열은 프로그래밍에서 매우 많이 사용하는 자료구조로, 여러개의 동일한 자료형을 한꺼번에 순차적으로 관리할 수 있다. 일일히 변수를 선언할 수도 있지만 그 수가 많아지면 매우 불편하기 때문에 배열을 사용하는 것이 효율적이다. 인덱스 혹은 첨자연산자[ ]를 사용하여 사용한다. 모든 배열의 순서는 0부터 시작하며, n개 배열은 0부터 n-1위치까지 자료가 존재한다.배열을 선언하는 방법은 두 가지가 있으며, 다음과 같다.

 

// 자료형[] 배열이름 = new 자료형[개수];
int[] arr = new int[10];

// 자료형 배열이름[] = new 자료형[개수];
int arr[] = new int[10];

또한 배열은 고정된 길이를 가지기 때문에(Fixed-length) 배열을 사용할 때는 먼저 몇 개를 쓸지 선언하고 사용해야 하고, 연속된 자료구조이기 때문에 자리를 건너뛰어서 요소를 삽입할 수 없다. 만약 배열을 더 추가하고 싶다면 더 긴 배열을 다시 만들어서 그 안에 기존의 값을 복사하여 넣어주어야 한다. 이러한 불편함이 있기 때문에 ArrayList 클래스를 사용하는 것이 더 간편하다.

 

배열 초기화

배열을 초기화 하는 방법은 다양하다. 우선 아래와 같은 방법이 있다. 중괄호 안에 있는 데이터만큼 메모리가 잡힌다.

int[] numbers = new int[] {1, 2, 3};

혹은 아래와 같이 초기화할 수도 있다.

 

int[] numbers = {1, 2, 3};

아래 코드처럼 배열의 길이만 정의할 경우, 배열의 요소를 각각 넣어주어야 한다.

int[] numbers = new int[3];

numbers[0] = 1; 
numbers[1] = 2;
numbers[2] = 3;

혹은 아래와 같이 초기화할 수도 있다.

int[] numbers;
numbers = new int[3];

numbers[0] = 1; 
numbers[1] = 2; 
numbers[2] = 3;

만약 초기화하지 않을 경우, int형 기준 0으로 초기화 된다.

public class ArrayTest {
	public static void main(String[] args) {
    
		int[] studentIDs = new int[10]; // 요소를 안넣으면 0으로 초기화
        
		for(int i = 0; i < studentIDs.length; i++) {
			System.out.println(studentIDs[i]);
		}
	}
}

배열의 길이와 유효한 요소 값

만약 5개인 배열에 3개의 값만 넣으면 어떤 결과가 나올까? 아래 코드에서 num[0] ~ num[2] 까지만 값을 넣고 요소 전체를 출력해봤더니 값을 넣지 않은 요소 2개는 0.0으로 초기화되어 출력되는 것을 볼 수 있다. 그래서 total의 값 역시 0.0이다.

public class ArrayTest2 {

	public static void main(String[] args) {
		double[] num =  new double[5];
		num[0] = 10.0; 
		num[1] = 20.0; 
		num[2] = 30.0; 
		
		double total = 0.0;
		
		for(int i = 0; i < num.length; i++) {
			System.out.println(num[i]);
			total *= num[i];
		}
		System.out.println("Total = " + total);
	}
}
10.0
20.0
30.0
0.0
0.0
Total = 0.0

아래 코드에서는 유효한 개수만큼만 반복하기 위해서 size라는 변수를 만든 후, 요소를 추가할 때 마다 증가시킨뒤 size만큼 반복했다. 그 결과 값이 있는 2번째 인덱스까지만 출력되었고, total의 값도 6000.0으로 다르게 출력된 것을 볼 수 있다.

public class ArrayTest2 {

	public static void main(String[] args) {
		double[] num =  new double[5];
		int size = 0;
		num[0] = 10.0; size++;
		num[1] = 20.0; size++;
		num[2] = 30.0; size++;
		
		double total = 1;
		
		for(int i = 0; i < size; i++) {
			System.out.println(num[i]);
			total *= num[i];
		}
		System.out.println("Total = " + total);
	}
}
10.0
20.0
30.0
Total = 6000.0

문자 배열

알파벳을 차례대로 출력하기 위해 변수 ch에 'A'라는 값을 대입 한 후, 하나씩 증가시키며 alphabets 배열에 넣어주었다. 그리고나서 for문을 통해 alphabets 배열의 요소를 하나씩 돌고, 해당 아스키 값에 해당하는 문자들이 차례로 출력된다.

public class ArrayTset3 {

	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]);
		}

	}

}

객체 배열

✔ 기본 배열과 객체 배열의 차이점?

기본 배열에서는 10개를 선언하면 그 10개를 바로 쓸 수 있지만, 객체 배열은 객체를 각각 생성해서 배열에 다시 넣어주어야 한다.

 

객체 배열은 선언한다고 해서 바로 사용할 수 있는 것이 아니라, new를 활용해 객체를 각각 생성해서 배열에 다시 넣어주어야 한다. 배열만 생성한 경우 그 안의 요소는 null로 초기화된다. 다음 코드는 Book이라는 클래스의 인스턴스를 담는 객체 배열에 대한 예시다. 객체 배열만 만드는 것이 아니라, 우선 객체 배열에 담을 객체를 만들어야 하기 때문에 Book 클래스를 먼저 만들었다. 

public class Book {
	private String bookName;
	private String author;
	
	public Book() {}
	public Book(String bookName, String author) {
		this.bookName = bookName;
		this.author = author;
	}
	
	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 showBookInfo() {
		System.out.println(bookName + "," + author);
	}
}

Book 클래스를 만든 후, 객체 배열을 생성하였다. 혼동하지 말아야 할 점은 책 인스턴스를 5개 만드는게 아니라, 책 인스턴스 5개가 만들어질 주소를 5개 만들었다는 점이다. 맨 첫 번째 for 문을 돌리면 5개의 null이 나오는 것도 그 이유다. 각각의 인덱스에 new를 사용하여 요소를 넣어주어야 한다. 그리고나서 showBookInfo( ) 메서드를 사용해 출력하면 내가 넣은 요소가 전부 출력된다. 맨 마지막 for 문에서는 각 인덱스를 돌면서 그에 해당하는 주소값을 출력한다.

public class BookArray {

	public static void main(String[] args) {
		Book[] library = new Book[5];
		
		for(int i = 0; i < library.length; i++) {
			System.out.println(library[i]);
		} // null
		
		library[0] = new Book("태백산맥", "조정래");
		library[1] = new Book("데미안", "헤르만 헤세");
		library[2] = new Book("어떻게 살 것인가", "유시민");
		library[3] = new Book("로지", "박경리");
		library[4] = new Book("어린왕자", "생텍쥐페리");

		for(int i = 0; i < library.length; i++) {
			library[i].showBookInfo();
		}
		
		for(int i = 0; i < library.length; i++) {
			System.out.println(library[i]);
		}
	}
}
null
null
null
null
null
태백산맥,조정래
데미안,헤르만 헤세
어떻게 살 것인가,유시민
로지,박경리
어린왕자,생텍쥐페리
array.Book@1175e2db
array.Book@36aa7bc2
array.Book@76ccd017
array.Book@182decdb
array.Book@26f0a63f

배열 복사하기

배열은 Fixed-length로 시작하기 때문에 배열이 꽉 찬 경우, 더 큰 배열을 만들고 그 안에 기존 배열 자료를 복사하여 넣어야한다. 이와 같은 작업은 자바 라이브러리에서 제공되는 메서드를 통해 쉽게 할 수 있으며, 사용법은 다음과 같다.

System.arraycopy(scr, scrPos, dest, destPos, length)

 

매개변수는 총 5개로 각자의 대한 설명은 다음과 같다. 만약 복사할 사이즈가 안맞으면 오류가 발생하므로 주의해야한다.

src 복사할 배열 이름
scrPos 복사할 배열의 첫 번째 위치
dest 복사해서 붙여 넣을 대상 배열 이름
destPos 복사해서 대상 배열에 붙여넣가를 시작할 첫 번째 위치
length src에서 dest로 자료를 복사할 요소 개수

객체 배열 복사하기

객체 배열 복사에는 얕은 복사와 깊은 복사가 있으며, 복사 방법에 따라 사용법이 조금씩 다르다.

✔ 얕은 복사 : 배열 요소의 주소만 복사되므로 배열 요소가 변경되면 복사된 배열의 값도 변경 됨.(1이 변경되면 2도 변경됨)

깊은 복사 : 인스턴스를 다시 생성해야 하며 서로 다른 인스턴스의 메모리를 요소로 가지게 됨. (1이 변경되어도 2는 변경되지 않음)

 

다음 코드는 얕은 복사로 객체 배열을 복사한 것으로, bookArray1의 요소를 변경하자 bookArray2의 요소 역시 변경되었다.

public class ObjectCopy1 {
	// 얕은 복사
	public static void main(String[] args) {
		Book[] bookArray1 = new Book[3];
		Book[] bookArray2 = new Book[3];
		
		bookArray1[0] = new Book("태백산맥", "조정래");
		bookArray1[1] = new Book("데미안", "헤르만 헤세");
		bookArray1[2] = new Book("어떻게 살 것인가", "유시민");
		
		System.arraycopy(bookArray1, 0, bookArray2, 0, 3);
		// 1 -> 2로 복사
		for(int i = 0; i < bookArray2.length; i++) {
			bookArray2[i].showBookInfo();
		}
		System.out.println("----------------------");
		// 1을 변경
		bookArray1[0].setBookName("나목");
		bookArray1[0].setAuthor("박완서");
		
		for(int i = 0; i < bookArray1.length; i++) {
			bookArray1[i].showBookInfo();
		}
		System.out.println("----------------------");
		// 2도 같이 변경 됨
		for(int i = 0; i < bookArray2.length; i++) {
			bookArray2[i].showBookInfo();
		}
	}

}

다음 코드는 깊은 복사로 객체 배열을 복사한 것으로, bookArray1의 요소를 변경해도 bookArray2의 요소가 변경되지 않는다.

public class ObjectCopy1_2 {
	// 깊은 복사
	public static void main(String[] args) {
		Book[] bookArray1 = new Book[3];
		Book[] bookArray2 = new Book[3];
		
		bookArray1[0] = new Book("태백산맥", "조정래");
		bookArray1[1] = new Book("데미안", "헤르만 헤세");
		bookArray1[2] = new Book("어떻게 살 것인가", "유시민");
		
		bookArray2[0] = new Book(); // 멤버변수가 채워지지 않은 빈 Book 생성(디폴트생성자를 이용)
		bookArray2[1] = new Book();
		bookArray2[2] = new Book();
		
		for(int i = 0; i < bookArray1.length; i++) {
			bookArray2[i].setAuthor(bookArray1[i].getAuthor());
			bookArray2[i].setBookName(bookArray1[i].getBookName());
		}
		bookArray1[0].setBookName("나목");
		bookArray1[0].setAuthor("박완서");
		
		// 1만 바뀜
		for(int i = 0; i < bookArray1.length; i++) {
			bookArray1[i].showBookInfo();
		}
		
		System.out.println("----------------------");
		// 2는 처음 상태 그대로
		for(int i = 0; i < bookArray2.length; i++) {
			bookArray2[i].showBookInfo();
		}
	}
}

✔ 객체 배열을 선언한다고 해서 인스턴스가 모두 생성되는 것이 아니다.

✔ 객체 배열을 복사한다고 해서 완전히 다른 인스턴스를 가리키고 있는 것이 아니다. 같은 인스턴스를 가리키고 있기 때문에, 문제 될 요소가 있다면 다른 인스턴스를 가리키도록 깊은 복사를 해야한다.

다차원 배열

다차원 배열은 2차원 이상의 배열을 말한다. 지도, 게임 등 평면이나 공간을 구현할 때 많이 사용된다. 선언 방법은 아래와 같다.

// 자료형[][] 배열이름 = new 자료형[i][j]
// i는 행의 개수, j는 열의 개수
int[][] arr = new int[2][3];
int[][] arr = {{1, 2, 3}, {4, 5, 6}};

다차원 배열을 출력하려면 이중 for문을 사용한다.

public class TowDimensionArry {

	public static void main(String[] args) {
		int[][] arr = {{1, 2, 3},{4, 5, 6}};
		
		System.out.println(arr.length); //행의 길이를 출력함
		System.out.println(arr[0].length);
		System.out.println(arr[1].length); // 열의 길이를 출력함
		
		for(int i = 0; i < arr.length; i++) {
			for(int j = 0; j < arr[i].length; j++) {
				System.out.println(arr[i][j]);
			}
		}
	}
}

💡 향상된 for문(enhanced for loop)

배열 요소의 처음부터 끝까지 모든 요소를 참조할 때 편리한 반복문이다. 다음과 같이 작성한다. arr 에 있는 요소 하나하나가 num이라는 변수로 대입된다. (num = arr[i];)

// for(자료형 변수이름 : 배열이름)
int[] arr = {1, 2, 3, 4, 5};

for(int num : arr){
	System.out.println(num);
}

다음은 강의에 나온 예시로, Java, Android, C가 차례대로 출력된다. 기존 for문보다 향상된 for문이 더 짧고 간결하다.

public class EnhancedForLoop {

	public static void main(String[] args) {
		String[] strArr = {"Java", "Android", "C"};
		
		// 기존 for문
		for(int i = 0; i < strArr.length; i++) {
			System.out.println(strArr[i]);
		}
		// 향상된 for문
		for(String s : strArr) {
			System.out.println(s);
		}
	}
}

💡 ArrayList 클래스

ArrayList는 자바에서 제공해주는 배열의 기능을 구현한 클래스이다. 배열을 직접 생성해서 쓰면 불편한 점이 여러가지 있을 수 있는데 그런 불편한 점을 해결해준다. (배열의 길이가 고정되어 있어서 배열의 크기가 늘어나야 할 경우, 새로 배열을 만들고 그곳에 기존의 원소를 복사해야하는 점, 중간의 요소가 삭제되거나 삽입되는 경우도 나머지 요소에 대한 조정하는 코드를 구현해야 하는 등..) 이런 불편한 점을 ArrayList 클래스를 이용하여 쉽게 처리할 수 있다. ArrayList 클래스는 자바에서 제공되는, 객체 배열이 구현된 클래스이다. 여러 메서드와 속성 등을 사용하여 객체 배열을 편리하게 관리할 수 있다.

public class ArrayListTest {

	public static void main(String[] args) {
		// String[] slist = new String[10]; 이건 String배열을 직접 선언한 것
		// 아래는 ArrayList에서 String타입으로 가져다 쓰겠다라는 의미
        ArrayList<String> list = new ArrayList<String>();
		list.add("aaa");
		list.add("bbb");
		list.add("ccc");
		
		for(String s : list) {
			System.out.println(s);
		}
	}
}
aaa
bbb
ccc

ArrayList 클래스 주요 메서드

요소를 추가하거나 제거할 때 각 내부에서 코드가 모두 구현되어 있으므로 배열을 직접 선언하여 사용하는 것보다 편리하다.

boolean add(E e) 요소 하나를 배열에 추가한다. E는 요소의 자료형 의미
int size( ) 배열에 추가된 요소 전체 개수를 반환
E get(int index) 배열의 index 위치에 있는 요소 값을 반환
E remove(int index) 배열의 index 위치에 있는 요소 값을 제거하고 그 값을 반환
boolean isEmpty( ) 배열이 비어있는지 확인