본문 바로가기
Java

자바 배열

by 승환파크 2023. 6. 22.

배열은 같은 타입의 데이터를 연속된 공간에 나열하고, 각 데이터에 인덱스를 부여하는 자료구조이다.

예를 들어 학생 30명의 성적 평균을 구한다는 상황이 발생했을 때 배열을 사용하지 않는다면 학생의 성적을 하나하나 변수로 선언한 후 변수들을 하나하나 다 더해서 변수의 갯수만큼 나눠야 하는 비효율적인 코드를 작성해야 한다. 하지만 배열을 이용해서 사용 한다면 for 문을 이용해서 쉽게 계산을 할 수 있다.

 

배열은 다음과 같은 특징을 가진다.

  1. 배열은 같은 타입의 데이터만 저장할 수 있다.
    • int 배열은 int 값만 저장 가능하고, String 배열은 문자열만 저장한다. 또한 선언과 동시에 값을 저장할 수 있는 타입이 결전된다. 만약 다른 타입의 값을 저장하려고 하면 타입 불일치 컴파일 에러가 발생한다.
  2.  한 번 생성한 배열은 길이를 늘리거나 줄일 수 없다.
    • 3개의 값을 저장하는 배열을 생성했다고 가정해보자. 프로그램 실행 도중 5개의 값을 저장하는 배열로 수정할 수 없고, 반대로 2개의 값만 저장하는 배열로 수정할 수도 없다. 만약 5개의 값을 저장해야 하는 경우가 발생한다면 길이가 5인 새로운 배열을 생성하고, 기존 배열 항목을 새 배열로 복사해야 한다.

 

배열 선언

배열을 선언하기 위해서는 우선 배열 변수를 선언해야 한다. 배열 변수 선언은 다음과같이 두 가지 형식으로 선언할 수 있다.

// 형식 1
타입[] 변수;

// 형식 2
타입 변수[];

대괄호[] 는 배열 변수를 선언하는 기호로 사용되는데, 타입 뒤에 붙을 수도 있고 변수 뒤에 붙을 수도 있다. 타입은 배열에 저장될 데이터의 타입을 말한다.

 

배열 변수는 참조 변수에 속한다. 배열도 객체이므로 힙 영역에 생성되고 배열 변수는 힙 영역의 배열 객체를 참조하게 된다. 만일 참조할 배열 객체가 없다면 배열 변수는 null 값으로 초기화 될 수 있다.

타입[] 변수 = null;

만약 배열 변수가 null 값을 가진 상태에서 변수[인덱스]로 값을 읽거나 저장하게 되면 NullPointerException이 발생한다.

배열을 생성하고 배열 변수가 참조하는 상태에서 값을 저장하거나 읽어야 한다.

 

배열의 각 인덱스는 각 항목의 데이터를 읽거나 저장하는 데 사용되며 아래와 같이 배열 이름 옆에 대괄호에 기입된다. 인덱스는 0부터 시작한다.

 

 

배열 생성

배열 객체를 생성하려면 갓ㅂ 목록을 이용하거나 new 연산자를 이용하는 방법이 있다.

 

값 목록으로 배열 생성

값의 목록이 있다면 다음과 같이 간단하게 배열 객체를 생성할 수 있다.

타입[] 변수 = {값1, 값2, 값3, 값4, ...};

중괄호 {} 는 주어진 값들을 항목으로 가지는 배열 객체를 힙에 생성하고, 배열 객체의 번지를 리턴한다. 배열 변수는 리턴된 번지를 저장함으로써 참조가 이루어진다. 예를 들어 "홍길동", "이순신", "세종대왕" 문자열을 갖는 배열은 아래와 같이 생성할 수 있다.

String[] names = {"홍길동", "이순신", "세종대왕"};

이렇게 생성된 배열에서 "홍길동"은 names[0], "이순신"은 names[1], "세종대왕"은 names[2]로 읽을 수 있다.

names[1]의 "이순신"을 "이성계"로 바꾸고 싶다면 아래와 같이 대입 연산자를 사용하면 된다.

names[1] = "이성계";

 

값의 목록으로 배열 객체를 생성할 때 주의할 점이 있는데, 배열 변수를 이미 선언한 후에는 다른 실행문에서 중괄호를 사용한 배열 생성이 허용되지 않는다.

타입[] 변수;
변수 = {값1, 값2, 값3, ...}; <-- 컴파일 에러

배열 변수를 미리 선언한 후 값 목록들이 나중에 결정되는 상황이라면 다음과 같이 new 연산자를사용해서 값 목록을 지정해주면 된다. new 연산자 바로 뒤에는 배열 변수 선언에서 사용한 "타입[]"를 붙여주고 중괄호 {} 에는 값들을 나열해준다.

타입[] 변수;
변수 = new 타입[] {값1, 값2, 값3, ...};

예를 들어 names를 아래와 같이 생성할 수 있다.

String[] names = null;
names = new String[] {"홍길동", "이순신", "세종대왕"};

 

