본문 바로가기

Java/기본 이론

[JAVA] Chapter 13. 배열 변수

 

1. 배열 변수의 개념

배열은 행과 열로 구성된 일련의 데이터를 말하는데, 기본적으로 1행에 순차적으로 나열된 정보를 배열이라고 한다. 

 

봄, 여름, 가을, 겨울

 

사계절의 구성 요소를 나열하면 위와 같이 봄, 여름, 가을, 겨울로 나열될 것이다.

다시말해 사계절이라는 상자에 봄, 여름, 가을, 겨울이 담겨있는 것이다. 

배열이란 이와 같이 하나의 속성에 다수의 다른 속성이 담겨있는 것을 말한다.

즉, 배열은 객체라는 것이다. 

 

배열 변수는 이러한 배열의 특성에 따라, 하나의 변수에 나열된 다수의 데이터를 담는 것을 말한다.

담겨져있는 데이터가 (11, 7, 8, 22, 0) 처럼 연관이 없어보여도 '배열 변수'로 선언되는 순간 반드시 2가지의 공통점을 가진다.

 

  1. 배열에 포함된 구성요소의 상위 항목 변수는 동일하다
  2. 배열에 포함된 구성요소는 같은 데이터 타입을 가진다. 

배열에 담기는 데이터는 반드시 같은 타입의 데이터여야 한다.

이는 배열 변수가 '변수'이기 때문인데, Java에서 변수는 반드시 데이터를 담는 상자의 크기를 반드시 지정하기 때문이다.

상위 변수가 같다는 것은 배열로 선언된 데이터에 접근하기 위한 기본 값이 존재한다는 것이다 .

이를 이해하기 위해 아래에 배열 선언과 생성을 보자. 

 

  • 배열 선언: int[] math; 
  • 배열 생성: math = new int[10]; | 

배열 변수를 선언하면 데이터타입 뒤에 대괄호([])를 위치하는데, 해당 모양이 배열 변수 선언에 대한 약속이다.

기본형 데이터 타입이 아니기 때문에 참조형 데이터이며, 참조형 데이터임에 따라 heap 영역에 공간할당을 선언해야 한다.

이러한 과정을 배열 생성이라고 하며, new 명령어를 통해 배열에 총 갯수를 지정해준다. 

 

예를 들자면, 내가 데이터 타입이 int인 math라는 변수를 선언하였다.

해당 배열에 0~9까지 데이터를 담고 싶기 때문에, new를 통해 해당하는 개수만큼 지정하였다.

이제 heap 영역에는 10개의 상자가 생긴 상태이고, 각각의 상자에 겹치지 않게 0~9의 숫자를 담았다.

이제 math 변수는 배열로 0~9까지의 데이터를 가진 상자를 찾기 위해 필요한 '지도'가 담긴 큰 상자이다. 

 

2. 배열 변수의 값

배열 변수는 동일한 속성의 데이터를 각각 선언하고 할당할 때 사용한다.

배열은 결국 객체이고, 객체의 속성에서 규칙이 발견된다면 알고리즘을 구성하여 할당하는 것 또한 가능하다. 

기본적으로 배열 변수의 선언은 아래와 같이 3가지로 구분할 수 있다. 

for 문(제어문) 사용

직접 할당 

열거하여 할당

int[] math;

math = new int[5];

 

int i;

for(i=0; i<5; i++) {

math[i] = 100-(i*10);}

int[] math;

math = new int[5];


math[0] = 100; 

math[1] = 90; 

math[2] = 80; 

math[3] = 70; 

math[4] = 60; 

int[] kor;

kor = new int[] {50, 50, 50, 50}


int[] com = {50, 50, 50, 50};

int[] math;

math = new int[5];

math[0] = 100; 
math[1] = 90; 
math[2] = 80; 
math[3] = 70; 
math[4] = 60; 

배열 변수에 값이란 = 오른쪽에 나오는 숫자이다.

물론 데이터 타입에 따라 담기는 정보가 다를수도 있다.

배열의 선언, 생성, 값으로 구성된 배열이라는 객체를 분해하여 보자면 다음과 같다 .

 

math[0] = 100; 
math 변수명 
[0] 배열의 순번, 그 중 숫자0은 인덱스 
100 heap에 할당된 배열의 값

엄밀히 말해서 stack 메모리에 할당된 배열 변수의 값과 heap에 할당된 값은 다르다.

stack에 할당된 값은 heap에서 데이터를 참조하기 위한 '참고값 주소'이다. 

 

3. 2차원 배열 

2차원 배열은 배열의 x값과 y값을 할당하는 방식이다.

기본 형태는 배열과 동일하지만 배열이 x값에 따른 직선상에 할당 값이라면, 2차원 배열은 x와 y값에 따른 평면상의 할당 값이다. 

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

int[0][0] = 1; 

int[0][1] = 2;

int[0][2] = 3;

int[1][0] = 4; 


… int[2][3] = 12;

 

 2차원 배열은 행과 열을 순서로 데이터가 할당된다. 

