배열

황찬호·2021년 4월 28일

JAVA

목록 보기
3/5

배열

1.1 배열이란?

같은 타입의 여러 변수를 하나의 묶음으로 다루는 것을 '배열(array)'이라고 한다. 많은 양의 데이터를 저장하기 위해서, 그 데이터의 숫자만큼 변수를 선언해야 한다면 매우 혼란스러울 것이다. 이럴 경우에 배열을 사용하면 많은 양의 데이터를 손쉽게 다룰 수 있다.

배열은 같은 타입의 여러 변수를 하나의 묶음으로 다루는 것.

여기서 중요한 것은 '같은 타입'이어야 한다는 것이며, 서로 다른 타입의 변수들로 구성된 배열은 만들 수 없다.
서로 다른 타입의 값들을 하나로 묶으려면 '클래스'를 정의하면 되는데 다음에 배우게 된다.

1.2 배열의 선언과 생성방법

int[] score; // (타입) 변수이름;
String[] name;

배열의 생성

배열을 선언한 다음에는 배열을 생성해야 한다. 배열을 선언하는 것은 단지 생성된 배열을 다루기 위한 참조변수를 위한 공간이 만들어질 뿐이고, 배열을 생성해야만 비로소 값을 저장할 수 있는 공간이 만들어지는 것이다.
배열을 생성하기 위해서는 연산자 'new'와 함께 배열의 타입과 길이를 지정해 주어야 한다.

타입[] 변수이름;			// 배열을 선언(배열을 다루기 위한 참조변수 선언)
변수이름 = new 타입[길이];	// 배열을 생성(실제 저장공간을 생성)

아래의 코드는 '길이가 5인 int배열'을 생성한다.

int[] score;		// int타입의 배열을 다루기 위한 참조변수 score선언
score = new int[5];	// int타입의 값 5개를 저장할 수 있는 배열
// 한 줄로 선언 가능하다.
int[] score = new int[5]; // 길이가 5인 int배열을 선언,생성

1.3 배열의 길이와 인덱스

생성된 배열의 각 저장공간을 '배열의 요소'라고 하며, '배열이름[인덱스]'의 형식으로 배열의 요소에 접근한다. 인덱스는 배열의 요소마다 붙여진 일련번호로 각 요소를 구별하는데 사용된다. 다만 인덱스는 1이 아닌 0부터 시작한다.

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

배열에 값을 저장하고 읽어오는 방법은 변수와 같다. 단지 변수이름 대신 '배열이름[인덱스]'를 사용한다는 점만 다르다.

score[3] = 100;		// 배열 score의 4번째 요소에 100을 저장
int value = score[3];	// 배열 score의 4번째 요소에 저장된 값을 읽어서 value에 저장

배열의 또 다른 장점은 index로 상수 대신 변수나 수식도 사용할 수 있다. for문을 이용해서 간단히 할 수 있다.
for문이 제어변수 i는 배열의 index로 사용하기에 딱 알맞아서, 배열을 다룰 때는 for문이 필수적이다.

for (int i=0; i<5; i++) {
	score[i] = i + 10;
}

만일 괄호[]안에 수식이 포함된 경우, 이 수식이 먼저 계산된다. 그래야만 배열의 몇 번째 요소인지 알 수 있기 때문이다.

int tmp = score[i + 1];

배열이름.length

자바에서는 JVM(Java Virtual Machine)이 모든 배열의 길이를 별도로 관리하며, '배열이름.length'를 통해서 배열의 길이에 대한 정보를 얻을 수 있다.

int[] arr = new int[5];	// 길이가 5인 int배열
int tmp = arr.length;	// arr.length의 값은 5이고 tmp에 5가 저장된다.

그래서 for문의 조건식에 배열의 길이를 직접 적어주는 것보다 '배열이름.length'를 사용하는 것이 좋다. 위의 for문을 '배열이름.length'를 사용해서 변경하면 다음과 같다.

int[] score = new int[5]; // int[6]에서 5로 변경

