본문 바로가기

개발/Java

[Java] 자바 - 배열 / 배열 복사

 

< 배열 ( Array ) >


- 하나의 공간에 여러개의 값을 담을 수 있음
- 단, "같은 자료형의 값들"이어야 함
- 배열의 각 인덱스 자리에 실제 값이 담김. 이때, 인덱스는 '0'부터 시작함
- 배열을 사용하면 반복문 사용이 용이

- 단, 한 번 지정한 배열의 크기는 변경이 불가능 


[ 선언 ]
1) 자료형 배열식별자[];
2) 자료형[] 배열식별자; (주로 사용)

[ 할당 ]
- 해당 배열에 몇 개의 값이 들어갈 것인지 배열의 크기를 정해주는 과정
- 지정한 개수만큼 값이 들어갈 공간이 만들어짐

 

	int[] arr; // 선언 - 정수형 배열
	arr1 = new int[5] // 할당 - new 자료형[개수];

	int[] arr2 = new int[5]; // 선언과 동시에 할당
    
    // 크기가 정해진 배열의 선언과 동시에 초기화(대입)하는 방법
    
    // 1.
    int[] arr1 = new int[] {1, 3, 8, 5} // 초기화블럭 사용
    
    // 2. 
    int[] arr2 = {1, 3, 8, 5}

 


[ 값 대입 ]
배열이름[인덱스] = 값; - 이때, 인덱스는 '0'부터 시작

* 배열의 범위를 벗어난 인덱스에 값을 추가하려고 한다면,
문법적으로는 오류가 없어서 실행은 가능, 오류 발생 (콘솔창에 해당 경고문 표시)

ArrayIndexOutOfBounds : 100 <= 오류 발생 이유 : 배열의 인덱스 범위를 벗어남
at com.kh.array.A_Array.method2(A_Array.java:181) <= 오류의 위치

 

public void method4() {
    // 사용자로부터 입력을 통해 다섯칸짜리 정수 배열에 값을 담기

    int[] nums = new int[5];

    Scanner sc = new Scanner(System.in);

    for (int i = 0; i < nums.length; i++ ) {

        System.out.print("정수를 입력해주세요 > ");
        nums[i] = sc.nextInt();
        System.out.println("nums라는 배열의 " + i + "번째 인덱스에 들어가는 값 : " + nums[i]);
    }

    System.out.println(Arrays.toString(nums)); 

    // 해당 배열에서 가장 작은값(최솟값) 구하는 기능
    int min = nums[0]; // 초기값을 배열의 첫번째 값으로 해서 기준점을 삼음

    for(int i = 0; i < nums.length; i++) {

        if(nums[i] < min) { // 기준값보다 작을때 
            min = nums[i];  // min에 대입
        }
    }
    System.out.println("최솟값 : " + min);

    // 해당 배열에서 가장 큰 값(최댓값) 구하는 기능
    int max = nums[0]; // 초기값을 배열의 첫번째 값으로 해서 기준점을 삼음

    for(int i = 0; i < nums.length; i++) {

        if (nums[i] > max) { // 기준값보다 클때
            max = nums[i];   // max에 대입
        }
    }
    System.out.println("최댓값 : " + max);
}

 

[ 저장구조 ]

- 배열은 참조형 변수로 Heap영역에 할당. 배열 공간의 주소값을 저장

- 배열 공간의 주소를 이용해 인덱스를 참조하는 방식으로 값을 처리함

- arr = null; 주소값을 담은 공간을 null로 초기화 하면 Heap영역에 생성된 배열과의 연결이 끊어짐

  => 연결이 끊어진 배열은 ''가비지 컬렉터(Garbage collector)"가 삭제 (자동 메모리 관리)

- 주소값이 비워진 arr에 접근하려고 하면 오류 발생 : Exception in thread "main" java.lang.NullPointerException

 


 

< 배열 복사 >

 

1. 얕은 복사 : 배열의 주소값만을 복사. 원본의 배열과 복사본의 배열이 같음.

int[] origin = {1, 2, 3, 4, 5}; // 값 == 요소

System.out.println("-- 원본 배열 출력 --");
for (int i = 0; i < origin.length; i++) {
    System.out.print(origin[i] + " ");
}

int[] copy = origin; // 같은 주소값이 대입됨 => Heap영역에 생성된 같은 배열을 가리킴

