Java/Java의 정석

Chapter 05 배열(Array)

계란💕 2022. 2. 20. 20:29

1. 배열(Array)

  1.1 배열이란?

    Def) 배열: 같은 타입의 여러 변수를 하나의 묶음으로 다루는 것을 배열이라고 한다. 

 

  1.2 배열의 선언과 생성

  - 선언 방법

    -> 타입[] 변수이름 ex) int[] score; String[] name

    -> 타입 변수이름[] ex) int score[]; String name[]

 

  - 배열의 생성

    -> 변수이름 = new 타입[길이] // 배열을 생성(실제 저장공간을 생성한다.)

 

  - 배열의 선언과 생성을 동시에 할 수 있다.

<hide/>
int score = new int[5];

 

  1.3 배열의 길이와 인덱스

  - 인덱스의 범위는 0부터 '배열 길이 -1'까지

  - 인덱스 0부터 시작한다.

  - 배열의 범위에 속하지 않는 값을 인덱스로 하여 출력하도록 하면 "ArrayIndexOutOfBoundsException" 발생한다.

    -> ArrayIndexOutOfBoundsException : 배열의 index가 유효한 범위을 벗어났다는 에러가 난다. 

  - 배열 길이는 거의 제약이 없다. 

  - 배열 길이는 JVM이 관리한다. 

  - 배열의 길이 변경하기 (변경할 수 없고 새로 만들어야한다. ) 

    (1) 더 큰 배열을 새로 생성한다.

    (2) 기존 배열의 내용을 새로운 배열에 복사한다. 

 

 

  1.4 배열의 초기화

  - 배열의 길이가 큰 경우에는 요소 하나하나에 값을 지정하기 보다는 for문을 사용하면 좋다. 

  - 배열의 생성과 초기화를 한 번에 할 수 있다.

<hide/>
int score = new int[]{ 50, 60, 70, 80, 90 };
int score = { 50, 60, 70, 80, 90 }; // new int 생략 가능하다.

  - 배열의 선언과 생성을 따로하는 경우는 new int를 생략할 수 없다. 

<hide/>
int[] score;
score = new int[]{ 50, 60, 70, 80, 90 };  // 정상
score = { 50, 60, 70, 80, 90 };  에러.new int[] 생략할 수 없다.

  - Arrays.ToString(배열 이름) : 배열의 모든 요소를 '[ 첫번째 요소, 두번째 요소, ...]'와 같은 형식의 문자열로 

  - 만들어서 반환한다. (모든 요소를 출력한다. )

 

  Ex ) int[] iArr이라는 배열이 있을 때, iArr의 값을 하나씩 출력하거나 또는 Arrays.ToString 메서드를 이용하지 않고

  바로 출력하면 오류가 난다. 

    -> iArr은 참조변수이기 때문에 변수에 저장된 값, 즉 배열의 주소가 출력될 것이기 때문이다. 

    -> "타입@주소"형식으로 출력된다. 

    -> 배열을 가리키는 참조변수를 출력해봐야 별로 얻을 정보가 없다. 

    -> 예외적으로 char배열은 println메서드로 출력하면 각 요소가 구분자 없이 그대로 출력된다.

    -> println()메서드가 char배열일 때만 이렇게 동작하도록 작성되었기 때문이다. 

 

 

  1.5 배열의 복사

  1) 얕은 복사: 값의 주소를 저장한다. 

  2) 깊은 복사: 배열의 원소를 하나하나 저장한다. 

 

 

  Ex) ArrayEx2 - 얕은 복사

<hide/>
int[] arr= {1, 2, 3, 4, 5};
int[] tmp = new int[arr.length * 2];
for(int i = 0;i < arr.length;++i){
    tmp[i] = arr[i];
}
arr = tmp;
System.out.println(Arrays.toString(arr));
System.out.println(Arrays.toString(tmp));

 

  • 참조변수 arr에 참조변수 tmp의 값을 저장한다. 
  • arr = tmp 와 같이 대입하고 난 다음에는, 이름만 다를 뿐 완전히 같은 배열이 된다. 
  • 즉,  arr 과 tmp의 주솟값이 같기 때문에 arr == tmp 를 조회하면 true이다. 
  • 배열은 참조변수를 통해서만 접근가능하므로 자신을 가리키는 참조변수가 없는 배열은 사용할 수 없다. 이렇게 쓸모없게 된 배열은 JVM의 가비지 컬렉터에 의해 자동 제거된다. 

  Note) 실행 결과

 

 

 

 

  Ex) 얕은 복사, 깊은 복사