for (int i=0; i < score.length; i++) { // 조건값 (i < 6)을 따로 건드려 줄 필요 없이 배열이름.length 메소드 사용
	System.out.println(score[i]);

배열의 길이 변경하기

배열은 한번 선언되고 나면 길이를 변경할 수 없다고 배웠는데, 그렇다면 배열에 저장할 공간이 부족한 경우에는 어떻게 해야 할까? 이때는 더 큰 길이의 새로운 배열을 생성한 다음, 기존의 배열에 저장된 값들을 새로운 배열에 복사하면 된다.

  • 배열의 길이를 변경하는 방법
    - 더 큰 배열을 새로 생성한다.
    - 기존 배열의 내용을 새로운 배열에 복사한다.

이러한 작업들은 꽤나 비용이 많이 들기 때문에, 처음부터 배열의 길이를 넉넉하게 잡아줘서 새로 배열을 생성해야하는 상황이 가능한 적게 발생하도록 해야 한다. 기존의 2배정도의 길이로 생성하는 것이 좋다.
자세한 내용은'1.5 배열의 복사'에서 다룬다.

1.4 배열의 초기화

배열은 생성과 동시에 자동적으로 자신의 타입에 해당하는 기본값으로 초기화되므로 배열을 사용하기 전에 따로 초기화를 해주지 않아도 되지만, 원하는 값을 저장하려면 아래와 같이 각 요소마다 값을 지정해 줘야한다.

int[] score = new int[5];
score[0] = 50; 
score[1] = 60;
score[2] = 70;
score[3] = 80
score[4] = 90;

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

int[] score = new int[5];

for (int i=0 i < score.length; i++) {
	score[i] = (i*10) + 50;
}

그러나 for문으로 배열을 초기화 하려면, 저장하려는 값에 일정한 규칙이 있어야만 가능하기 때문에 자바에서는 다음과 같이 배열을 간단히 초기화 할 수 있는 방법을 제공한다.

int[] score = new int[] { 50, 60, 70, 80, 90}; // 배열의 생성과 초기화를 동시에 함

괄호{}안에 저장할 값들을 쉼표로 구분해서 나열하면 되며 괄호[]안에 배열의 길이는 적지 않는다. 괄호{}안에 적힌 값의 개수에 의해 배열의 길이가 자동적으로 결정되기 때문이다.

int[] score = new int[] { 50, 60, 70, 80, 90};
int[] score = {50, 60, 70, 80, 90}; // new int[]를 생략할 수 있음

'new 타입[]'을 생략하여 코드를 더 간단히 할 수도 있다. 생략된 형태의 코드가 더 간단하므로 자주 사용된다. 다만 다음과 같이 배열의 선언과 생성을 따로 하는 경우에는 생략할 수 없다.

int[] score;
score = new int[] { 50, 60, 70, 80, 90};	// OK
score = { 50, 60, 70, 80, 90};			// Error. new int[]를 생략할 수 없음

또 다른 예로, 아래와 같이 매개변수로 int배열을 받는 add메서드가 정의되어 있고 이 메서드를 호출해야 할 경우 역시 'new 타입[]'을 생력할 수 없다.

int add(int[] arr) {
	//...
    }
    int result = add(new int[] { 100, 90, 80, 70, 60,});	// OK
    int result = add({ 100, 90, 80, 70, 60});			//Error. new int[]를 생략할 수 없음 

그리고 괄호{} 안에 아무것도 넣지 않으면, 길이가 0인 배열이 생성된다. 참조변수의 기본 값은 null이지만, 배열을 가리키는 참조변수는 null대신 0인 배열로 초기화하기도 한다.

int[] score = new int[0];	// 길이가 0인 배열
int[] score = new int[] {};	// 길이가 0인 배열
int[] score = {};		// 길이가 0인 배열

배열의 출력

배열을 초기화 할 때 for문을 사용하듯이, 배열에 저장된 값을 확인할 때도 다음과 같이 for문을 사용하면 된다.

int[] iArr = { 100, 90, 80, 70, 60};
    // 배열의 요소를 순서대로 하나씩 출력
    for(int i=0; i < iArr.length; i++) {
    System.out.println(iArr[i]);
    }

더 간단한 방법은 'Array.toString(배열이름)'메서드를 사용하는 것이다. 이 메서드는 배열의 모든 요소를 '[첫번째 요소, 두번째 요소, ...]'와 같은 형식의 문자열로 반환한다. 이 메서드와 관련된 내용들이 더 있지만, 진도를 나가면서 자연스럽게 알게 될 것들이다.

int[] iArr = { 100, 90, 80, 70, 60};
// 배열 iArr의 모든 요소들을 출력
System.out.println(Arrays.toString(iArr));

예제를 통해 복습

import java.util.Arrays; // Arrays.toString()을 사용하기 위해 추가

public class ArrayEx2 {

	public static void main(String[] args) {
		int[] iArr1 = new int[10];
		int[] iArr2 = new int[10];
//		int[] iArr3 = new int[10] {100, 95, 80, 70, 60};
		int[] iArr3 = { 100, 95, 80 ,70, 60};
		char[] chArr = {'a', 'b', 'c', 'd'};
		
		for (int i=0; i< iArr1.length; i++) {
			iArr1[i] = i + 1; // 1~10의 숫자를 순서대로 배열에 넣는다.
		}
		
		for (int i=0; i< iArr2.length; i++) {
			iArr2[i] = (int)(Math.random()*10 + 1) ; // 1~10의 값을 배열에 저장
		} /* Math.random메소드는 0.0 ~ 1.0까지의 실수값을 무작위로 선정하기 때문에 *10을 하여 0.0 ~ 10.0까지 생성하고 
		타입변환int는 소수점 이하의 값을 버리기때문에 + 1을 하여 1.0 ~ 11.0으로 변경 */
		
		
		// 배열에 저장된 값을 출력
		for (int i=0; i< iArr1.length; i++) {
			System.out.print(iArr1[i] + ", ");
		}
		
		System.out.println();
		System.out.println(Arrays.toString(iArr2));
		System.out.println(Arrays.toString(iArr3));
		System.out.println(Arrays.toString(chArr));
		System.out.println(iArr3);
		// 메서드를 사용하지 않고 지정하면  '타입@주소(16진수)'의 형식으로 출력된다.
		System.out.println(chArr);
		// char배열은 예외이다.
	}
}

1.5 배열의 복사

배열은 한번 생성하면 그 길이를 변경할 수 없기 때문에 더 많은 저장공간이 필요하다면 큰 배열을 새로 만들고 이전 배열로부터 내용을 복사해야한다. 배열을 복사하는 방법은 두가지가 있다.

for문을 이용한 배열의 복사

이러한 작업들은 꽤나 비용이 많이 들기 때문에, 처음부터 배열의 길이를 넉넉하게 잡아줘서 새로 배열을 생성해야하는 상황이 가능한 적게 발생하도록 해야 한다. 그렇다고 배열의 길이를 너무 크게 잡으면 메모리를 낭비하게 되므로, 기존의 2배 정도의 길이로 배열을 생성하는 것이 좋다.

int[] arr = new int[5];
// ...
int[] tmp = new int[arr.length * 2]; // 기존 배열보다 길이가 2배인 배열 생성

for(int i=0; i < arr.length; i++) {
    tmp[i] = arr[i]; // arr[i]의 값을 tmp[i]에 저장
}
arr = tmp; // 참조변수 arr이 새로운 배열을 가르키게 한다.
for문을 이용항 배열의 복사 예제
public class ArrayEx3 {

	public static void main(String[] args) {
		int[] arr = new int[5];
		
		for (int i=0; i < arr.length; i++) {
			arr[i] = i + 1;
		}
		System.out.println("변경전 - arr.length: " + arr.length);
		for(int i=0; i < arr.length; i++) {
			System.out.println("arr[" + i + "]: " + arr[i] );
		}
		int[] tmp = new int[arr.length * 2];
			
		// 배열 arr에 저장된 값들을 배열 tmp에 복사한다.
		for(int i=0; i < arr.length; i++) {
			tmp[i] = arr[i];
		}
		arr = tmp; // tmp에 저장된 값을 arr에 저장한다.
			
		System.out.println("변경후 - arr.length: " + arr.length);
		for(int i=0; i<arr.length; i++) {
			System.out.println("arr[" + i + "]: " + arr[i]);
		}
	}
}

System.arraycopy()를 이용한 배열의 복사

for문 대신 System클래스의 arraycopy()를 사용하면 보다 간단하고 빠르게 배열을 복사할 수 있다. for문은 배열의 요소 하나하나에 접근해서 복사하지만, arraycopy()는 지정된 범위의 값들을 한 번에 통째로 복사한다. 각 요소들이 연속적으로 저장되어 있다는 배열의 특성때문에 이렇게 처리하는 것이 가능한 것이다.

배열의 복사는 for문 보다 System.arraycopy()를 사용하는 것이 효율적이다.

System.arraycopy(num, 0, newNum, 0m num.length);
// num[0]에서 newNum[0]으로 num.length개의 데이터 복사

만약 복사하려는 배열의 위치가 적절하지 못하여 복사하려는 내용보다 여유 공간이 적으면 에러(ArrayIndexOutOfBoundsException)가 발생한다.

System.arraycopy()를 이용한 배열의 복사 예제
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);
		
		// 배열 abc와 num을 붙여서 하나의 배열(result)로 만든다.
		char[] result = new char[abc.length + num.length];
		System.arraycopy(abc, 0, result, 0, abc.length);
		System.arraycopy(num, 0, result, abc.length, num.length);
		System.out.println(result);
		
		// 배열 abc을 배열 num의 첫번째 위치부터 배열 abc의 길이만큼 복사
		System.arraycopy(abc, 0, num, 0, abc.length);
		System.out.println(num);
		
		// number의 인덱스6 위치에 3개를 복사
		System.arraycopy(abc, 0, num, 6, 3);
		System.out.println(num);
	}
}

