배열(array)

하나에 많은 것을 담을 수 있는 배열이라는게 있다는데…

배열은 한 가지 타입에 대해서, 하나의 변수에 여러 개의 데이터를 넣을 수 있다.

기본 자료형의 배열은 다음과 같이 선언할 수 있다.

int[] lottoNumbers;
int lottoNumbers[];

선언한 배열은 다음과 같이 초기화를 해 주어야 한다.

int[] lottoNumbers = new int[7];

int는 기본 자료형이지만 int[]와 같이 int를 배열로 만든 lottoNumbers는 참조 자료형이다. 참조 자료형의 객체를 생성할 때는 반드시 new를 사용해야 한다. 그리고 꼭 이와 같이 한 줄로 선언해야 하는 것은 아니고 다음과 같이 선언해도 된다.

int[] lottoNumbers;
lottoNumbers = new int[7];

이렇게 배열을 선언해 두면 lottoNumbers라는 배열은 7개의 값을 지정할 수 있는 자리를 가진다. 그리고 그 자리의 값은 다음과 같이 선언할 수 있다.

lottoNumbers[1] = 15;

배열의 위치는 0부터 시작한다. 배열의 크기가 7이기 때문에, 배열의 위치는 0부터 6번까지 있다. 여기서 배열의 위치를 나타내는 숫자를 index라고 한다. 매회의 로또 번호를 저장하는 예제를 통해서 배열과 친해져 보자. 먼저 Array라는 클래스를 만들고 main() 메소드를 만들자. 주의해야 할 것은 Array라는 클래스는 자바 reflect 패키지의 클래스도 이미 있다.

public class Array {
    public static void main(String[] args) {
        Array array = new Array();
    }

    public void init() {
        int[] lottoNumbers = new int[7];
        lottoNumbers[0] = 5;
        lottoNumbers[1] = 12;
        lottoNumbers[2] = 23;
        lottoNumbers[3] = 25;
        lottoNumbers[4] = 38;
        lottoNumbers[5] = 41;
        lottoNumbers[6] = 2;
        lottoNumbers[7] = 9;
    }
}

main() 메소드에 init()을 추가해서 컴파일을 하면 문제 없이 컴파일이 되지만 실행하면 예외 메시지가 출력이 된다. lottoNumbers[7] 위치는 없는 것이기 때문에 ArrayIndexOutOfBoundsException가 발생이 된다. 모든 기본 자료형과 참조 자료형은 배열로 만들어서 사용할 수 있다.

배열의 기본값

** 기본 자료형 **

기본 자료형 배열의 기본값은 각 자료형의 기본값과 동일하다. 지역 변수의 경우에는 초기화를 하지 않으면 사용이 불가능하지만 배열에서는 지역 변수라고 할지라도, 배열의 크기만 정해주면 에러가 발생하지 않는다.

public class Array {
  // 중간 생략
    public void primitiveTypes() {
        byte[] byteArray = new byte[1];
        short[] shortArray = new short[1];
        int[] intArray = new int[1];
        long[] longArray = new long[1];
        float[] floatArray = new float[1];
        double[] doubleArray = new double[1];
        char[] charArray = new char[1];
        boolean[] booleanArray = new boolean[1];
        System.out.println("byteArray[0]=" + byteArray[0]);
        System.out.println("shortArray[0]=" + shortArray[0]);
        System.out.println("intArray[0]=" + intArray[0]);
        System.out.println("longArray[0]=" + longArray[0]);
        System.out.println("floatArray[0]=" + floatArray[0]);
        System.out.println("doubleArray[0]=" + doubleArray[0]);
        System.out.println("charArray[0]=[" + charArray[0] + "]");
        System.out.println("booleanArray[0]=" + booleanArray[0]);

        System.out.println("byteArray=" + byteArray);
        System.out.println("shortArray=" + shortArray);
        System.out.println("intArray=" + intArray);
        System.out.println("longArray=" + longArray);
        System.out.println("floatArray=" + floatArray);
        System.out.println("doubleArray=" + doubleArray);
        System.out.println("charArray=" + charArray);
        System.out.println("booleanArray=" + booleanArray);
    }
}

참고로 char 배열의 기본값은 '\u0000'이며, 화면에 출력될 때에는 한 칸의 공백으로 보인다.

** 참조 자료형 **

그렇다면 참조 자료형은 초기화를 하지 않으면 어떻게 될까? 다음의 예제를 보자.

