1. 배열
- 동일한 자료형, 순차적 자료 구조
- 선언 시 배열 길이 결정된다.
- heap 영역에 new 연산자를 통해 할당된다.
- 인덱스 연산자[]로 빠른 참조가능 (cf. Linked list는 빠르지 않다. 시간 복잡도 O(n))
- 물리적 위치와 논리적 위치가 동일 (실제 메모리에서 A[0]와 A[1]은 연속적이다. Linked list는 물리적 위치 != 논리적 위치)
- 자바에선 객체 배열을 구현한 ArrayList를 자주 사용한다. (Java util 패키지에서 제공)
2. 배열 선언 및 초기화
(1) 배열 선언하기
: 스택에 배열의 주소를 보관할 레퍼런스 변수 공간을 만드는 것
- 선언한 레퍼런스 변수에 배열을 할당하여 대입할 수 있다.
(발생한 주소가 레퍼런스 변수에 저장되고, 이것을 참조해서 사용하므로 참조 자료형이라 한다.)
int[] arr1 = new int[10];
int arr2[] = new int[10];
// new 연산자는 heap 영역에 공간을 할당하고 발생한 주소값을 반환한다.
(2) 배열 초기화하기
- 배열은 선언과 동시에 초기화된다. (정수: 0, 실수: 0.0, 논리: false, 문자: \u0000, 참조: null) (초기값 지정도 가능)
cf. 스택에 할당되는 지역변수는 초기화해야지만 접근할 수 있지만, heap 영역에 할당될 경우 초기값이 설정된다.
int[] numbers = new int[] {10, 20, 30}; // 개수는 작성하지 않는다.
int[] numbers = {10, 20, 30}; // 선언과 초기화를 동시에 할 경우애는 new int[] 생략 가능
int[] ids;
ids = new int[] {10, 20, 30}; // 선언 후 배열을 생성하는 경우에는 new int[] 생략할 수 없다.
(3) 배열 접근하기
- heap 메모리는 이름이 아닌 주소로 접근하는 영역이다.
- stack에 저장된 주소로 heap에 할당된 배열을 찾아갈 수 있다.
System.out.println("numbers = " + numbers);
- hashCode(): 객체의 주소값을 10진수로 변환하여 생성한 객체의 고유 정수값 반환 메소드
System.out.println("numbers.hashCode() = " + numbers.hashCode());
(4) 배열 덮어쓰기
- 한 번 지정한 배열의 길이는 변경할 수 없으므로 새로운 배열을 생성하여 그 주소값을 레퍼런스 변수에 덮어쓸 수 있다.
numbers = new int[100];
System.out.println("수정된 numbers.hashCode() = " + numbers.hashCode()); // 위에서 출력한 hashCode와 다르다.
- cf. 한 번 할당된 배열은 지울 수 없는데, 참조되지 않는 배열은 시간이 지난 후 old 영역으로 이동하여 GC가 삭제한다.
- cf. 주소값을 잃어버린 배열은 재참조 불가하다.
iarr = null // 참조하는 주소값이 없음을 뜻한다.
/*null을 참조할 때 참조 연산자를 사용하면 java.lang.NullPointerException이 발생한다.
System.out.println("수정된 numbers.hashCode() = " + numbers.hashCode());
System.out.println("수정된 numbers.length = " + numbers.length); */
3. 배열의 길이 & 요소의 개수
- 배열을 선언하면 개수만큼 메모리가 할당되지만, 실제 요소(데이터)가 없는 경우도 있다.
- 배열의 length는 배열의 개수를 반환. (요소의 개수를 반환하는 것이님 )
double[] dArr = new double[5];
dArr[0] = 1.1;
dArr[1] = 2.1;
dArr[2] = 3.1;
// 이런 식으로 일부만 초기화할 수도 있다.
- 요소의 개수에 대한 정보가 필요하면 아래와 같이 count 변수를 이용할 수 있다.
double[] dArr = new double[5];
int count = 0;
dArr[0] = 1.1; count++;
dArr[1] = 2.1; count++;
dArr[2] = 3.1; count++;
4. 배열에서 향상된 for문 사용하기
- 배열의 요소 n개를 0~n-1까지 순회할 때(즉, 배열 전체를 순회할 때) 사용가능
public class CharArrayTest {
public static void main(String[] args) {
char[] alphabets = new char[26];
char ch = 'A';
for (int i=0; i<alphabets.length; i++) {
alphabets[i] = ch++;; // alphabets[i]에 ch를 넣어주고 ch값이 증가
}
for (char alpha:alphabets) {
System.out.println(alpha + "," + (int)alpha);
}
}
}
5. 배열 복사하기
- 깊은 복사: heap의 배열에 저장된 값을 복사
(1) 얕은 복사
: stack에 저장되어 있는 배열의 주소값만 복사.
- 두 레퍼런스 변수는 동일한 배열의 주소값을 갖고 있다.
- 하나의 레퍼런스 변수에 저장된 주소값을 가지고 배열의 내용을 수정하면,
다른 레퍼런스 변수로 배열에 접근했을 때도 변경된 값이 반영돼 있다.(동일한 배열을 가리키고 있기 때문)

