5. 배열

1. 배열(Array)

1.1 배열(Array)이란?

배열

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

  • 많은 양의 데이터를 손쉽게 다룰 수 있다.
  • 데이터의 수가 아무리 많아도 배열의 길이만 바꾸면 된다.

중요: 서로 다른 타입의 변수들로 구성된 배열은 만들 수 없다.

  • 서로 다른 타입의 값들을 하나로 묶으려면 '클래스'를 정의하면 되는데, 클래스는 다음장에서

특징: 배열은 각 배열요소의 저장공간이 연속적으로 배치되어 있다.

1.2 배열의 선언과 생성

배열의 선언

원하는 타입의 변수를 선언하고 변수 또는 타입에 배열임을 의미하는 대괄호 []를 붙인다.

선언방법 예제
타입[] 변수이름; int[] scores;
타입 변수이름[]; int scores[];

참고: 저자는 타입에 대괄호를 붙이는 것을 선호 (대괄호가 타입의 일부라고 보기때문)

배열의 생성

배열을 선언하는 것은 단지 생성된 배열을 다루기 위한 참조변수를 위한 공간이 만들어질 뿐이고, 배열을 생성해야만 비로소 값을 저장할 수 있는 공간이 만들어진다.

연산자 new와 함께 배열의 타입과 길이 지정
타입[] 변수이름; // 배열 선언
변수이름 = new 타입[길이]; // 배열 생성
 // int 형 배열 참조변수 score를 선언 (아직 데이터를 저장할 공간은 마련되지 않음)
 int[] score;
 // 연산자 new에 의해 메모리 빈 공간에 5개의 int형 데이터를 저장할 수 있는 공간 마련 
 // 배열의 각 요소는 자동으로 int형의 기본값인 0으로 초기화 됨
 // 대입연산자 =에 의해 배열의 주소가 int형 배열 참조변수 score에 저장됨
 score = new int[5]; // 길이가 5인 int 배열, 배열 score
 // 이제 참조변수 score를 통해서 배열에 값을 저장하거나 읽어 올 수 있다.
배열의 선언과 생성을 동시에
타입[] 변수이름 = new 타입[길이]; // 배열 선언과 생성을 동시에
int[] score = new int[5];

1.3 배열의 길이와 인덱스

배열의 요소(element)

생성된 배열의 각 저장공간

배열이름[인덱스] // 배열 요소 접근방식

배열의 인덱스(index)

배열의 요소마다 붙여진 일련번호.

  • 0 부터 시작
  • 범위: 0 ~ 배열길이-1
  • 인덱스는 상수, 변수, 수식 모두 가능

주의: index의 범위를 벗어난 값을 사용하면 오류 발생 (IndexOutOfBoundsException)

  • 전적으로 프로그래머 책임
int[] score = new int[5] // 길이가 5인 int 배열. index 범위 = 0~4

score[3] = 100; // index로 상수를 쓸수 있음. 배열 score의 4번째 요소에 100을 저장
int value = score[3]; // 배열 요소 접근. 배열 score의 4번째 요소에 저장된 값을 읽어서 value에 저장

for(int i=0; i<5 ; i++) {
  score[i] = i*10; // index로 변수도 쓸 수 있음
}

int tmp = score[2+1]; // index로 수식도 쓸 수 있음 (수식이 먼저 계산되어 index를 결정)

score[5] = 100; // index의 범위를 벗어남 -> IndexOutOfBoundsException

배열의 길이

배열의 요소 개수, 즉 값을 저장할 수 있는 공간의 개수

  • (당연히) 양의 정수(0포함)이어야 하며, 최대값은 int 타입의 최대값 (약 20억)
  • 길이가 0인 배열도 생성 가능
배열이름.length
int[] arr = new int[5]; // 길이가 5인 int 배열
int tmp = arr.length; // tmp = 5
arr.length = 10 // 에러 (배열의 길이는 변경 불가)

: for문 조건식에 배열의 길이를 직접 적어주는 것보다 '배열이름.length'를 사용하는 것이 안전하다.
코딩하다가 중간에 배열 길이 변경시 일일이 찾아서 고쳐주지 않아도 되기 때문이다.

배열 길이 변경 방법

  1. 더 큰 배열을 새로 생성
  2. 기존 배열의 내용을 새로운 배열에 복사