1.6 배열의 활용

총합과 평균
public class ArrayEx5 { // 총합과 평균

	public static void main(String[] args) {
		int sum = 0;		// 총점을 저장하기 위한 변수
		float average = 0f;	// 평균을 저장하기 위한 변수
		
		int[] score = {100, 88, 100, 100, 90};
		
		for(int i=0; i < score.length; i++) {
			sum += score[i];
		}
		average = sum / (float)score.length; // 계산결과를 float으로 얻기 위함
		
		System.out.println("총점: " + sum);
		System.out.println("평균: " + average);
	}
}
최대값과 최소값
public class ArrayEx6 { // 최대값과 최소값

	public static void main(String[] args) {
		int[] score = { 79, 88, 91, 33, 100, 55, 95};
		
		int max = score[0]; // 배열의 첫번째 값으로 최대값을 초기화 한다.
		int min = score[0]; // 배열의 첫번째 값으로 최소값을 초기화 한다.
		
		for (int i=1; i < score.length; i++) {
			if(score[i] > max) {
				max = score[i];
			} else if(score[i] < min) {
				min = score[i];
			}
		}
		System.out.println("최대값: " + max);
		System.out.println("최소값: " + min); 
	}
}
섞기
public class ArrayEx7 { // 섞기

	public static void main(String[] args) {
		int[] numArr = new int[10];
		
		for (int i=0; i < numArr.length; i++) {
			numArr[i] = i;
			System.out.print(numArr[i]);
		}
		System.out.println();
		
		for (int i=0; i < 100; i++) {
			int n = (int)(Math.random() * 10); // 
			int tmp = numArr[0];
			numArr[0] = numArr[n];
			numArr[n] = tmp;
		}
		for(int i=0; i < numArr.length; i++) {
			System.out.print(numArr[i]);
		}
	}
}
public class ArrayEx8 { // 섞기