// <얕은 복사>
int[] originArr = {1, 2, 3, 4, 5};
// originArr에 저장된 배열의 주소를 copyArr에도 저장
int[] copyArr = originArr;
// hashcode를 출력해보면 두 레퍼런스 변수는 동일한 주소를 갖고 있는 걸 확인할 수 있다.
System.out.println(originArr.hashCode());
System.out.println(copyArr.hashCode());
// 원본 배열과 복사본 배열의 값 출력 (두 배열은 동일한 값 갖고 있다.)
for (int i = 0; i < originArr.length; i++) {
System.out.print(originArr[i] + " ");
}
for (int i = 0; i < copyArr.length; i++) {
System.out.print(copyArr[i] + " ");
}
// 복사본 배열의 값을 변경한 뒤 원본 배열의 값을 확인해보면, 원본 배열에도 변경이 반영되어 있다.
// 현재 존재하는 배열은 하나 뿐이기 때문
copyArr[0] = 99;
for (int i = 0; i < originArr.length; i++) {
System.out.print(originArr[i] + " ");
}
for (int i = 0;; i < copyArr.length; i++) {
System.out.print(copyArr[i] + " ");
}
public static void main(String[] args) {
String[] names = {"홍길동", "유관순", "이순신"};
System.out.println(names.hashCode());
// 배열을 매개변수로 전달받아 출력하는 메소드. 출력해보면 동일한 hashCode을 갖는 걸 볼 수 있다.
// 이와 같이 다른 메소드에서 동일한 배열(객체)를 사용하도록 하고 싶은 경우 얕은 복사 이용
print(names);
String[] animals = getAnimals();
System.out.println(animals.hashCode());
print(animals);
}
public static void print(String[] sarr) {
// 전달받은 배열의 hashcode 출력
System.out.println("sarr의 hashcode : " + sarr.hashCode());
// 전달받은 배열의 값 출력
for (int i = 0; i < sarr.length; i++) {
System.out.print(sarr[i] + " ");
}
System.out.println();
}
public static String[] getAnimals() {
String[] animals = new String[] {"낙타", "호랑이", "나무늘보"};
// 얕은 복사 확인을 위한 hashcode 출력
System.out.println(animals.hashCode());
return animals;
}
(2) 깊은 복사
: heap의 배열에 저장된 값을 복사
- 서로 같은 값을 갖고 있긴 하지만, 두 배열은 서로 다른 배열이기 때문에 하나의 배열을 변경해도 다른 배열에 영향을 주지 않는다.
- 새롭게 할당한 힙 영역에 기존 배열의 값을 복사한 후 새롭게 생성된 배열의 주소값을 넘겨준다.