메소드의 매개값이 배열일 경우에도 마찬가지이다. 아래와 같이 매개 변수로 int[] 배열이 선언된 add() 메소드가 있을 경우, 값 목록으로 배열을 생성함과 동시에 add()메소드의 매개값으로 사용하고자 할 때는 반드시 new 연산자를 사용해야 한다.

int add(int[] scores){
	...
}

int result = add({95, 85, 90}); <-- 컴파일 에러
int result = add(new int[]{95, 85, 90});

 

new 연산자로 배열 생성

값의 목록을 가지고 있지 않지만, 향후값들을 저장할 배열을 미리 만들고싶다면 new 연산자로 아래와 같이 배열객체를 생성할 수 있다.

타입[] 변수 = new 타입[길이];

길이는 배열이 저장할 수 있는 값의 개수를 말한다. 이미 배열 변수가 선언된 경우에도 new 연산자로 배열을 생성할 수 있다.

타입[] 변수 = null;
변수 = new 타입[길이];

 

new 연산자로 배열을 처음 생성할 경우 배열은 자동적으로 기본값으로 초기화 된다. 만약 학생 30 명의 점수를 저장할 배열을 아래와 같이 생성하낟고 가정을 해본다면 scores 배열은 int 배열이므로 다음과 같이 scores[0] ~ scores[29] 까지 모두 기본값은 0으로 초기화 된다.

int[] scores = new int[30];

scores[0] -> 0
scores[1] -> 0
scores[2] -> 0
	...
scores[28] -> 0
scores[29] -> 0

 

만약 String 배열을 생성했다면 모두 초기값은 null 로 초기화 된다.

 

타입별로 배열의 초기값을 보여주는 표이다.

분류 타입 초기값
기본 타입(정수) byte[]
char[]
short[]
int[]
long[]
o
'\u0000'
0
0
0L
기본 타입(실수) float[]
double[]
0.0F
0.0
기본 타입(논리) boolean[] false
참조 타입 클래스[] null
인터페이스[] null

 

배열이 생성디고 나서 특정 인덱스 위치에 새로운 값을 저장하려면 아래와 같이 대입 연산자를 이용하면 된다.

변수[인덱스] = 값;

 

예를 들어 배열 scores의 0, 1, 2 인덱스에 각각 95, 85, 90을 저장하는 코드는 아래와 같다.

int[] scores = new int[3];
scores[0] = 95;
scores[1] = 85;
scores[2] = 90;

 

 

배열 길이

배열의 길이란 배열에 저장할 수 있는 전체 항목의 개수를 말한다. 코드에서 배열의 길이를 얻으려면 아래와 같이 배열 객체의 length 필드를 읽으면 된다. 참고로 필드는 객체 내부의 데이터를 말한다. 배열의 length 필드를 읽기 위해서는 배열 변수에 도트(.) 연산자를 붙이고 length를 적어주면 된다.

배열 변수.length;

 

아래는 배열 intArray의 길이를 알아보는 코드이다. 배열 intArray가 3개의 값을 가지고 있기 때문에 변수 num에는 3이 저장된다.

int[] intArray = {10, 20, 30};
int num = intArray.length;

length 필드는 읽기 전용 필드이기 때문에 값을 바꿀 수가 없다.

배열의 length 필드는 for문을 사용해서 배열 전체를 루핑할 때 매우 유용하게 사용할 수 있다.

 

 

다차원 배열

지금까지 본 배열은 값 목록으로 구성된 1차원 배열이다. 이와 달리 값들이 행과 열로서 구성된 배열을 2차원 배열이라고 한다.

2차원 배열은 수학의 행렬을 생각하면 되는데, 가로 인덱스와 세로 인덱스를 사용한다. 자바는 2차원 배열을 중첩 배열 방식으로 구현한다. 예를 들어 2(행)x3(열) 행렬을 만들기 위해 아래와 같은 코드를 사용한다.

2 x 3 행렬의 구조

열↓ 행→ 0 1 2
0 (0, 0) (0, 1) (0, 2)
1 (1, 0) (1, 1) (1, 2)

 

int[][] scores = new int[2][3];

위 코드는 메모리에 3개의 배열 객체를 생성한다.

1. int 타입 배열 A -> 길이는 2인 배열을 생성한다.

2. int 타입 배열 B -> 길이가 3인 배열을 생성한다.

3. int 타입 배열 C -> 길이가 3인 배열을 생성한다.

 

배열 변수인 scores는 길이가 2인 배열 A를  참조한다. 배열 A의 scores[0]은 다시 길이가 3인 배열 B 를 참조한다. 그리고 scores[1]은 길이가 3인 배열 C 를 참조한다. scores[0]과 scores[1]은 모두 배열을 참조하는 변수 역할을 한다. 따라서 아래와 같이 각 배열의 길이를 얻을 수 있다.

scores.length; <-- 2(배열 A의 길이)
scores[0].length; <-- 3(배열 B의 길이)
scores[1].length; <-- 3(배열 C의 길이)