System.out.println("\n-- 복사본 배열 출력 --");
for (int i = 0; i < copy.length; i++) {
    System.out.print(copy[i] + " ");
}

System.out.println("\n원본 배열의 해시코드 : " + origin.hashCode());
System.out.println("복사본 배열의 해시코드 : " + copy.hashCode());
// 같은 주소값이 담겨있음

 

? 어떻게 사용하지?

------------------------------ StudentController클래스---------------------------------
package com.kh.example.practice.student.controller;

import com.kh.example.practice.student.model.vo.Student;

public class StudentController {
	// 필드부
	private Student[] sArr = new Student[5];
	public static final int CUT_LINE = 60; // static 상수 필드 - 필드식별자 대문자
	
	// 생성자부
	public StudentController() {
		sArr[0] = new Student("김길동", "자바", 100);
		sArr[1] = new Student("박길동", "디비", 50);
		sArr[2] = new Student("이길동", "화면", 85);
		sArr[3] = new Student("정길동", "서버", 60);
		sArr[4] = new Student("홍길동", "자바", 20);
	};
	
	// 메소드부
	public Student[] printStudent() { 
		return sArr; // Student객체 배열의 주소값 반환
	}
}

------------------------------ StudentMenu클래스---------------------------------
package com.kh.example.practice.student.view;

import com.kh.example.practice.student.controller.StudentController;
import com.kh.example.practice.student.model.vo.Student;

public class StudentMenu {
	// 필드부
	private StudentController ssm = new StudentController();
	
	// 생성자부
	public StudentMenu() { // 기본생성자
		System.out.println("========== 학생 정보 출력 ==========");
		// !!! 얕은복사 - 주소값만 복사해옴 !!!
		Student[] sArr = ssm.printStudent();
        // 변수의 데이터타입과 메소드 리턴값이 일치하는지 확인!!!
		
		for (int i = 0;i < sArr.length; i++) {
			System.out.println(sArr[i].inform());
		}
    }
}

 

2. 깊은 복사 : 동일한 새로운 배열을 하나 생성해 실제 내부값까지 복사

 

[ 깊은 복사 방법 ] 

1. 인덱스로 접근해 값을 대입

1단계 : 기존과 동일한 크기의 새 배열 생성 및 할당

2단계 : 인덱스 값으로 접근해서 대입

int[] origin = {1, 2, 3, 4, 5};

int[] copy = new int[origin.length]; // 1단계

for(int i = 0; i < copy.length; i++) { // 2단계
    copy[i] = origin[i];
}

System.out.println("\n원본 배열의 해시코드 : " + origin.hashCode());
System.out.println("복사본 배열의 해시코드 : " + copy.hashCode());
// 주소값이 복사된 것이 아닌, 새로운 배열의 주소값이 들어감

2. arraycopy()이용

1단계. 새로운 배열을 생성

2단계. System클래스의 arraycopy() 호출 - 몇 번 인덱스부터 몇 개를, 어느 위치에 넣을 것인지 지정 가능

[ 표현법 ]

System.arraycopy(원본배열이름, 원본배열에서 복사를 시작할 인덱스, 복사본배열 이름, 복사본배열에서 복사가 시작될 인덱스, 복사할 개수);

int[] origin = {1, 2, 3, 4, 5};

int[] copy = new int[10]; // 1단계
        
System.arraycopy(origin, 0, copy, 3, 5); // 2단계

System.arraycopy(origin, 2, copy, 9, 2);
// 배열의 범위가 벗어나면 오류 발생 (ArrayIndexOutOfBoundsException)

3. copyOf() 사용

1단계. 새로운 배열을 생성하며 Arrays.copyOf(); 호출 - 무조건 원본배열의 0번 인덱스부터 지정한 개수만큼 복사 진행

[ 표현법 ]

Arrays.copyOf(원본 배열 이름, 복사할 개수);

int[] origin = {1, 2, 3, 4, 5};

int[] copy = Arrays.copyOf(origin, 10);
// 원본 배열보다 큰 값을 제시하면 복사본배열에 공간 생성

4. clone() 이용

1단계. 새로운 배열을 생성하며 clone() 호출 - 원본배열과 똑같이 복사(인덱스 지정 X, 복사할 개수 지정X)

[ 표현법 ] 복사본배열이름 = 원본배열이름.clone();

int[] origin = {1, 2, 3, 4, 5};

int[] copy = origin.clone();