[Java Semina] 4회

Jiwon-Woo·2021년 7월 13일
0

Java Semina

목록 보기
5/9

1. 배열

배열은 자료형이 같은 자료가 연속으로 나열된 자료구조이다. 배열은 자료형이 같은 자료들을 한번에 관리할 수 있어 편리하다.

배열을 이루고 있는 자료들을 배열의 요소라고 하는데, 배열의 요소들은 실제 메모리 상에서도 이웃해있다. 물리적 위치와 논리적 위치가 같다고 표현하며, 배열의 i번째 요소는 j번째 인덱스로부터 실제 메모리 상에서 |i - j| * 자료형의 크기(byte) 만큼 떨어져 있다는 의미다.

int 형 배열인 intArray 라는 이름의 배열이 있다고 한다면, intArray[1]intArray[0]로부터 4byte 다음 메모리 주소에 위치하고 있는 것이다.

자바의 배열은 클래스로 구현되어 있으므로, 배열 객체를 생성하여 이용할 수 있다. 다른 클래스처럼 배열 객체 또한 배열 클래스 내에 있는 속성과 메서드를 활용할 수 있다.


1.1 배열의 생성

배열은 하나의 클래스로, new 를 활용하여 배열 객체를 생성할 수 있고, 배열 변수에 배열 인스턴스의 주소값을 담을 수 있다. 만약 배열 객체를 생성하지 않고 변수만 선언 하게 되면, 변수는 null 값을 가지게 된다.

배열 변수를 선언하고, 배열 객체를 생성

int[] intArray = new int[5];

// int[] 자료형으로 intArray 라는 배열 변수를 선언한다.
// new 예약어를 활용하여 공간이 5개인 int형 배열을 생성한다.
// intArray 변수에는 배열 객체가 생성된 힙 메모리의 주소가 저장된다.

배열 객체를 생성하지 않고, 변수만 선언

int[] intArray;

/* 배열 객체를 생성하여 intArray 변수에 배열 객체의 주소값을 담기 전까지
  intArray 변수는 null로 초기화 되어있다. */

배열 참조

배열 객체의 생성은 힙 메모리에서 이루어지고, 변수는 참조만 하기 때문에 하나의 배열 객체를 가리키는 변수가 다수일 수 있다.
간단히 말하면 다음과 같이 동일한 메모리 주소를 담고 있는 변수가 여러개일 수 있다는 것이다.

int[] intArray1 = new int[5];
int[] intArray2 = intArray1;

/* intArray2 = intArray1 에 의해
  intArray2와 intArray1는 동일한 메모리 주소를 가리키고 있다. */

1.2 배열의 선언 및 초기화

배열을 선언할 때는 초기값을 정해줄 수도 정하지 않을 수도 있다. 만약 초기값을 따로 지정해주지 않으면 배열의 자료형에 따라 배열 요소의 초기값이 설정된다. 자료형이 int 라면, 0 으로, double 이나 float 라면 0.0, 객체 배열이라면 null 로 초기화 된다.

선언과 동시에 초기값을 설정해주기

int[]	math = new int[] {78, 46, 98, 22, 100};
int[]	english = {10, 20, 30, 40, 50};
  • 값을 넣어 초기화해줄 경우 new int[]의 대괄호에 자료형의 개수를 적으면 오류가 발생하므로 적지 말것.
  • 초기값을 넣어주는 동시에 배열을 선언할 때는 new int[]을 생략해도 되지만, 만약 선언과 초기값의 대입이 다른 라인에서 이루어진다면 new int[]을 생략할 수 없다.

1.3 배열의 사용

배열을 선언할 때 사용했던 []를 인덱스 연산자라고 한다. 배열은 []을 통해 배열의 요소에 접근할 수 있다.

math[i]math 라는 배열의 i 번째 요소라는 뜻이다. 인덱스 연산자를 활용하여 i번째 값을 얻거나 바꿀수 있다.



2. 객체 배열