배열의 생성 원리는 수학 행렬과 근본적으로 다르지만 사용 방식은 행렬과 동일하다. scores[0][1]은 배열 B의 인덱스 1  값을 뜻하는데, 수학 행렬에서는 (0, 1) 값이라고 볼 수 있다. 마찬가지로 scores[1][0]은 배열 C의 인덱스 0 값을 뜻하며 이는 수학 행렬의 (1, 0) 값이다. 이처럼 자바는 1차원 배열이 서로 연결된 구조로 다차원 배열을 구현하기 때문에 수학 행렬 구조가 아닌 계단식 구조를 가질 수 있다.

 

아래의 코드를 보면 계단식 구조를 가진 배열을 확인할 수 있다.

int[][] scores = new int[2][];
scores[0] = new int[2];
scores[1] = new int[3];

위 코드는 메모리에 다음과 같이 배열 객체를 생성한다.

1. int 타입 배열 A : 길이가 2인 배열을 생성한다.

2. int 타입 배열 B : 길이가 2인 배열을 생성한다.

3. int 타입 배열 C : 길이가 3인 배열을 생성한다.

 

이 경우 배열 항목의 수를 조사해보면 다음과 같다.

scores.length; <-- 2(배열 A의 길이)
scores[0].length; <-- 2(배열 B의 길이)
scores[1].length; <-- 3(배열 C의 길이)

 

이러한 형태의 배열에서 주의할 점은 배열의 정확한 길이를 알고 인덱스를 사용해야 한다는 것이다.

socres[0][2]는 ArrayIndexOutOfBoundsException을 발생시킨다. 이유는 배열 B 객체의 마지막 인덱스는 1이기 때문이다. 만약 그룹화된 값 목록을 가지고 있다면 아래와 같이 중괄호 안에 다시 중괄호를 사용해서 값 목록을 나열한다.

타입[][] 변수 = {{값1, 값2, ...}, {값4, 값5, ...}, ...};

 

 

객체를 참조하는 배열

기본타입(byte, short, char, int, long, float, double, boolean)배열은 각 항목에 직접 값을 갖고 있지만, 참조타입(클래스, 인터페이스) 배열은 각 항목에 객체의 번지를 가지고 있다. 예를 들어 String은 클래스이므로 String[] 배열은 각 항목에 문자열이 아니라, String 객체의 번지를 가지고 있다. 즉 String[] 배열은 String 객체를 참조하게 된다.

 

 

배열 복사

배열은 한 번 생성하면 크기를 변경할 수 없기 때문에 더 많은 저장 공간이 필요하다면 더 큰 배열을 새로 만들고 이전 배열로부터 항목 값들을 복사해야 한다. 배열 간의 항목 값들을 복사하려면 for 문을 사용하거나 System.arraycopy() 메소드를 사용한다. for문으로 배열을 복사하는 코드는 아래와 같다.

public static void main(String[] args) {
    int[] oldIntArray = {1, 2, 3};
    int[] newIntArray = new int[5];

    for(int i = 0; i < oldIntArray.length; i++){
        newIntArray[i] = oldIntArray[i];
    }

    for(int i = 0 ; i < newIntArray.length; i++){
        if(i == newIntArray.length - 1){
            System.out.println(newIntArray[i]);
        }else {
            System.out.print(newIntArray[i] + ", ");
        }
    }

}

위 코드는 배열을 복사시키는 코드인데 복사되지 않은 항목은 int[] 배열의 기본 초기값인 0이 그대로 유지된다.

 

 

향상된 for문(forEach문)

자바는 배열이나 컬렉션을 좀 더 쉽게 처리하기 위해 향상된 for문을 제공한다. 향상된 for문은 반복 실행을 하기 위해 루프 카운터 변수와 증감식을 사용하지 않는다. for문의 괄호 ()에는 배열에서 꺼낸 항목을 저장할 변수 선언과 콜론(:) 그리고 배열을 작성한다. 배열 및 컬렉션 항목의 개수만큼 반복하고, 자동적으로 for문을 빠져나간다.

 

아래는 향상된 for 문의 작성 방식과 실행흐름이다

for(타입 변수 : 배열 ){

	실행문;

}

1. for문이 처음 실행될 때 배열에서 가져올 첫 번째 값이 존재하는지 평가한다.

2. 가져올 값이 존재하면 해당 값을 변수에 저장한다.

3. 실행문을 실행한다.

4. 블록 내부의 실행문이 모두 실행되면 다시 루프를 돌아 배열에서 가져올 다음값이 존재하는지를 평가한다.

5. 만약 다음 항목이 존재한다면 2 -> 3 -> 1 번 순서로 루프를 다시 진행하고, 가져올 다음값이 없다면 for문이 종료된다.

 

따라서 for문의 반복 횟수는 배열의 항목수가 된다.

'Java' 카테고리의 다른 글

자바 클래스  (0) 2023.06.22
자바 열거 타입  (0) 2023.06.22
자바 참조 타입  (1) 2023.06.21
자바 반복문  (0) 2023.06.20
자바 조건문  (0) 2023.06.20