: 이 작업은 비용이 많이 들므로 처음부터 넉넉하게 잡아 주는 것이 좋다.
너무 크게 잡으면 메모리 낭비이므로 기존의 2배정도 길이로 생성하는 것이 좋다.

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;
}
생성시 초기화
  • 괄호 {} 안에 저장할 값들의 개수에 의해 배열의 길이가 자동적으로 결정된다.
// 선언과 동시에 생성 및 초기화
int[] score1 = new int[]{ 50, 60, 70, 80, 90 }; // OK
int[] score2 = { 50, 60, 70, 80, 90 }; // OK: new int[] 생략 가능 

// 선언 후 생성 및 초기화
int[] score3;
score3 = new int[]{ 50, 60, 70, 80, 90 }; // OK
score3 = { 50, 60, 70, 80, 90 }; // ERROR: new int[] 생략 불가

// 메서드의 매개변수로 넘길 때 
int add(int[] arr) { /* 생략 */}
int result = add(new int[]{ 50, 60, 70, 80, 90 }); // OK
int result2 = add({ 50, 60, 70, 80, 90 }); // ERROR: new int[] 생략 불가

// 길이가 0인 배열
int[] score4 = new int[0];
int[] score5 = new int[]{};
int[] score6 = {}; // OK: new int[] 생략 가능

배열의 출력

for문
int[] iArr = { 100, 95, 80, 70, 60 };
for(int i=0; i<iArr.length; i++) {
  System.out.print(iArr[i]+","); // 요소 구별을 위해 쉼표를 넣음
}
System.out.println(); // 줄바꿈
100,95,80,70,60
Array.toString(배열이름)
import java.util.*;
int[] iArr = { 100, 95, 80, 70, 60 };
System.out.println(Arrays.toString(iArr)); // 배열요소 출력

System.out.println(iArr); // int 배열 참조변수 출력 