1) 깊은 복사 1. for문을 이용한 동일한 인덱스의 값 복사
// 원본 배열
int[] originArr = new int[] {1, 2, 3, 4, 5};
print(originArr);
// 복사한 배열
int[] copyArr1 = new int[10];
for (int i = 0; i < originArr.length; i++)
copyArr1[i] = originArr[i];
print(copyArr1);
// 원본 배열과 복사한 값은 같은 값을 갖고 나머지 인덱스는 다른 값, 다른 주소를 갖고 있다.
2) 깊은 복사 2. Object의 clone()을 이용한 복사
// 원본 배열
int[] originArr = new int[] {1, 2, 3, 4, 5};
print(originArr);
// 복사한 배열
int[] copyArr2 = originArr.clone();
print(copyArr2); // 동일한 길이, 동일한 값을 가지는 배열이 생성되어 복사되며, 다른 주소를 갖고 있다.
3) 깊은 복사 3.System의 arraycopy()를 이용한 복사
// 원본 배열
int[] originArr = new int[] {1, 2, 3, 4, 5};
print(originArr);
// 복사한 배열
int[] copyArr3 = new int[10];
// arraycopy(원본배열, 복사를 시작할 인덱스, 복사본 배열, 복사를 시작할 인덱스, 복사할 길이)
System.arraycopy(originArr, 0, copyArr3, 3, orginArr.length);
print(copyArr3); // 복사한만큼의 값은 같지만 길이도 다르고 주소도 다르다.
4) 깊은 복사 4. Arrays의 copyOf()를 이용한 복사
// 원본 배열
int[] originArr = new int[] {1, 2, 3, 4, 5};
print(originArr);
// 복사한 배열
int[] copyArr4 = Arrays.copyOf(originArr, 7);
print(copyArr4);
- 위의 4가지 방법 중 가장 높은 성능을 보이는 것은 arraycopy() 메소드이고,
가장 많이 사용되는 방식은 좀 더 유연한 방식인 copyOf() 메소드이다.
- clone()은 이전 배열과 같은 배열 밖에 만들 수 없다는 특징을 갖고,
그 외 3가지 방법은 복사하는 배열의 길이를 마음대로 조절할 수 있다는 특징을 갖는다.
* 깊은 복사의 활용
// 같은 값을 갖는 두 배열 초기화
int[] arr1 = {1, 2, 3, 4, 5};
int[] arr2 = arr1.clone();
// arr1의 값을 10 누적 증가시키기
for (int i = 0; i < arr1.length; i++)
arr1[i] += 10;
// arr1 값 출력
for (int i = 0; i < arr1.length; i++)
System.out.print(arr1[i] + " ");
// arr2의 값도 10 누적 증가시키기
for (int i : arr2)
i += 10;
// arr2 값 출력
for (int i = 0; i < arr2.length; i++)
System.out.print(arr2[i] + " ");
// 증가되지 않고 있다. 향상된 for문에서 i는 배열의 각 요소를 복사한 값이기 때문에 실제 배열의 값을 변경할 순 없다.
// 변경이 아니라 접근이 목적이라면 향상된 for문을 이용할 수 있다.
6. 객체 배열
(1) 객체 배열 선언과 구현
- 객체 배열은, 선언 시 객체의 주소가 들어갈 메모리만 할당되고(NULL),
각 요소 객체는 일일이 직접 생성해서 저장해줘야 한다.
package ch21;
public class Book {
private String title;
private String author;
public Book() {}
public Book(String title, String author) {
this.title = title;
this.author = author;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public void showInfo() {
System.out.println(title + ", " + author);
}
}
public class BookTest {
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]); // 출력하면 null 5개 나옴
}
// 이제 Book instance을 만들어보자.
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 (Book book : library) {
System.out.println(book);
book.showInfo();
}
}
}
(2) 객체 배열 복사하기
1) 얕은 복사
- System.arrayCopy(src, srcPos, dest, destPos, length)
- 얕은 복사를 하게 되면 두 배열이 같은 객체를 가리키게 되고,
따라서 한 쪽 배열을 수정하면 같이 수정된다.
public class ObjectCopy {
public static void main(String[] args) {
Book[] library = new Book[5];
Book[] copyLibrary = 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", "조정래");
System.arraycopy(library, 0, copyLibrary, 0, 5);
System.out.println("======copy library=========");
for( Book book : copyLibrary ) {
book.showBookInfo();
}
library[0].setTitle("나목");
library[0].setAuthor("박완서");
System.out.println("======library=========");
for( Book book : library) {
book.showBookInfo();
}
System.out.println("======copy library=========");
for( Book book : copyLibrary) {
book.showBookInfo();
}
}
}