위의 그림과 같이 a라는 참조값 주소를 추적하면, heap 영역에 할당된 a[] 들의 참조값 주소를 확인할 수 있다.

이 영역에서 다시 참조한 값을 찾아가면 개발자가 할당한 a[][]의 값이 확인되는 것이다. 

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

 

2차원 배열 변수는 담고자하는 데이터가 일정한 규칙을 갖는다면 for문을 이용하여 선언할 수 있고, 직접 선언하는 것 또한 가능하다. 

 

4. 배열 변수의 길이

배열 변수는 heap 메모리에 데이터가 할당되는 상태이며, 0부터 시작하여 갯수만큼 생성된다.

heap에 할당된 데이터의 개수는 알고리즘을 구성할 때 필요한 경우도 있다.

 

1차원 배열이라면 개발자가 선언한 값으로 코드를 읽어 쉽게 확인이 가능하겠지만, 2차원 배열이라면 할당된 배열의 갯수가 a[0]과 a[1]이 서로 다를 수도 있다.

또한 데이터가 방대해지면 이러한 정보를 확인하는 것이 어려울 가능성도 배제할 수 없다. 

 

때문에 배열 변수에서 배열이 가진 갯수를 확인하는 명령어 혹은 지정 방법이 필요한데, 이러한 명령어를 '배열의 길이 확인'이라고 한다.

기본적인 형태는 배열 변수에 .length를 붙이는 것이며, 1차원 배열은 변수에 바로 붙이고, 2차원 배열은 확인하고자하는 행의 배열 변수에 붙여서 배열의 개수를 확인할 수 있다. 

 

int[] a = new int[3];

int[][] b = {{1, 5 , 7},{2},{0 , 9 ,12, 5, 6}}

 

위의 코드에서 a가 가진 배열의 개수는 a[0], a[1], a[2]로 총 3개이다. 

b가 가진 배열의 개수는 우선 3개의 행을 가지고, 각각의 행은 서로 다른 열을 가진다. 

 

 

표로 표현하자면 위와 같이 구성되며,  b[0].length 는 3, b[1].length는 1로 서로 다른 값을 가진다. 

즉 동일한 변수명인 b라도 b의 첫번째 배열에 따라 가지고 있는 상자의 갯수가 서로 다르다는 것이다.

 

때문에 배열마다 상자의 갯수가 다를 수 있어 반드시 '배열의 길이'를 정확히 알아야 한다.

Java를 배운 초기에는 많은 데이터를 다루지 않아 육안으로 빠르게 식별이 가능하지만, 배열 변수의 갯수가 늘어나면 length를 이용하는 것이 더욱 효율적이다. 

 

5. 배열 변수의 복사 

선언된 배열 변수는 데이터가 지정된 영역 이외에 정보를 요청하면 프로그램의 동작을 멈춘다.

예를 들어, 배열 생성한 상자가 4개인데, 5번째 상자를 요청한다면 컴퓨터는 이를 해결하거나 이해할 수 없다.

 

때문에 배열에서 데이터를 선언하거나 확인할 때, 생성된 범위를 정확하게 알아야 한다. 

 

기본적으로 배열의 크기는 선언하면서 고정된다.

때문에 새로운 데이터를 담고 싶어도, 배열의 크기를 늘려서 데이터를 담아야 한다.

새로운 배열을 생성하는 것이기 때문에 기존에 사용하던 변수 데이터가 배열에 담겨있지는 않다. 

만약 기존 데이터를 사용하고 싶다면 새로운 배열 변수에 기존 배열의 데이터를 복사해서 할당 해주어야 한다. 

 

5.1. 기존 heap 메모리에 할당된 데이터를 유지하고 참조 값만 복사 

public class Array_Copy
{   
	public static void main(String[] args)  
	{      
		int[] a = { 1, 2, 3, 4 };     
		int[] b = a; 
	}
}

 

위의 방법을 사용하면 배열 변수 a에 b의 참조값만 할당되고, 변수 b는 a의 참조값을 가지고 heap 메모리에 접근한다.

참조값만 복사하기 때문에 기존의 heap 메모리에서 변동이 생기면 같이 변경된다.

얕은 복사(Shallow Copy)라고도 한다.

 

5.2. 새로운 heap 메모리를 할당하고 참조값 또한 새로 할당하는 법 

 

참조값 복사처럼 동일한 메모리를 공유하지 않고, 다른 메모리에 배열을 복사하는 방법이다.

여러가지 방법이 있으나 기본적으로 원리는 새로운 변수에 기존 변수의 데이터를 heap 영역에 복사하고, 해당 heap 메모리 영역에 대한 참조값을 새로 할당하는 것이다.

 

데이터가 저장된 heap 영역이 별개의 공간이므로 기존 배열 데이터가 변하여도 독립적으로 존재한다. 깊은 복사(Deep Copy)라고도 한다.

 

5.3. 배열을 복사하는 메서드