<hide/>
public class ArrayTest {
	public static void main(String[] args) {
		int [] arr1 = {10, 20, 25, 30, 45};
		//얕은 복사
		int[] arr2 = new int[arr1.length];
		arr2 = arr1;
		for(int i= 0; i < arr1.length; ++i) {
			System.out.println(arr1[i]);		
		}
		for(int i= 0; i < arr2.length; ++i) {
			System.out.println(arr2[i]);		
		}
		
		//깊은 복사
		int[] arr3 = new int[arr1.length];
		for(int i = 0;i < arr3.length; ++i) {
			arr3[i] = arr1[i];
		}
		for(int i= 0; i < arr3.length; ++i) {
			System.out.println(arr3[i]);		
		}
	}
}

   Note) 실행 결과

 

 

  Ex) 배열의 복사 

  - int [] c1 = {1, 2, 3, 4};

  1) int [] c2 = Arrays.copyOf(c1, c1.length); 

  2) int [] c2 = c1.clone();         // Object.clone();

  3) System.arraycopy()  : System클래스의 arraycopy()를 사용하면 배열을 복사할 수 있다.

  - System.arraycopy( num, 0, newNum, 0, num.length ) : num[0]에서  newNum[0]으로 num.length개의 데이터를 복사한다.

  

<hide/>
package javaStudy;
public class ArrayEx4 {
	public static void main(String[] args) {
		char[] abc = { 'A', 'B', 'C', 'D'};
		char[] num = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
		System.out.println(abc);
		System.out.println(num);
		
		char[] result = new char[ abc.length + num.length ];// abc와 num을 붙여서 하나의 배열로 만든다.
		System.arraycopy(abc, 0, result, 0, abc.length);
		System.arraycopy(num, 0, result, abc.length, num.length);
		System.out.println(result);
		
		System.arraycopy(abc, 0, num, 0, abc.length);  // 배열abc를 배열num의 첫번째 위치부터 배열abc길이만큼 복사한다. 
		System.out.println(num);
		System.arraycopy(abc, 0, num, 6, 3);  // num의 index 6번 위치에 3개를 복사한다. 
		System.out.println(num);
	}
}

  Note) 실행 결과

 

  1.6 배열의 활용

  Ex 5-8) 로또 번호 만들기

<hide/>
package javaStudy;
public class ArrayEx8 {
	public static void main(String[] args) {
		int[] ball = new int[45];	
		for(int i = 0; i < ball.length; ++i) 
			ball[i] = i + 1; // ball[0]에 1이 저장된다. 
			int temp = 0;  // 두 값을 바꾸기 위한 임시 변수
			int  j = 0;  // 임의의 값을 얻어서 저장할 변수
			for(int i = 0; i < 6; ++i) {
				j = (int)(Math.random() * 45); // 0~44사이의 임의의 값을 얻는다.
				temp = ball[i];
				ball[i] = ball[j];
				ball[j] = temp;			
			}
			for(int i = 0; i < 6; ++i)
				System.out.printf("ball[%d] = %d%n", i, ball[i]);
	}
}

  Note) 실행 결과

  - 먼저, 길이가 45인 배열에 1부터 45까지의 값을 담는다.

  - 반복문을 이용해서 ball[i]와 random()에 의해서 결정된 임의의 위치에 있는 값과 자리를 바꾸는 것을 6번 반복한다. 

  - 1 ~ 45번까지 번호가 쓰인 카드를 섞어서 맨 위의 6장을 꺼내는 것과 같다. 

 

 

  Ex 5-10)  버블소트(Bubble Sort)

<hide/>
package javaStudy;
public class ArrayEx10 {
	public static void main(String[] args) {
		int[] numArr = new int[10];
		for(int i = 0; i < numArr.length; ++i) {
			System.out.print(numArr[i] = (int)(Math.random() * 10 ));
		}
		System.out.println();
		for(int i = 0; i < numArr.length - 1 ; ++i) {
				boolean changed = false;  // 자리바꿈이 생겼는지 확인한다.
				for(int j = 0; j < numArr.length - 1 - i; ++j) {
					if(numArr[j] > numArr[j + 1] ) {  //  오른쪽 값이 작으면 서로 바꾼다. 
						int temp = numArr[j];
						numArr[j] = numArr[j + 1];
						numArr[j + 1] = temp;
						changed = true;  // 자리바꿈이 발생했으니 changed를 true로 바꾼다.
					}
				}
				if( !changed) break;  // 자리바꿈이 없으면 반복문을 벗어난다.
				for(int k = 0; k < numArr.length; ++k)
					System.out.print(numArr[k]); //  정렬된 결과를 출력한다.
				System.out.println();
		}
	}
}

  Note) 실행 결과

  - 길이가 10인 배열에 0 ~ 9사이의 임의 값으로 채운 다음에 버블 소트 알고리즘을 통해서 크기순으로 배열한다. 

  - 배열의 길이가 n일 때, 배열의 첫 번째부터 n - 1까지의 요소에 대해 근접한 값과 크기를 비교해서 자리 바꿈을 반복. 

  - for문을 비교할수록 비교해야 하는 범위는 하나씩 줄어든다.

  - 그래서 원래는 numArr.length - 1 번을 비교해야 하는데 

  - 매 반복마다 비교횟수가 1씩 줄어들므로 바깥쪽 for문의 제어 변수 i를 빼주는 것이다. 

  - 자리 바꿈이 없다는 건 정렬이 완료됨을 뜻한다. 자리바꿈 없으면 break로 반복문으로 빠져나간다.

 

 