2) 깊은 복사
public class ObjectCopy2 {
public static void main(String[] args) {
Book[] library = new Book[5];
Book[] copyLibaray = 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", "조정래");
copyLibaray[0] = new Book();
copyLibaray[1] = new Book();
copyLibaray[2] = new Book();
copyLibaray[3] = new Book();
copyLibaray[4] = new Book();
for(int i = 0; i< library.length; i++) {
copyLibaray[i].setTitle(library[i].getTitle());
copyLibaray[i].setAuthor(library[i].getAuthor());
}
library[0].setTitle("나목");
library[0].setAuthor("박완서");
System.out.println("======library=========");
for( Book book : library) {
book.showBookInfo();
}
System.out.println("======copy library=========");
for( Book book : copyLibaray) {
book.showBookInfo();
}
}
}
7. 이차원 배열

int[][] arr = {{1,2,3}, {4,5,6}}
// 주소를 묶어서 관리할 배열의 크기는 반드시 지정해주어야 한다.
iarr = new int[3][];
// 아래는 주소를 묶어서 관리할 배열의 크기를 지정해주지 않아 에러가 발생하는 경우들이다.
// iarr = new int[][];
// iarr = new int[][4];
public class TwoDimensionTest {
public static void main(String[] args) {
int[][] arr = {{1, 2, 3}, {1, 2, 3, 4}};
int i, j;
for (i=0; i < arr.length; i++) {
for (j=0; j < arr[i].length; j++) {
System.out.println(arr[i][j] + ",");
}
System.out.println("\t" + arr[i].length);
}
}
}
8. ArrayList
- java.util 패키지에서 제공되는 클래스이다.
import java.util.ArrayList;
import ch21.Book;
public class ArrayListTest {
public static void main(String[] args) {
ArrayList<Book> library = new ArrayList<Book>(); // 이게 생성자
// 근데 요즘에는 아래와 같이 작성해도 된다.
ArrayList<Book> library = new ArrayList<>();
// 개수를 지정해주지 않으면 10개를 잡고, 더 필요하면 늘어남
library.add(new Book("태백산맥1", "조정래"));
library.add(new Book("태백산맥2", "조정래"));
library.add(new Book("태백산맥3", "조정래"));
library.add(new Book("태백산맥4", "조정래"));
library.add(new Book("태백산맥5", "조정래"));
// size(): 요소의 개수
for(int i =0; i<library.size(); i++) {
library.get(i).showBookInfo();
}
}
}
9. 간단한 ArrayList 예제
- StudentTest 클래스를 참고하여 Student와 Subject 클래스를 작성했다.
Student.java
import java.util.ArrayList;
public class Student {
int studentId;
String studentName;
Student(int studentId, String studentName){
this.studentId = studentId;
this.studentName = studentName;
}
ArrayList<Subject> subjectList = new ArrayList<Subject>();
public void addSubject(String subjectName, int score) {
subjectList.add(new Subject(subjectName, score));
}
public void showStudentInfo() {
int total = 0;
for (int i = 0; i < subjectList.size(); i++) { // for(Subject subject : subjectList) 라고 해줄 수도 있다.
System.out.println("학생 " + studentName + "의 " + subjectList.get(i).getSubjectName() + " 과목 성적은 " + subjectList.get(i).getScore() + "입니다.");
total += subjectList.get(i).getScore();
}
System.out.println("학생 " + studentName + "의 총점은 " + total + " 입니다.");
}
}
StudentTest.java
public class StudentTest {
public static void main(String[] args) {
Student studentLee = new Student(1001, "Lee");
studentLee.addSubject("국어", 100);
studentLee.addSubject("수학", 50);
Student studentKim = new Student(1002, "Kim");
studentKim.addSubject("국어", 70);
studentKim.addSubject("수학", 85);
studentKim.addSubject("영어", 100);
studentLee.showStudentInfo();
System.out.println("======================================");
studentKim.showStudentInfo();
}
}
Subject.java
public class Subject {
private String subjectName;
private int score;
Subject() {}
Subject(String subjectName, int score) {
this.subjectName = subjectName;
this.score = score;
}
public String getSubjectName() {
return subjectName;
}
public void setSubjectName(String subjectName) {
this.subjectName = subjectName;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
}
'Java' 카테고리의 다른 글
Java(2) 문자열 비교하기 (0) | 2024.08.05 |
---|---|
Java(1) 리터럴, 변수, 오버플로우, 언더플로우, 형변환 (0) | 2024.08.05 |
static 변수 (0) | 2024.07.07 |
[Java] 객체 자신을 가리키는 this (0) | 2024.07.07 |
접근 제어 지시자(access modifier)와 캡슐화 (0) | 2024.07.05 |