	public static void main(String[] args) {
		// 45개의 정수값을 저장하기 위한 배열 생성
		int[] ball = new int[45];
		
		// 배열의 각 요소에 1~45의 값을 저장한다.
		for(int i=0; i < ball.length; i++) {
			ball[i] = i + 1;	// ball[0]에 1이 저장된다.
		}
		int temp = 0;	// 두 값을 바꾸는데 사용할 임시변수
		int j = 0;		// 임의의 값을 얻어서 사용할 변수
		
		// 배열의 i번째 요소와 임의의 요소에 저장된 값을 서로 바꿔서 값을 섞는다.
		// 0번째부터 5번째 요소까지 모두 6개만 바꾼다.
		for(int i=0; i < 6; i++) {
			j = (int)(Math.random()* 45); // 0~44범위의 임의의 값을 얻는다.
			temp = ball[i];
			ball[i] = ball[j];
			ball[j] = temp;
		}
		// 배열 ball의 앞에서부터 6개의 요소를 출력한다.
		for (int i=0; i < 6; i++) {
			System.out.printf("ball[%d]=%d\n", i, ball[i]);
		}	
	}
}
임의의 값으로 배열 채우기
import java.util.Arrays;

public class ArrayEx9 { // 임의의 값으로 배열 채우기

	public static void main(String[] args) {
		int[] code = { -4, -1, 3, 6, 11};
		int[] arr = new int[10];
		
		for (int i=0; i < arr.length; i++) {
			int tmp = (int)(Math.random() * code.length);
			arr[i] = code[tmp];
		}
		System.out.println(Arrays.toString(arr));
	}
}
정렬하기
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();
		}
	}
}
빈도수 구하기
public class ArrayEx11 { // 빈도수 구하기

	public static void main(String[] args) {
		int[] numArr = new int[10];
		int[] counter = new int[10];

		for(int i=0; i < numArr.length; i++) {
			numArr[i] = (int)(Math.random() * 10);
			System.out.print(numArr[i]);
		}
		System.out.println();
		for(int i=0; i < numArr.length; i++) {
			counter[numArr[i]]++;
		}
		for(int i=0; i < numArr.length; i++) {
			System.out.println(i + "의 개수: " + counter[i]);
		}
	}
}
profile
천천히 그리고 꾸준히

0개의 댓글