2. String 배열

  2.1 String배열의 선언과 생성

String[] name = new String[3];  // 3개의 문자열을 담을 수 있는 배열을 생성한다.

  - 3개의 String타입의 참조변수를 저장하기 위한 공간이 마련되도 참조형 변수의 기본 값은 null이므로 각 요소의 값은   null로 초기화 된다. 

  - String은 클래스이므로 new 연산자를 이용해서 객체를 생성해야만 한다.

 

 

  2.2 String 배열의 초기화

  Ex 5-13) 16진수를 2진수로 변환

<hide/>
package javaStudy;
public class ArrayEx13 {
	public static void main(String[] args) {
		char[] hex = {'C', 'A', 'F', 'E'};
		String[] binary = { "0000", "0001", "0100", "0011",
						  "0100", "0101", "0110", "0111", 
						  "1000", "1001", "1010", "1011",
						  "1100", "1101", "1110", "1111"};
		String result = "";
		for(int i = 0; i < hex.length; ++i) {
			if( '0' <= hex[i]  && hex[i] <= '9') {
				result += binary[hex[i] - '0'];  // '8'-'0'의 결과는 8이다. 
			}else {
				result += binary[hex[i] - 'A' + 10 ];  // 'C' - 'A'의 결과는 2이다. 
			}			
		}								//String(char[] value)
		System.out.println("hex: "+ new String(hex));
		System.out.println("binary: "+ result );
	}
}

  Note) 실행 결과

  - 변환하고자 하는 16진수를 hex에 나열한다. 

  - 문자 배열 binary에는 0000~1111까지 모두 16개의 값을 문자열로 저장한다. 

  - for문을 이용하여 배열 hex에 저장된 문자를 하나씩 읽어서 그에 해당하는 이진수 표현을 

  - 배열 binary에서 얻어 result에 덧붙이고 그 결과를 화면에 출력한다.  

 

 

  2.3 char배열과 String 클래스

  - 자바에서는 char 배열이 아닌 String클래스를 이용해서 문자열을 처리한다. 

  - 그 이유는 String클래스가 char배열에 여러 가지 기능(메서드)을 추가하여 확장한 것이기 때문이다. 

  - 그러므로 char보다 String을 사용하는 게 문자열을 다루기 더 편하다. 

  - String클래스의 주요 메서드

    -> 문자열이름.charAt(int index) : 문자열에서 해당 위치(index)에 있는 문자를 반환한다. 

    -> int length() : 문자열의 길이를 반환한다. 

    -> 문자열이름.substring(int from, int to) : 문자열에서 해당 범위(from~to)에 있는 문자열을 반환한다. (to는 포함 X)

    -> char[] toCharArray() : 문자열을 문자 배열(char[])로 변환해서 반환한다. 

  - String과 char의 변환

  Ex 5-14)

<hide/>
package javaStudy;
public class ArrayEx14 {
	public static void main(String[] args) {
		String src = "ABCDE";
		for(int i = 0; i < src.length(); ++i ) {
			char ch = src.charAt(i);  // src의 i번째 문자를 ch에 저장한다. 
			System.out.println("src.charAt(" + i+ "):"+ ch);  
		}
		char[] chArr = src.toCharArray();  // String을 char[]로  변환
		System.out.println(chArr); // char배열(char[])을 출력
	}
}

  Note) 실행 결과

   - char[] chArr = src.toCharArray();   String src를 char[]로  변환

  

 

  2.4 커맨드 라인을 통해 입력받기

  - 커맨드 라인을 이용해서 프로그램을 실행할 때 클래스 이름 뒤에 공백 무자로 구분하여

  - 여러 개의 문자열을 프로그램에 전달할 수 있다.

  - Integer.parseInt(): 문자열을 숫자로 바꾸는 메서드 

 

 

3. 다차원 배열 

  3.1 2차원 배열의 선언과 인덱스

  - 2차원 배열을 선언하는 방법은 1차원 배열과 같다. 괄호[]가 하나 더 들어간다. 

  - 아래 코드는 4행 3열의 2차원 배열을 생성.

int [][] score = new int[4][3];

 

  3.2 2차원 배열의 초기화

  Ex 5-18)