char[] chArr = { 'a', 'b', 'c', 'd' }; 
System.out.println(chArr); // char 배열 참조변수 출력
[100, 95, 80, 70, 60] // '[첫번째요소, 두번째요소, ... ]'
[I@14318bb // '타입@주소'
abcd // println 메소드가 char 배열일때는 요소가 구분자없이 그대로 출력되도록 작성됨

1.5 배열의 복사

for문

배열의 요소 하나하나에 접근하여 복사 (비효율적)

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이 새로운 배열로 교체

System.arraycopy()

지정된 범위의 값들을 한 번에 통째로 복사 (for문보다 효율적)

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

System.arraycopy(arr, 0, tmp, 0, arr.length); // arr[0]에서 tmp[0]로 arr.length 개의 데이터 복사

arr = tmp; // 참조변수 arr이 새로운 배열로 교체

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

1.6 배열의 활용

  • 총합과 평균: 배열의 모든 요소를 더해서 총합과 평균을 구한다.
  • 최대값과 최소값: 배열의 요소 중에서 제일 큰 값과 제일 작은 값을 찾는다.
  • 섞기(shuffle): 배열의 요소의 순서를 반복해서 바꾼다. (카드섞기, 로또번호생성)
  • 임의의 값으로 배열 채우기: 연속 또는 불연속적인 값들로 배열을 초기화 한다.
  • 정렬하기(sort): 오름차순, 내림차순으로 배열을 정렬한다.
  • 빈도수 구하기: 배열에 어떤 값이 몇 개 저장되어 있는지 세어서 보여준다.

2. String 배열

2.1 String 배열의 선언과 생성

  • 3개의 문자열(String)을 담을 수 있는 배열을 생성해보자.
    String[] name = new String[3];
    // name    -> 0x100
    // name[0] -> null
    // name[1] -> null
    // name[2] -> null
    
  • 참조형 변수의 초기값은 null 이다.

2.2 String 배열의 초기화

  • 배열의 각 요소에 문자열을 지정하자.
    String[] name = new String[3];
    name[0] = "Kim";
    name[1] = "Park";
    name[2] = "Yi";
    
  • 괄호{} 를 사용하여 간단히 초기화하자.(생성과 초기화를 동시에)
    String[] name = new String[]{"Kim", "Park", "Yi"};
    
  • 참조형 배열(객체 배열)의 메모리에 대한 이해
    String[] name = new String[]{"Kim", "Park", "Yi"};
    // name    -> 0x100
    // name[0] -> 0x200 -> "Kim"
    // name[1] -> 0x300 -> "Park"
    // name[2] -> 0x400 -> "Yi"
    
  • 예제 5-12

    class ArrayEx12 {
      public static void main(String[] args) {
          String[] names = new String[]{"Kim", "Park", "Yi"};
    
          for (int i = 0; i < names.length; i++) {
              System.out.println("names[" + i + "]:" + names[i]);
          }
    
          String tmp = names[2]; // 배열의 names 의 세번째 요소를 tmp 에 저장
          System.out.println("tmp:" + tmp);
    
          names[0] = "Yu";
    
          for (String str : names) // 향상된 for 문
              System.out.println(str);
    
          // result
          // names[0]:Kim
          // names[0]:Park
          // names[0]:Yi
          // tmp:Yi
          // Yu
          // Park
          // Yi
      }
    }
    
  • 예제 5-13

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

2.3 char 배열과 String 클래스

  • String 클래스는 char 배열에 기능을 추가한 것이다.

    • 데이터 : char 배열
    • 기능 : length (문자열의 길이), substring (일부 문자열), equals (문자열 내용 비교)
  • 부록 : Char[] vs String

    class CharArrVsString {
      public static void main(String[] args) {
          String str1 = "java.study";
          String str2 = "java.study";
    
          char[] charArray1 = new char[]{'j','a','v','a','.','s','t','u','d','y'};
          char[] charArray2 = new char[]{'j','a','v','a','.','s','t','u','d','y'};
    
          System.out.println("str1       : " + str1);
          System.out.println("charArray1 : " + charArray1);
          System.out.println("str1       : " + Integer.toHexString(str1.hashCode()));
          System.out.println("str2       : " + Integer.toHexString(str2.hashCode()));
    
          System.out.println(str1);           // System.out.println(String)
          System.out.println(charArray1);     // System.out.println(char[])
    
          System.out.println(str1 == str2);
          System.out.println(charArray1 == charArray2);
          System.out.println(charArray2 == charArray2);
    
          System.out.println(str1.equals(charArray1));
          System.out.println(str1.equals(str2));
          System.out.println(str1.equals(new String("java.study")));
    
          // result
          // str1       : java.study
          // charArray1 : [C@677327b6
          // str1       : 966802fd
          // str2       : 966802fd
          // java.study
          // java.study
          // true
          // false
          // true
          // false
          // true
          // true
      }
    }
    
  • 예제 5-14

    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[])을 출력
    
          // result
          // src.charAt(0):A
          // src.charAt(1):B
          // src.charAt(2):C
          // src.charAt(3):D
          // src.charAt(4):E
          // ABCDE
      }
    }
    
  • 예제 5-15

    class ArrayEx15 {
      public static void main(String[] args) {
          String source = "SOSHELP";
          String[] morse = {".-", "-...", "-.-", "-..", "."
              , "..-.", "--.", "....", "..", ".---"
              , "-.-", ".-..", "--", "-.", "---"
              , ".--.", "--.-", ".-.", "...", "-"
              , "..-", "...-", ".--", "-..-", "-.--"
              , "--.." };
    
          String result = "";
    
          for (int i = 0; i < source.length(); i++) {
              result += morse[source.charAt(i) - 'A'];
          }
    
          System.out.println("source:" + source);
          System.out.println("morse:" + result);
    
          // source:SOSHELP
          // morse:...---.........-...--.
      }
    }
    

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

  • 예제 5-16

    class ArrayEx16 {
      public static void main(String[] args) {
          System.out.println("매개변수의 개수:" + args.length);
    
          for (int i = 0; i < args.length; i++) {
              System.out.println("args[" + i + "] = \"" + args[i] + "\"");
          }
    
          // result
          // java ArrayEx16 abc 123 "Hello World"
          // 매개변수의 개수:3
          // args[0] = "abc"
          // args[1] = "123"
          // args[2] = "Hello World"
      }
    }
    
  • 예제 5-17

    class ArrayEx17 {
      public static void main(String[] args) {
          if (args.length != 3) {
              System.out.println("usage: java Array17 NUM1 OP NUM2");
              System.exit(0);
          }
    
          int num1 = Integer.parseInt(args[0]);
          char op = args[1].charAt(0);
          int num2 = Integer.parseInt(args[2]);
    
          int result = 0;
    
          switch(op) {
              case '+':
                  result = num1 + num2;
                  break;
              case '-':
                  result = num1 - num2;
                  break;
              case 'x':
                  result = num1 * num2;
                  break;
              case '/':
                  result = num1 / num2;
                  break;
              default:
                  System.out.println("지원하지 않는 연산입니다");
          }
    
          System.out.println("결과:" + result);
    
          // result
          // java ArrayEx17
          // usage: java Array17 NUM1 OP NUM2
          // java ArrayEx17 10 + 3
          // 결과:13
          // java ArrayEx17 10 x 3
          // 결과:30
      }
    }
    