public class Array {
  // 중간 생략
    public void referenceTypes() {
        String[] strings = new String[2];
        Array[] array = new Array[2];

        System.out.println("strings[0]=" + strings[0]);
        System.out.println("array[0]=" + array[0]);
    }
}

모든 참조 자료형은 초기화를 하지 않으면 null이다. 다음과 같이 수정하자.

public class Array {
  // 중간 생략
    public void referenceTypes() {
        String[] strings = new String[2];
        Array[] array = new Array[2];

        strings[0]="Please visit www.GodOfJava.com.";
        array[0]=new Array();
        System.out.println("strings[0]="+strings[0]);
        System.out.println("strings[1]="+strings[1]);
        System.out.println("array[0]="+array[0]);
        System.out.println("array[1]="+array[1]);
    }
}

String의 경우 굳이 new String();과 같이 생성자를 사용하지 않고 쌍따옴표 만으로 정의가 가능하다. 참조 자료형 배열의 각각의 값은 반드시 각각의 값을 초기화해 줘야만 null이 되지 않는다. Array[0] 객체를 출력하면 Array@해시값이 출력이 된다. 해시값은 객체를 구별하는 고유한 값이다. 원하는 형식으로 출력하려면 toString() 메소드를 overriding 해야 한다.

배열을 그냥 출력해보면 어떻게 나올까?

referenceTypes() 메소드에 다음을 추가해 보자.

System.out.println("strings = "+ strings);
System.out.println("array = "+ array);

실행을 해보면 다음과 같이 출력 될 것이다.