<hide/>
package javaStudy;
public class ArrayEx18 {
	public static void main(String[] args) {
		int [][] score = {
							{100, 100, 100},
							{20, 20, 20},
							{30, 30, 30},
							{40, 40, 40}
						 };
		int sum = 0;
		for(int i = 0; i < score.length; ++i) {
			for(int j = 0; j < score[i].length; ++j) {
				System.out.printf("score[%d][%d] = %d%n", i, j, score[i][j]);		
			}
		}
		for(int[] tmp : score) {  // score의 각 요소(1차원 배열 주소)를 tmp에 저장
			for(int i : tmp) {  // tmp는 1차원 배열을 가리키는 참조 변수
				sum += i;
			}
		}
		System.out.println("sum = " + sum);
	}
}

  Note) 실행 결과

  - 2차원 배열score의 모든 요소의 합을 구하고 출력하는 예제이다. 

 

  3.3 가변 배열

  - 2차원 이상의 다차원 배열을 생성할 때. 전체 배열 차수 중 마지막 차수의 길이를 지정하지 않고

  - 추후에 각기 다른 길이의 배열을 생성함으로써 고정된 형태가 아닌 보다 유동적인 가변 배열을 구성할 수 있다. 

 

  3.4 다차원 배열의 활용

  Ex 5-20) 입력한 2차원 좌표의 위치에 X를 표시

<hide/>
package javaStudy;
import java.util.*;
public class MutiArrEx1 {
	public static void main(String[] args) {
		final int SIZE = 10;
		int x = 0, y = 0;
		char [][] board = new char[SIZE][SIZE];
		byte [][] shipBoard = {
			//  1  2  3  4  5  6  7  8  9  
				{ 0, 0, 0, 0, 0, 0, 1, 0, 0 }, // 1
				{ 1, 1, 1, 1, 0, 0, 1, 0, 0 }, // 2
				{ 0, 0, 0, 0, 0, 0, 1, 0, 0 }, // 3
				{ 0, 0, 0, 0, 0, 0, 1, 0, 0 }, // 4
				{ 0, 0, 0, 0, 0, 0, 1, 0, 0 }, // 5
				{ 1, 1, 0, 1, 0, 0, 0, 0, 0 }, // 6
				{ 0, 0, 0, 1, 0, 0, 0, 0, 0 }, // 7
				{ 0, 0, 0, 1, 0, 0, 0, 0, 0 }, // 8
				{ 0, 0, 0, 0, 0, 1, 1, 1, 0 } // 9
		};
		for(int i = 1; i < SIZE; ++i)
			board[0][i] = board[i][0] = (char)(i + '0');
		Scanner scanner = new Scanner(System.in);
		while(true) {
			System.out.printf("좌료를 입력하세요.(종료는 00)>");
			String input = scanner.nextLine();  // 화면에 입력받은 내용을 input에 저장
			if(input.length() == 2) {  // 두 글자를 입력한 경우
				x = input.charAt(0) - '0'; // 문자를 숫자로 변환
				y = input.charAt(1) - '0';
				
				if( x == 0 && y == 0)  // x, y가 모두 0일 때 종료.
					break;
			}
			if(input.length() != 2 || x <= 0 || x >= SIZE || y <= 0 || y >= SIZE ) {
				System.out.println("잘못된 입력입니다. 다시 입력해주세요.");
				continue;
			}
			// shipBoard[x-1][y-1]의 값이 1이면, 'o'를 board[x][y]에 저장한다. 
			board[x][y] = shipBoard[x - 1][y - 1] == 1 ? 'O' : 'X';			
			// 배열 board의 내용을 화면에 출력한다.
			for(int i = 0;i < SIZE; ++i) 
				System.out.println(board[i]);  //board[i]는 1차원 배열
			System.out.println();
		}	
	}
}

  Note) 실행결과

  - 둘이 마주 앉아 다양한 크기의 배를 상대방이 알지 못하게 배치한 다음, 번갈아가며

  - 좌료를 불러서 상대방의 배의 위치를 알아내는 게임의 예제이다. 

  - 2차원 배열 char배열 board는입력한 좌표를 표시하기 위한 것이다.

  - 2차원 배열 byte배열 shipBoard에는 상대방의 배의 위치를 저장한다. 

  - 0은 바다, 1은 배가 있는 것이다. 

  - char배열이라서 println메서드를 사용가능하다. 

 

'Java > Java의 정석' 카테고리의 다른 글

Chapter 07 객체지향 프로그래밍 II  (0) 2022.02.22
Chapter 06 객체지향 프로그래밍 I  (0) 2022.02.22
Chapter 04 조건문과 반복문  (0) 2022.02.19
Chapter 03 연산자(Operator)  (0) 2022.02.19
Chapter 02 변수(Variable)  (0) 2022.02.17