3. 다차원 배열

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

  • 2차원 배열 선언하기
    int[][] score;
    int score[][];
    int[] score[];
    
  • 4행 3열의 2차원 배열 생성하기
    int[][] score = new int[4][3];
    

3.2 2차원 배열의 초기화

  • 생성과 초기화를 함께 하기

    int[][] arr = {
      {1, 2, 3},
      {4, 5, 6}
    };
    // arr       -> 0x100
    // arr[0]    -> 0x200
    // arr[0][0] -> 1
    // arr[0][1] -> 2
    // arr[0][2] -> 3
    // arr[1]    -> 0x300
    // arr[1][0] -> 4
    // arr[1][1] -> 5
    // arr[1][2] -> 6
    
  • 예제 5-18

    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) {
              for (int i : tmp) {
                  sum += i;
              }
          }
    
          System.out.println("sum=" + sum);
    
          // result
          // score[0][0]=100
          // score[0][1]=100
          // score[0][2]=100
          // score[1][0]=20
          // score[1][1]=20
          // score[1][2]=20
          // score[2][0]=30
          // score[2][1]=30
          // score[2][2]=30
          // score[3][0]=40
          // score[3][1]=40
          // score[3][2]=40
          // sum=570
      }
    }
    
  • 예제 5-19

    class ArrayEx19 {
      public static void main(String[] args) {
          int[][] score = {
              {100, 100, 100},
              {20, 20, 20},
              {30, 30, 30},
              {40, 40, 40},
              {50, 50, 50}
          };
          int korTotal = 0;
          int engTotal = 0;
          int mathTotal = 0;
    
          System.out.println("번호 국어 영어 수학 총점 평균");
          System.out.println("=============================");
    
          for (int i = 0; i < score.length; i++) {
              int sum = 0;        // 개인별 총점
              float avg = 0.0f;   // 개인별 평균
    
              korTotal += score[i][0];
              engTotal += score[i][1];
              mathTotal += score[i][2];
    
              System.out.printf("%3d", i + 1);
    
              for (int j = 0; j < score[i].length; j++) {
                  sum += score[i][j];
                  System.out.printf("%5d", score[i][j]);
              }
    
              avg = sum / (float) score[i].length;
    
              System.out.printf("%5d %5.1f\n", sum, avg);
          }
    
          System.out.println("=============================");
          System.out.printf("총점:%3d %4d %4d\n", korTotal, engTotal, mathTotal);
    
          // result
          // 번호 국어 영어 수학 총점 평균
          // =============================
          // 1  100  100  100  300 100.0
          // 2   20   20   20   60  20.0
          // 3   30   30   30   90  30.0
          // 4   40   40   40  120  40.0
          // 5   50   50   50  150  50.0
          // =============================
          // 총점:240  240  240
      }
    }
    

3.3 가변 배열

  • 2차원 배열
int[][] score = new int[5][3];
for (int i = 0; i < score.length; i++)
    for (int j = 0; j < score[i].length; j++)
        score[i][j] = ...;
  • 2차원 배열 -> 배열의 배열
int[][] score = new int[5][];

//  5 x 3
score[0] = new int[3];
score[1] = new int[3];
score[2] = new int[3];
score[3] = new int[3];
score[4] = new int[3];

// 5 x n
score[0] = new int[4];
score[1] = new int[3];
score[2] = new int[2];
score[3] = new int[2];
score[4] = new int[3];

// 생성과 초기화를 동시에
score = {
    {100, 100, 100, 100},
    {20, 20, 20},
    {30, 30},
    {40, 40},
    {50, 50, 50}
};

3.4 다차원 배열의 활용

  • 좌표에 X표하기
  • 빙고
  • 행렬의 곱셈
  • 단어 맞추기