strings = [Ljava.lang.String;@15db9742
array = [LArray;@6d06d69c
  • [L: 가장 앞의 “[“는 해당 객체가 배열이라는 의미이며 L은 해당 배열은 참조 자료형이라는 의미다.

  • java.lang.String;: 해당 배열의 타입을 보여준다.

  • @15db9742: 해당 배열의 해시값이다.

표시

타입

[B

byte

[S

short

[I

int

[J

long

[F

float

[D

double

[C

char

[Z

boolean

배열을 선언하는 또 다른 방법

지금까지 new int[1];과 같이 new라는 예약어를 사용하고, 타입과 크기를 지정해서 배열을 선언했다. 그런데 중괄호를 사용하면 보다 한번에 배열을 선언할 수 있다.

public class Array {
    public void otherInit() {
        int[] lottoNumbers = { 5, 12, 23, 25, 38, 41, 2 };
        String[] month = { "January", "February", "March", "April", "May",
                "June", "July", "August", "September", "October", "November",
                "December" };
    }
}

배열을 선언과 함께 초기화하려면 이처럼 중괄호 안에 각 위치에 해당하는 값들을 콤마로 구분하여 나열하면된다. 중괄호를 닫은 다음에는 반드시 세미콜론을 적어 주어야 한다. 그런데 중괄호를 이용하여 배열을 선언할 때는 다음과 같이 선언하면 안된다.

public class Array {
  // 중간 생략
  public void otherInit() {
    int[] lottoNumbers;
    lottoNumbers = {5, 12, 23, 25, 38, 41, 2}; //error
  }
}

중괄호를 사용하여 초기화를 할 때는 반드시 한번에 변수 선언 및 초기화가 이루어져야만 한다.

public class Array {
    static String[] month = { "January", "February", "March", "April", "May",
            "June", "July", "August", "September", "October", "November",
            "December" };
  // 이하 생략
}

이렇게 하면 Array 클래스의 객체를 생성할 때마다 month배열을 새로 생성하지 않는다. 클래스에 변수를 선언할 때 static이라고 선언하면 클래스 변수가 되기 때문이다.

별로 사용하지는 않지만, 알고 있어야 하는 2차원 배열

2차원 배열 선언은 다음과 같이 할 수 있다.

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

배열 twoDim은 행이 2개 열이 3개인 int 형이다. twoDim[0], twoDim[1]은 3개의 int 형으로 이루어진 배열이다. 2차원 배열에서 twoDim[0]int 값이 아니라 배열이다. twoDim[0][0]의 값이 int 값이다.

public class Array {
    public void twoDimensionArray() {
        int[][] twoDim;
        twoDim = new int[2][3];

        twoDim[0][0] = 1;
        twoDim[0][1] = 2;
        twoDim[0][2] = 3;

        twoDim[1][0] = 4;
        twoDim[1][1] = 5;
        twoDim[1][2] = 6;
    }
}

2차원 배열은 다음과 같이 선언할 수도 있다.

towDim = new int[2][];

1차원 배열의 크기만 지정하고, 2차원 배열의 크기를 지정하지 않을 수도 있지만 1차원 배열은 빈공간으로 놔두고, 2차원 배열만 지정하거나, 두 배열의 공간의 크기를 모두 지정 안하면 안된다. 즉 다음과 같이 하면 안된다.

twoDim = new int[][]; // x
twoDim = new int[][3]; // x

1차원 배열을 선언한 다음에 반드시 2차원 배열을 다음과 같이 선언해 주어야 한다.

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

이와 같이 2차원 배열의 공간의 크기를 서로 다르게 지정할 수 있다. 다음과 같이 초기화와 동시에 배열을 지정할 수 있다.

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

배열을 출력하기 위해서는 배열의 크기를 확인하는 방법이 필요하다.

배열의 길이는 어떻게 알 수 있을까요?

배열의 길이를 알아내는 방법은 배열이름에 .length를 붙여 주면된다.

public class Array {
  // 중간 생략
    static String[] month = { "January", "February", "March", "April", "May",
            "June", "July", "August", "September", "October", "November",
            "December" };

    public void printArrayLength() {
        int monthLength = month.length;
        System.out.println("month.length=" + monthLength);

        int[][] twoDim = { { 1, 2, 3 }, { 4, 5, 6 } };
        System.out.println("twoDim.length=" + twoDim.length);
        System.out.println("twoDim[0].length=" + twoDim[0].length);

        for (int loop1 = 0; loop1 < 2; loop1++) {
            for (int loop2 = 0; loop2 < 3; loop2++) {
                System.out.println("twoDim[" + loop1 + "][" + loop2 + "]="
                        + twoDim[loop1][loop2]);
            }
        }

        for (int loop1 = 0; loop1 < twoDim.length; loop1++) {
            for (int loop2 = 0; loop2 < twoDim[loop1].length; loop2++) {
                System.out.println("twoDim[" + loop1 + "][" + loop2 + "]="
                        + twoDim[loop1][loop2]);
            }
        }

        int twoDimLength = twoDim.length;
        for (int loop1 = 0; loop1 < twoDimLength; loop1++) {
            int twoDim1Length = twoDim[loop1].length;
            for (int loop2 = 0; loop2 < twoDim1Length; loop2++) {
                System.out.println("twoDim[" + loop1 + "][" + loop2 + "]="
                        + twoDim[loop1][loop2]);
            }
        }

        for (int[] tempArray : twoDim) {
            for (int temp : tempArray) {
                System.out.println(temp);
            }
        }

        int count1 = 0;
        for (int[] tempArray : twoDim) {
            int count2 = 0;
            for (int temp : tempArray) {
                System.out.println("twoDim[" + count1 + "][" + count2 + "]="
                        + temp);
                count2++;
            }
            count1++;
        }

    }
}

twoDim.length의 결과는 2이다. 왜냐하면 1차원 배열의 크기가 2이기 때문이다. 그리고 twoDim[0].length의 결과는 3이다. 왜냐면 2차원 배열의 크기가 3이기 때문이다.

for 루프를 이용하여 twoDim 배열의 값을 출력하도록 하자.

public class Array {
  // 중간 생략
    static String[] month = { "January", "February", "March", "April", "May",
            "June", "July", "August", "September", "October", "November",
            "December" };

    public void printArrayLength() {
        int monthLength = month.length;
        System.out.println("month.length=" + monthLength);

        int[][] twoDim = {{1, 2, 3},  4, 5, 6}};
        System.out.println("twoDim.length=" + twoDim.length);
        System.out.println("twoDim[0].length=" + twoDim[0].length);

        for (int loop1 = 0; loop1 < 2; loop1++) {
            for (int loop2 = 0; loop2 < 3; loop2++) {
                System.out.println("twoDim[" + loop1 + "][" + loop2 + "]="
                        + twoDim[loop1][loop2]);
            }
        }
    }
}

배열의 내용을 별도의 메소드를 이용하여 출력한다면 배열의 크기를 확인하는 .length를 사용하면 좋다.

for (int loop1 = 0; loop1 < twoDim.length; loop1++) {
    for (int loop2 = 0; loop2 < twoDim[loop1].length; loop2++) {
        System.out.println("twoDim[" + loop1 + "][" + loop2 + "]="
                + twoDim[loop1][loop2]);
    }
}

이렇게 하면 배열의 크기가 가변적이라고 할지라도 정확하게 데이터를 출력할 수 있다. 하지만 이렇게 for 루프가 수행될 때마다 길이를 얻어오는 것은 성능적 측면에서 좋지 않다. 따라서 다음과 같이 별도의 크기를 알아내는 변수를 할당하여 사용하는 것이 효과적이다.

int twoDimLength = twoDim.length;
for (int loop1 = 0; loop1 < twoDimLength; loop1++) {
    int twoDim1Length = twoDim[loop1].length;
    for (int loop2 = 0; loop2 < twoDim1Length; loop2++) {
        System.out.println("twoDim[" + loop1 + "][" + loop2 + "]="
                + twoDim[loop1][loop2]);
    }
}

배열을 위한 for 루프

자바에서 제공되는 Collection이라는 자료구조를 처리할 때 for 루프를 쉽게 사용할 수 있도록 하고 있다. for 루프를 편하게 사용할 수 있도록 다음과 같은 문법이 추가 되었다.

for (타입이름 임시변수 : 반복대상객체) {

}

반복대상 객체가 Collection의 객체가 된다. month라는 배열을 이용하여 for 루프에 적용하면 다음과 같다.

for (String tempMonth : month) {
  System.out.println(tempMonth);
}

String은 month의 성분들이 String 타입이라는 것이고 tempMonth는 month의 각 성분을 임시로 저장하는 변수이다. 그러면 2차원 배열은 어떻게 사용할 수 있는지 알아 보자.

for(int[] tempArray : twoDim) {
  for(int temp: tempArray) {
    System.out.println(temp);
  }
}

바깥 루프는 twoDim의 일차원 배열에 해당되고 안쪽 루프는 2차원 배열에 해당된다. 따라서 int[], int로 타입이 지정된 것을 알 수 있다.

자바를 실행할 때 원하는 값들을 넘겨주자

main() 메소드를 만들 때 다음과 같이 선언했다.

public class Array {
  //중간생략
  public static void main(String[] args) {
    //내용생략
  }
}

main() 메소드의 매개 변수인 args라는 String 타입의 배열이 있다.

public class Array {
  //중간생략
  public static void main(String[] args) {
  //중간 생략
    if (args.length>0) {
        for (String arg : args) {
            System.out.println(arg);
        }
    }
  }
}

컴파일 후 다음과 같이 해보자.

javac Array.java
java Array a b c d

클래스 이름 뒤에 문자들이 args라는 배열에 저장되어 출력이 되는 것을 알 수 있다.

직접해 봅시다

한 학년에는 5개의 반이 있다. 각 반 학생들의 키를 하나의 배열에 저장하여 관리하는 프로그램을 작성하자.

  1. 키를 관리하는 ManageHeight라는 클래스를 만들고 main() 메소드도 만들자.

  2. 각 반별로 학생의 수는 다음과 같다. 이 데이터를 클래스의 인스턴스 변수로 int 타입의 gradeHeights라는 2차원 배열을 선언하자. 이 데이터는 public void setDAte() 메소드에서 지정하자. | 반 | 학생수 | 1번부터의 키 | | — | — | — | | 1 | 5 | 170, 180, 173, 175, 177| | 2 | 4 | 160, 165, 167, 186| | 3 | 4 | 158, 177, 187, 176| | 4 | 3 | 173, 182, 181| | 5 | 5 | 170, 180, 165, 177, 172|

  3. 반 번호를 매개변수로 넘겨주면 해당 반 학생들의 키를 번호 순대로 출력하는 public void printHeight(int classNo)라는 메소드를 만들자.

  4. main() 메소드에서 setData() 메소드를 호출하여 데이터를 지정하고, printHeight() 메소드를 for 루프를 사용하여 1반 ~ 5반까지 호출하자.

  5. 각 반별 키의 평균을 계산하여 출력하는 public void printAverage(int classNo) 메소드를 만들지ㅏ. 매개변수인 classNo는 반번호를 의미한다. 단 여기서, 평균을 구하기 위한 합을 저장하는 변수는 double로 지정한다.

  6. main() 메소드에서 printHeight()를 호출하는 부분만 주석 처리하고, printAeverage() 메소드를 while 루프를 사용하여 1반 ~ 5반까지 호출하자.