Contents
Java Array
   Oct 3, 2022     19 min read

배열

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

로또 번호와 같이 데이터의 개수가 정해져 있을 경우 기본 자리수 6개에 보너스 번호 1개를 포함하여 총 7개의 자리를 미리 만들어 놓고 사용하면 된다

➡️기본형 자료형의 배열 선언 형식

int [] lottoNumbers;
int[] lottoNumbers;
int lottoNumbers[];

※주의 : 배열 변수를 정의할 때 대괄호 안에는 아무것도 써주면 안된다.

  • 대괄호는 타입과 변수 사이에 위치해도 되고, 변수명 뒤에 위치해도 된다.
  • 보통 첫번째에 있는 것과 같이 타입과 변수명 사이에 대괄호를 넣는 것을 권장한다.

➡️배열의 초기화

int [] lottoNumbers = new int[7];
  • 배열을 선언할 때는 new를 써 준 후 타입 이름을 명시
  • 대괄호 안에 해당 배경의 크기를 지정해 준다.
  • 배열도 참조 자료형의 객체를 생성할 때 처럼 반드시 new를 써야한다. 배열도 참조 자료형이기 때문이다.(참고 꼭 new를 사용하여 정의해야 하는 것은 아니다. 예외도 있다.

➡️배열의 특징

  • 배열의 순서는 0부터 시작한다.
public class ArrayLotto {
    public static void main(String[] args) {
        ArrayLotto array = new ArrayLotto();

    }

    public void init(){
        int [] lottoNumbers = new int[7];

    }
}

lottoNumbers라는 배열

0번 방1번 방2번 방3번 방4번 방5번 방6번 방
       
public class ArrayLotto {
    public static void main(String[] args) {
        ArrayLotto array = new ArrayLotto();
        array.init();
    }

    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;

    }
}

실행 결과 : Error 메시지

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 7

이 메시지의 끝 부분에는 ArrayIndexOutOfBoundsException 이라는 말과 7이라는 숫자가 있다.

ArrayIndexOutOfBoundsException 의 의미는 배열(Array)위치(Index)벗어난(OutOfBountd)

예외(Exception) 가 발생했다. 라는 의미이다.

ArrayIndexOutOfBoundsException 는 배열에 값에 할당할 때 발생, 값을 참조할 때에도 발생한다.


배열의 기본값

  • 기본 자료형 배열의 기본값은 각 자료형의 기본값과 동
public class ArrayInitValue {
    public static void main(String[] args) {
        ArrayInitValue array = new ArrayInitValue();
        array.primitiveTypes();
				System.out.println();
        array.referenceTypes();
    }

    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]);

    }

    public void referenceTypes(){
        String[] strings = new String[2];
        ArrayInitValue[] array = new ArrayInitValue[2];
        System.out.println("string[0] = " + strings[0]);
        System.out.println("array[0] = " + array[0]);

    }

}
결과 :
byteArray[0] = 0
shortArray[0]= 0
intArray[0] = 0
longArray[0] =0
floatArray[0] = 0.0
doubleArray[0] = 0.0
charArray[0]= [ ]
booleanArray[0] = false

string[0] = null
array[0] = null

public void referenceTypesSetValue(){
        String[] strings = new String[2];
        ArrayInitValue[] array = new ArrayInitValue[2];
        strings[0] = "Please visit www.GodOfJava.com";
        array[0] = new ArrayInitValue();
        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]);

    }
결과 :
strings[0] =Please visit www.GodOfJava.com
strings[1] = null
array[0] = main.Chapter07.ArrayInitValue@68837a77
array[1] = null
String[] strings = null;

위와 같이 선언한 다음에 값을 사용할 때에는 반드시 초기화를 하여 사용해야 한다는 점 기억하자 !

**※참고

ArrayInitValue 객체를 출력한 결과는 타입의 이름과 함께 @가 붙은 수수께끼의 답이 나올까 ? 참조 자료형은 public String toString()이라는 메소드를 만들어 줘야만 이러한 암호 같은 내용이 출력되지 않는다. toString()이라는 메소드를 만들어 주지 않으면 “타입이름@고유번호” 순으로 내용이 출력된다.**

배열을 그냥 출력 할 때

배열을 참조조자료형이다.