1) 배열을 복사하는 메서드: Object.clone()

public class Array_Copy
{
	public static void main(String[] args)  
	{
		int[] a = { 1, 2, 3, 4 };
		int[] b = a.clone(); 
	}
}

 

 Array.clone()을 사용하면 배열을 쉽게 복사할 수 있다. 깊은 복사의 가장 보편적인 방법이다.

 

 

2) 배열을 복사하는 메서드: Arrays.copyOf()

import java.util.Arrays;

public class Array_Copy
{
	public static void main(String[] args)  
	{
		int[] a = { 1, 2, 3, 4 };
		int[] b = Arrays.copyOf(a, a.length); 
	}
}

 

 Arrays클래스는 배열을 조작할 수 있는 메소드를 가졌다.

이 클래스 안에 있는 Arrays.copyOf()를 사용하면 배열의 시작점 ~ 원하는 length까지 배열의 깊은 복사를 할 수 있다.

 

 

3) 배열을 복사하는 메서드: Arrays.copyOfRange()

import java.util.Arrays;

public class Array_Copy
{
	public static void main(String[] args)  
	{
		int[] a = { 1, 2, 3, 4 };
		int[] b = Arrays.copyOfRange(a, 1, 3); 
	}
}

 

 Arrays.copyOf()는 배열의 처음~지정한 length까지 복사하는 메서드였다면 Arrays.copyOfRange() 메서드는 복사할 배열의 시작점도 지정할 수 있다.

 

 

4) 배열을 복사하는 메서드: System.arraycopy(src, srcPos, dest, destPos)

public class Array_Copy
{
public static void main(String[] args)  
	{
		int[] a = { 1, 2, 3, 4 };
		int[] b = new int[a.length];
		System.arraycopy(a, 0, b, 0, a.length); 
	}
}

 

src: 복사할 원본 배열

srcPos: 원본 배열의 복사 시작 위치

dest: 목적배열

destPos: 목적 배열의 복사 시작 위치

 

System.arraycopy() 메서드는 지정된 배열을 대상 배열의 지정된 위치에 복사한다. 

 

5.4. 2차원 배열의 깊은 복사

 

1차원 배열의 깊은 복사의 경우 위에서 소개한 메서드를 사용하면 쉽게 복사가 가능하다.

 

하지만 2차원 배열의 경우 위의 메서드를 활용해도 깊은 복사가 되지 않는다.

 

그 이유는 위와 같은 2차원 배열의 구조 a[x][y]에서 배열을 복사하는 메서드를 사용하게 되면 y좌표를 가리키는 주소 값만 있는 a[x] 부분만 깊은 복사가 되고, 값이 있는 a[x][y]는 깊은 복사가 되지 않기 때문이다.

 

그렇기에 2차원 배열을 복사하기 위해서는 for문을 돌리면서 값이 있는 a[x][y]를 일일이 복사해주어야 한다.

 

1) 이중 for문 활용

public class Array_Copy
{
public static void main(String[] args)  
	{
	int a[][] = {{1,2,3},{4,5,6,},{7,8,9}};
	int[][] b = new int[a.length][a[0].length]; 

	for(int i=0; i<a.length; i++) 
		{
		for(int j=0; j<a[i].length; j++) 
			{
				b[i][j] = a[i][j];  
			}
		}
	}
} 

2차원 객체 배열의 복사를 할 경우 arraycopy나 clone을 이용해서 복사할 수가 없다.

렇기에 복사하려면 이중 for문을 돌면서 값을 일일이 옮겨주어야 한다.

 

2) System.arraycopy 활용

public class Array_Copy
{

public static void main(String[] args)  

{
	int a[][] = {{1,2,3},{4,5,6,},{7,8,9}};
	int b[][] = new int[a.length][a[0].length]; 

	for(int i=0; i<b.length; i++)
		{
		System.arraycopy(a[i], 0, b[i], 0, a[0].length); 
		}
	}
}

 

이중 for문이 싫다면 for문을 돌려 System.arraycopy 메서드를 이용해 2차원 배열을 복사할 수 있다.

1차원 배열을 2차원 배열의 row 길이만큼 복사한다.

 

6. 배열의 값을 비교해주는 함수 equals()

equal은 특정 데이터를 비교하는데 사용하는 함수이다.

배열의 데이터를 추출하거나 확인하기 위해 사용할 수 있다.

주어진 값과 비교해 보고 같으면 true 값을 리턴해준다. 

 

boolean java.lang.String.equals(Object anObject){실행문;}

 

String name=”홍길동”;

if(name.equals(“홍길동”))
	{ 
	System.out.println(“이름은 홍길동이 맞습니다.”}
	}
else
	{
	System.out.println(“이름은 홍길동이 아닙니다.)
	}

 

위의 코드에서 확인할 수 있듯이, equals는 다음에 오는 String 값과 변수 name을 비교해주는 역할을 한다.

비교한 데이터의 값이 같다면 이를 true로 리턴한다.