배열의 요소가 객체인 배열로, 기본 자료형 배열과 사용법이 약간 다르다.

기본 자료형 배열 요소에는 그 자료형에 해당하는 실제 값이 담기지만 객체 배열에는 생성된 인스턴스의 주소가 담긴다.

2.1 객체 배열의 사용

Student 클래스

package array;

public class Student {
	
	private	String	id;

	public Student() {}
	
	public Student(String id) {
		this.id = id;
	}
	
	public String	getStudentID() {
		return this.id;
	}

	public void	setStudentID(String id) {
		this.id = id;
	}
}

객체 배열 선언하기

Student[] cadet = new Student[5];

위와 같이 클래스 명과 []을 사용하여 객체 배열을 선언한다. new를 사용하여 선언하지만, 이때는 Stduent 인스턴스가 생성되는 게 아니라 인스턴스의 메모리 주소가 담길 공간이 []안의 개수 만큼 생성된다.

객체 배열의 요소는 모두 null로 초기화 되어으며, 인스턴스를 생성하여 그 메모리 주소를 배열의 요소에 담아주는 방식으로 사용한다.

객체 배열에 인스턴스를 생성하고 저장하기

cadet[0] = new Student("Kang");
cadet[1] = new Student("Choi");
cadet[2] = new Student("Kim");
cadet[3] = new Student("Park");
cadet[4] = new Student("Lee");

객체 배열 사용 예제

ArrayTest.java

package array;

public class ArrayTest {

	public static void main(String[] args) {
		
		Student[] cadet = new Student[5];
		
		cadet[0] = new Student("Kang");
		cadet[1] = new Student("Choi");
		cadet[2] = new Student("Kim");
		cadet[3] = new Student("Park");
		cadet[4] = new Student("Lee");
		
		System.out.println(cadet);
		
		for (int i = 0; i < cadet.length; i++) {
			System.out.println(cadet[i] + ", " + cadet[i].getStudentID());
		}
	}
}

출력 결과