➡️예제) 배열 그냥 출력할 때

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

    private void printString() {
        System.out.println("string = " + new String[0]);
        System.out.println("array = " + new ArrayPrint[0]);
    }
}
결과 :
string = [Ljava.lang.String;@2f7a2457
array = [Lmain.Chapter07.ArrayPrint;@6108b2d7

의미 파악하기

  • [L : 가장 앞의 “[”는 해당 객체가 배열이라는 의미이고, L은 해당 배열은 참조 자료형이라는 의미
  • java.lang.String: 해당 배열이 어떤 타입의 배열인지를 보여줌
  • @2f7a2457 : 해당 배열의 고유 번호
참조형 자료형(Reference)
L

➡️기본 자료형의 배열을 그냥 출력할 경우

public class ArrayPrint {
    public static void main(String[] args) {
        ArrayPrint array = new ArrayPrint();
        array.printString();

        array.printPrimitiveArray();
    }

    private void printString() {
        System.out.println("string = " + new String[0]);
        System.out.println("array = " + new ArrayPrint[0]);

    }

    private void printPrimitiveArray() {
        System.out.println("byteArray = " + new byte[1]);
        System.out.println("shortArray = " + new short[1]);
        System.out.println("intArray = " + new int[1]);
        System.out.println("longArray = " + new long[1]);
        System.out.println("floatArray = " + new float[1]);
        System.out.println("doubleArray = " + new double[1]);
        System.out.println("charArray =" + new char[1]);
        System.out.println("booleanArray =" + new boolean[1]);

    }
}
결과 :
byteArray = [B@1554909b
shortArray = [S@6cd8737
intArray = [I@13969fbe
longArray = [J@3498ed
floatArray = [F@3d8c7aca
doubleArray = [D@21bcffb5
charArray =[C@668bc3d5
booleanArray =[Z@7a5d012c

➡️ “[” 다음에 알파벳은 해당 타입을 대표하는 문자이다.

  • [B : byte
  • [S : short
  • [I : int
  • [F : float
  • [D : double
  • [C : char
  • [J : long
  • [Z : boolean

boolean은 byte가 먼저 B를 갖고 있어서 Z로 표현하게되었다.

booleanbytechardoublefloatintlongshort
ZBCDFIJS

배열을 선언하는 다른 방법

private void otherInit() {
        int[] lottoNumbers = {5, 12, 23, 25, 38, 41, 2};
        //int[] lottoNumbers2;
        //lottoNumbers2 = {5,12,23,25,38,41,2}; // compile error
}

배열을 선언과 함께 초기화하려면..

  • 중괄호 안에 각 위치에 해당하는 값들을 콤마(,)로 구분하여 나열”하면 된다.
  • 중괄호를 닫은 다음에는 반드시 세미콜론(;)을 써줘야한다.

주석 처리 되어있는 compile error를 발생한다. 왜 그럴까?

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

➡️배열의 데이터를 추가할 때 콤마 사이의 줄 바꿈은 문제가 되지 않는다.

public void otherInit(){
	int[] lottoNumbers = {5,
  12,23,
  25,38,41,2};
}

위와같은 방법으로 사용될 때

모든 배열에 들어가는 값들이 처음부터 정할 수 있는 상황이 되지 않는다.

배열에 들어가는 값이 계속 바뀔 수도 있고, 언제든지 변경이 가능하기 때문에 보통 “절대 변경되지 않는 값” 을 지정할 때 이렇게 중괄호로 선언하여 사용한다.

➡️예시) month(월)로 표시하는 것에 있어서 활용할 때

public class ArrayInitialize {
    public static void main(String[] args) {
        ArrayInitialize array = new ArrayInitialize();
        System.out.println(array.getMonth(3));

    }
    public String getMonth(int monthInt){
        String[] month = {"January", "February", "March", "April", "May", "June",
                "July", "August", "September", "October", "Number", "Novemeber", "December"};
        return month[monthInt + 1];
    }
}

➡️ 보통 month(월) 처럼 변하지 않는 값은 메소드 내에서 선언하여 사용하는 것보다는, 클래스의 변수로 선언하여 재사용성을 높이는 것이 좋다.

public class ArrayInitialize {
	String[] month = {"January", "February", "March", "April", "May", "June",
	"July", "August", "September", "October", "Number", "Novemeber", "December"};

}

➡️ 하지만 getMonth() 라는 메소드에서만 이 month라는 배열을 사용한다면 반드시 메소드 밖으로 빼낼 필요는 없다.

이유 : ArrayInitalize라는 클래스의 객체를 생성할 때마다 month라는 배열이 생성되기 때문이다.

클래스의 인스턴스 변수로 사용할지? 메서드 안에서 선언으로 사용할지?

  • 사용의 빈도
  • 어디서 사용하는지

얼마나 자주 사용하는지, 어디에서 사용하는지를 확인하여 메소드에서 선언하여 사용할지, 클래스의 인스턴스 변수로 선언하여 사용할지 결정하면 된다.

하지만…. 이걸 극복하기 위해서 나온 예약어가 있다

static 예약어

➡️ static 예약어를 사용할 예제

public class ArrayInitialize {
	static String[] month = {"January", "February", "March", "April", "May", "June",
	"July", "August", "September", "October", "Number", "Novemeber", "December"};

}

static을 사용하면 ArrayInitialize 클래스의 객체를 생성할 때마다 month 배열을 새로 생성하지 않아도 된다.

static이 붙어서 “클래스 변수"가 되었다.

→ 배열의 선언 끝


2차원 배열

2차원 배열 생성 방법

➡️ 2차원 배열 선언

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

방법1.

int[] twoDim[];

방법2.

int twoDim[][];
  • 추천하는 방법은 1차원 배열처럼 타입과 배열 변수명 사이에 대괄호들을 넣는 방법이다.

  • 2차원 배열은 “배열의 배열" 을 의미함

※중요

1차원 배열은 int로 선언했다면 twoDim[0]는 int 값이다. 하지만 2차원 배열에서는 twoDim[0]는 int 값이 아니라 배열이다. 즉, twoDim[0][0]의 값이 int 값이다.

Untitled

2차원 배열의 개수 선언

twoDim = new int[2][3];

Untitled 1

그림과같이 2차원 배열이 생성된다. 2층으로 되어있고 한층에 3개의 집이 있는 복도식 아파트 같은 공간이 만들어 진다. 총 안에서 거주 할 수 있는 공간은 6개의 공간이다.

선언이 불가능한 경우

  • 1차원 배열 크기 저장하지 않고 2차원 배열만 지정할 경우 → 불가능.
twoDim = new int[][2]; // <-- 불가능. 컴파일 에러 발생!
  • 1차원 배열 크기 지정 하지않고, 2차원 배열도 지정하지 않을 경우 → 불가능.
twoDim= new int[][2] // <-- 불가능. 컴파일 에러 발생!

선언이 가능한 경우

  • 1차원 크기만 저장하고 2차원 크기를 지정하지 않는 경우 → 가능하다.
twoDim = new int[2][];    // <-- 선언 가능

twoDim[0]와 twoDim[1], 두 개의 배열만 선언해 놓은 것이다.

twoDim[0] 배열의 크기는 어떻게되는 것일까 ? 정해주지 않아도 되는 것일까?

정답은 반드시 정해줘야 한다가 답이다.

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

처음 선언한 2차원 배열(twoDim = new int[2][3])은 1차원 2차원의 크기가 고정되어 있었다.

하지만, 아래와 같이 선언하면 2차원 배열의 공간의 크기가 서로 다르게 지정할 수 있다.

Untitled 2

중괄호를 이용한 2차원 배열선언

  • 중괄호를 이용한 2차원 배열 선언
int[][] twoDim = {{ 1, 2, 3 }, {4,5,6}};
  • 위의 한줄을 기존의 표현으로 했을 때
int[][] 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;

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

배열의 이름에 .length 를 붙여주면 된다

ArrayLength 클래스에 printArrayLength()라는 메소드를 만들어보자

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

    private void printArrayLength() {
        int[] oneDim = new int[3];
        int[][] twoDim = new int[4][2];
        System.out.println(oneDim.length);
        System.out.println(twoDim.length);
    }
}

➡️ 결과

3
4
  • 왜 두번째는 4라는 크기가 나왔을 까?

2차원 배열의 경우 해당 배열의 크기를 알려달라고 하면 1차원 크기를 알려준다.

public void printArray(){
        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 oneLoop = 0; oneLoop < 2; oneLoop++) {
            for (int twoLoop = 0; twoLoop < 3; twoLoop++) {
                System.out.println("twoDim[" + oneLoop + "]"+ "[" + twoLoop + "] = " + twoDim[oneLoop][twoLoop] );

            }
        }
    }

➡️ 결과

twoDim.length = 2
twoDim[0].length =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

for루프에서 oneLoop<2twoLoop < 3 배열의 크기를 알고 있기 때문에 이렇게 썼다. 하지만 배열의 길이를 모를경우는? .length 를 사용하면 길이을 몰라도 배열의 길이만큼 for문이 돈다.

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

위와 같이 하면 배열의 크기가 가변적이라고 할지라도 정확하게 데이트럴 출력해 줄 수 있다. 하지만 이렇게 .length 를 사용하여 for 루프가 수행될 때마다 길이를 얻어오는 것은 성능적 측면에서 좋지 않다.

  • 크기를 알아내는 변수를 할당하여 사용하는 것이 가장 효과적이다.
    • 배열의 값을 for 루프의 조건에 직접 입력하는 하드코딩은 반드시 피해야만 한다.
int twoDimLength = twoDim.length;

for (int oneLoop = 0; oneLoop < twoDimLength; oneLoop++) {
   int twoDimOneLength = twoDim[oneLoop].length;
   for (int twoLoop = 0; twoLoop < twoDimOneLength; twoLoop++) {
     System.out.println("twoDim[" + oneLoop + "]"+ "[" + twoLoop + "] = " + twoDim[oneLoop][twoLoop] );
   }
}

배열을 위한 for 루프 - (for-each)

배열뿐만 아니라 자바에서 제공되는 Collection이라는 자료 구조를 처리할 때 for 루프를 보다 쉽게 사용할 JDK 5부터 개선된 부분이 있다.

  • 개선된 for 루프 - (for-each)
for(타입이름 임시변수명 : 반복대상객체){

}

괄호 안에 콜론 앞에는 배열의 각 항목을 처리하기 위한 “타입이름”과 “임시 변수명”을 지정해주고 콜론(:) 뒤에는 반복을 수행할 대상 객체(여기서는 배열)가 위치한다.

  • for-each의 이해를 돕는 예시
public class ArrayNewFor {
    public static void main(String[] args) {
        ArrayNewFor array = new ArrayNewFor();
        array.newFor();
    }

    private void newFor() {
        int[] oneDim = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        for (int date: oneDim) {
            System.out.println(date);
        }
    }
}

➡️ 앞서 했던 for문

int twoDimLength = twoDim.length;

for (int oneLoop = 0; oneLoop < twoDimLength; oneLoop++) {
   int twoDimOneLength = twoDim[oneLoop].length;
   for (int twoLoop = 0; twoLoop < twoDimOneLength; twoLoop++) {
     System.out.println("twoDim[" + oneLoop + "]"+ "[" + twoLoop + "] = " + twoDim[oneLoop][twoLoop] );
   }
}

➡️ new for문(for-each)

public void twoDimFor(){
   int[][] twoDim = { {1, 2, 3}, {4, 5, 6}};
   for (int[] dimArray : twoDim) { // 첫 번째
      for (int date : dimArray) { // 두 번째
         System.out.println(date);
      }
   }
}
  • 첫 번째 for 루프 안의 소괄호를 보면 twoDim이라는 배열의 1차원 값은 배열이다. 그래서, int dimArray가 아니라, int[] dimArray로 지정했다. (int의 배열을 앞부분에 쓴 것을 꼭 기억해야 한다.)

  • 두 번째 for 루프 안의 소괄호를 보면 dimArray 배열의 1차원 값이 int타입이기 때문에 int date라고 지정함.

위와 같이 for 루프를 사용하면 단점 1차원 배열과 2차원 배열의 위치를 알 수 없다는 것이다.

그 위치를 확인하려면? 해결 → 임시 변수를 두면된다.

public void twoDimForWithCounter(){
    int[][] twoDim = {{1, 2, 3}, {4, 5, 6}};
    int oneCounter = 0;
    for (int[] dimArray : twoDim) {
      int twoCounter = 0;
        for (int date : dimArray) {
          System.out.println(date);
          twoCounter++;
        }
        oneCounter++;
   }
}
  • 정리
    • 값만 처리하기 위한 배열 : for-each 사용하면 편리하다
    • 배열의 위치(index) 정보도 같이 필요한 배열은 그냥 for문 을 사용하는 것이 더 편리

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

public class ArrayMain{
   public static void main(String[] args){

   }
}

위 코드에서 main() 메소드의 매개 변수 args라는 String[] 타입의 배열이다.

흠…. 그러면 이 배열에는 값을 어떻게 전달할까 ?

public class ArrayMain {
   public static void main(String[] args) {
      if (args.length > 0) {
        for (String arg : args) {
          System.out.println(arg);
        }
      }
  }
}
$ javac ArrayMain.java
$ java ArrayMain
$
결과 :
실행하면 아무런 결과가 출력되지 않는다.
  • args매개변수를 넘길 때
$ java ArrayMain a b c d
a
b
c
d
$

위와 같이 클래스 이름 뒤에 공백으로 분리한 문자열을 나열하면 이 문자열들이 args라는 배열에 전달된다. 그래서 애플리 케이션이 시작할 때 전달해야 할 값들이 있다면, 이와 같은 방법을 사용하면 된다.