[Larray.Student;@41975e01
array.Student@1ee0005, Kang
array.Student@75a1cd57, Choi
array.Student@3d012ddd, Kim
array.Student@6f2b958e, Park
array.Student@1eb44e46, Lee



3. 배열의 복사

배열의 복사에는 두가지 방법이 있다.
첫번째로는 for 반복문을 사용하여 각 배열의 요소값을 복사하는 방법이 있다. 두번째는 System.arraycopy() 메서드를 활용하는 것이다.


3.1 System.arraycopy()

함수의 원형

System.arraycopy(src, srcPos, dest, destPos, length)

매개변수의 의미

매개 변수의미
src복사할 배열 이름
srcPos복사할 배열의 첫 번째 인덱스 위치
dest복사해서 붙여 넣을 대상 배열 이름
destPos복사해서 대상 배열에 붙여 넣기를 시작할 인덱스 위치
lengthsrc에서 dest로 복사할 요소의 개수

System.arraycopy() 사용 예제

package array;

public class ArrayTest {

	public static void main(String[] args) {
		
		int[]	math = {78, 46, 98, 22, 100};
		int[]	english = {10, 20, 30, 40, 50};
		
		System.arraycopy(math, 1, english, 2, 2);
		/* `math` 배열의 `1`번째 요소부터 `2`개의 요소를 복사해
		`english` 배열의 `2`번째 요소부터 차례로 넣어준다. */
		
		for (int i = 0; i < math.length; i++) {
			System.out.print(math[i] + " ");
		}
		
		System.out.println();
		
		for (int i = 0; i < english.length; i++) {
			System.out.print(english[i] + " ");
		}
	}
}

출력 결과

78 46 98 22 100 
10 20 46 98 50 

3.2 객체 배열의 복사

기본 자료형 배열과 마찬가지로 객체 배열 또한 System.arraycopy() 메서드로 배열 요소를 복사할 수 있다.

다만 객체 배열의 요소는 객체의 메모리 주소이므로, 기본 자료형 배열처럼 실제 자료값이 복사되는게 아니라 객체의 메모리 주소가 복사된다. 즉, srcdest의 각 배열요소가 같은 객체를 참조하게 되는 것이다.

그렇기 때문에 dest 배열은 src 배열처럼 배열 요소마다 객체를 생성해 넣지 않아도 오류없이 복사가 가능하다.

객체 배열을 System.arraycopy() 메서드로 복사해보기

package array;

public class ArrayTest {

	public static void main(String[] args) {
		
		Student[] cadet = new Student[5];
		Student[] student = new Student[5];
		
		cadet[0] = new Student("Kang");
		cadet[1] = new Student("Choi");
		cadet[2] = new Student("Kim");
		cadet[3] = new Student("Park");
		cadet[4] = new Student("Lee");
		
		
		for (int i = 0; i < cadet.length; i++) {
			System.out.println(cadet[i] + ", " + cadet[i].getStudentID());
		}
		for (int i = 0; i < student.length; i++) {
			System.out.println(student[i]);
		}
		
		System.out.println();
		System.arraycopy(cadet, 0, student, 0, 5);
		
		for (int i = 0; i < cadet.length; i++) {
			System.out.println(cadet[i] + ", " + cadet[i].getStudentID());
			System.out.println(student[i] + ", " + student[i].getStudentID());
		}
	}
}

출력 결과

array.Student@41975e01, Kang
array.Student@1ee0005, Choi
array.Student@75a1cd57, Kim
array.Student@3d012ddd, Park
array.Student@6f2b958e, Lee
null
null
null
null
null

array.Student@41975e01, Kang
array.Student@41975e01, Kang
array.Student@1ee0005, Choi
array.Student@1ee0005, Choi
array.Student@75a1cd57, Kim
array.Student@75a1cd57, Kim
array.Student@3d012ddd, Park
array.Student@3d012ddd, Park
array.Student@6f2b958e, Lee
array.Student@6f2b958e, Lee

3.3 얕은 복사

System.arraycopy() 메서드를 사용했을 때와 같이, 객체의 메모리 주소걊만 복사하는 것을 얕은 복사라고 한다.
얕은 복사를 할 경우, srcdest가 모두 같은 객체를 참조하고 있기 때문에, 배열의 객체 값이 변경되면 두 배열 모두 영향을 받게 된다.

얕은 복사 예제

package array;

public class ArrayTest {

	public static void main(String[] args) {
		
		Student[] cadet = new Student[5];
		Student[] student = new Student[5];
		
		cadet[0] = new Student("Kang");
		cadet[1] = new Student("Choi");
		cadet[2] = new Student("Kim");
		cadet[3] = new Student("Park");
		cadet[4] = new Student("Lee");
		
		System.arraycopy(cadet, 0, student, 0, 5);
		
		for (int i = 0; i < cadet.length; i++) {
			System.out.println(cadet[i] + ", " + cadet[i].getStudentID());
		}
		System.out.println();
		for (int i = 0; i < cadet.length; i++) {
			System.out.println(student[i] + ", " + student[i].getStudentID());
		}
		
		cadet[1].setStudentID("Woo Jiwon");
		System.out.println();
		
		for (int i = 0; i < cadet.length; i++) {
			System.out.println(cadet[i] + ", " + cadet[i].getStudentID());
		}
		System.out.println();
		for (int i = 0; i < cadet.length; i++) {
			System.out.println(student[i] + ", " + student[i].getStudentID());
		}
	}
}

출력 결과

array.Student@41975e01, Kang
array.Student@1ee0005, Choi
array.Student@75a1cd57, Kim
array.Student@3d012ddd, Park
array.Student@6f2b958e, Lee

array.Student@41975e01, Kang
array.Student@1ee0005, Choi
array.Student@75a1cd57, Kim
array.Student@3d012ddd, Park
array.Student@6f2b958e, Lee

array.Student@41975e01, Kang
array.Student@1ee0005, Woo Jiwon
array.Student@75a1cd57, Kim
array.Student@3d012ddd, Park
array.Student@6f2b958e, Lee

array.Student@41975e01, Kang
array.Student@1ee0005, Woo Jiwon
array.Student@75a1cd57, Kim
array.Student@3d012ddd, Park
array.Student@6f2b958e, Lee

3.4 깊은 복사

만약 srcdest의 배열요소가 서로 영향을 미치는 것(정확히는 같은 객체를 참조하고 있기 때문에 발생하는 현상)을 막고 싶다면, 얕은 복사를 해서는 안된다.

인스턴스의 메모리 주소가 아니라 인스턴스의 속성값을 복사하고 싶다면, dest 의 배열 요소에도 객체를 새로 생성하고, for 반복문을 사용하여 그 객체의 속성값에 src 의 객체 속성값을 직접 대입해야한다. 이렇게 요소의 값만 같게 복사하는 것을 깊은 복사라고 한다.

깊은 복사 예제

package array;

public class ArrayTest {

	public static void main(String[] args) {
		
		Student[] cadet = new Student[5];
		Student[] student = new Student[5];
		
		cadet[0] = new Student("Kang");
		cadet[1] = new Student("Choi");
		cadet[2] = new Student("Kim");
		cadet[3] = new Student("Park");
		cadet[4] = new Student("Lee");
		
		student[0] = new Student();
		student[1] = new Student();
		student[2] = new Student();
		student[3] = new Student();
		student[4] = new Student();
		
		for (int i = 0; i < cadet.length; i++) {
			System.out.println(cadet[i] + ", " + cadet[i].getStudentID());
		}
		System.out.println();
		for (int i = 0; i < student.length; i++) {
			System.out.println(student[i] + ", " + student[i].getStudentID());
		}
		
		for (int i = 0; i < student.length; i++) {
			student[i].setStudentID(cadet[i].getStudentID());
		}
		System.out.println();
		
		for (int i = 0; i < cadet.length; i++) {
			System.out.println(student[i] + ", " + student[i].getStudentID());
		}
		System.out.println();
		
		cadet[1].setStudentID("Woo Jiwon");
		System.out.println(cadet[1] + ", " + cadet[1].getStudentID());
		System.out.println(student[1] + ", " + student[1].getStudentID());
	}
}

출력 결과

array.Student@41975e01, Kang
array.Student@1ee0005, Choi
array.Student@75a1cd57, Kim
array.Student@3d012ddd, Park
array.Student@6f2b958e, Lee

array.Student@1eb44e46, null
array.Student@6504e3b2, null
array.Student@515f550a, null
array.Student@626b2d4a, null
array.Student@5e91993f, null

array.Student@1eb44e46, Kang
array.Student@6504e3b2, Choi
array.Student@515f550a, Kim
array.Student@626b2d4a, Park
array.Student@5e91993f, Lee

array.Student@1ee0005, Woo Jiwon
array.Student@6504e3b2, Choi



4. ArrayList

앞에서 다룬 기본 배열은 길이를 정하고 시작해야하는데, 이 길이는 변경할 수 없기 때문에 이후에 길이를 수정하고 싶어도 그럴 수 없다. 그리고 만약 배열의 요소를 제거하게 된다면, 그 부분은 앞으로 당겨지는 게 아니라 비워진 채로 존재하게 된다.

자바에서는 ArrayList 라는 객체 배열 클래스를 통해 이러한 단점을 극복할 수 있다.

ArrayList 클래스는 java.util 패키지에 구현되어 있는 클래스 이므로 ArrayList를 사용하기 위해서는 java.util 패키지를 import 해서 사용해야한다.


4.1 ArrayList의 선언과 생성

ArrayList의 기본 선언 형태

import java.util.ArrayList;

ArrayList<E> 배열 이름 = new ArrayList<E>();

E는 자료형을 의미하고, <E>와 같은 형태를 제네릭 자료형이라고 한다.

ArrayList 와 일반 배열의 비교

/* ArrayList */
ArrayList<Student> cadet = new ArrayList<Student>();
/* 일반 배열 */
Student[] cadet = new Student[5];

ArrayList은 배열의 길이 설정으로부터 비교적 자유롭기 때문에 선언할 때 길이를 선언해줄 필요가 없다.


4.2 ArrayList의 주요 메서드

메서드설명
boolean add(E e)요소 하나를 배열에 추가하고 성공적으로 추가했다면 true, 아니라면 false 반환
int size()배열에 추가된 요소 전체 개수를 반환
E get(int index)배열의 index 위치에 있는 요소 값(객체 배열의 경우 인스턴스의 주소값)을 반환
E remove(int index)배열의 index 위치에 있는 요소 값(객체 배열의 경우 인스턴스의 주소값)을 제거하고 그 값을 반환
boolean isEmpty()배열이 비어 있는지 확인하고, 비어있다면 true, 비어있지 않다면 false 반환
  • E는 요소의 자료형을, e는 요소의 변수명을 의미한다.
  • 배열의 요소를 추가할 때, 배열의 길이에 상관없이 추가 되며, 만약 길이가 부족하다면 배열의 길이가 더 늘어나도록 구현되어 있다.
  • 요소가 제거될 때도 마찬가지로, 어떤 인덱스의 요소가 제거되면 그 자리를 비워두지 않고 앞으로 하나씩 당기도록 구현되었다.

4.3 ArrayList의 활용

ArrayList 사용 예제

package array;
import java.util.ArrayList;

public class ArrayTest {

	public static void main(String[] args) {
		
		ArrayList<Student> cadet = new ArrayList<Student>();
		
		System.out.println(cadet.isEmpty() + ", " + cadet.size());
		System.out.println();
		
		System.out.println(cadet.add(new Student("Kang")));
		System.out.println(cadet.add(new Student("Choi")));
		System.out.println(cadet.add(new Student("Kim")));
		System.out.println(cadet.add(new Student("Park")));
		System.out.println(cadet.add(new Student("Lee")));
		System.out.println();
		
		System.out.println(cadet.isEmpty() + ", " + cadet.size());
		System.out.println();
		
		for (int i = 0; i < cadet.size(); i++) {
			Student	student = cadet.get(i);
			System.out.println(student + ", " + student.getStudentID());
		}
		System.out.println();
		
		System.out.println(cadet.remove(1));
		System.out.println();
		
		System.out.println(cadet.isEmpty() + ", " + cadet.size());
		System.out.println();
		
		for (Student student : cadet) {
			System.out.println(student + ", " + student.getStudentID());
		}
	}
}

출력결과

true, 0

true
true
true
true
true

false, 5

array.Student@7dc5e7b4, Kang
array.Student@1ee0005, Choi
array.Student@75a1cd57, Kim
array.Student@3d012ddd, Park
array.Student@6f2b958e, Lee

array.Student@1ee0005

false, 4

array.Student@7dc5e7b4, Kang
array.Student@75a1cd57, Kim
array.Student@3d012ddd, Park
array.Student@6f2b958e, Lee
  • ArrayList는 일반 배열과 달리 인덱스 연산자(cadet[i])로 접근할 수 없다. 대신 get(i)을 사용하여 객체의 인스턴스에 접근 가능하다.
  • for (Student student : cadet)Student 자료형 변수인 studentArrayList의 요소를 순서대로 넣는다는 의미이다.

향상된 for

아래의 for 반복문은 모두 같은 것을 출력해준다.

for (int i = 0; i < cadet.size(); i++) {
	System.out.println(cadet.get(i) + ", " + cadet.get(i).getStudentID());
}
for (int i = 0; i < cadet.size(); i++) {
	Student	student = cadet.get(i);
	System.out.println(student + ", " + student.getStudentID());
}
for (Student student : cadet) {
	System.out.println(student + ", " + student.getStudentID());
}

0개의 댓글