15 min read

<Java> 배열2

<Java> 배열2
  • 이번 포스팅에서는 String 배열과 다차원배열에 대해 다루겠습니다.

String 배열

  • 배열의 타입이 String인 경우에도 이전시간에 배운 배열과 마찬가지로 선언과 생성방법은 다르지 않습니다. 예를 들어 3개의 문자열을 담을 수 있는 배열의 경우 아래와 같이 선언 가능합니다.
String[] test = new String[3];
  • 다만, 기존의 Primitive Type의 배열을 요소의 초기화 없이 선언했을 때와 다르게 null값을 String 요소들은 가집니다.
  • 참고로 타입에 따른 변수의 기본값을 아래에 있느니 참고하시길 바랍니다.
  • 마지막으로 기존의 배열과 String배열의 차이에 대해 알려드리겠습니다.저희가 Reference Type을 처음 배울때, 이러한 타입은 변수 값이 특정한 상수가 아니라 주솟값을 가진다고 배웠습니다.(이러한 변수를 참조변수라 합니다.)즉, String 배열의 요소들은 특정한 상수 값을 가지는 것이 아니라 메모리 내의 특정 주솟값을 가지고 있습니다.즉, 특정한 문자열에 접근을 하려면 두번의 참조를 거쳐서 문자열에 접근할 수 있다는 것입니다.방금 드린 이 설명이 이해가 가지 않는다면 다시한번 Reference Type에 대한 개념을 다잡으시길 바랍니다.
  • 참고로 참조형 변수와 참조 변수는 동일한 단어이며, 모든 참조변수에는 객체가 메모리에 저장된 주소인 4 byteㅇ의 정수 값 또는 null값이 저장됩니다.
  • 이제 예제를 통해 String 배열의 선언과 초기화에 대해 알아봅시다.
public class ArrayEx12 {
    public static void main(String[] args) {
        String[] names = {"Hong", "Brido", "Daebak"};

        for (int i = 0; i < names.length; i++) {
            System.out.println("names[i] = " + names[i]);
        }
        String name = names[2];
        System.out.println("name = " + name);
        names[0] = "Song";
        for (String str : names) {
            System.out.println("str = " + str);
        }
    }
}
  • 앞서 배운 배열과 차이점은 내부의 실제 데이터에 접근할때 참조를 하냐 안하냐의 차이일뿐이지 실제로 String배열을 사용하는 법은 동일합니다.
  • 다음 예제는 String 배열을 활용해서 16진법의 수를 2진법으로 변환하는 예제입니다.16진법의 경우(0에서 9까지의 숫자 10개 + A부터 F까지 문자 6개)로 숫자를 표현하는 방식입니다.
public class ArrayEx13 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.print("16 진수를 입력하세요 : ");
        String string = sc.next().toUpperCase();
        char[] hex = string.toCharArray();
        String res = "";

        if (hex[0] == '0' && hex[1] == 'X') {
            res = hexToBinary(2, hex);
        }else{
            res = hexToBinary(0, hex);
        }
        System.out.println("16진수 : " + new String(hex));
        System.out.println("Converting to Binary result = " + res);
    }

    static String hexToBinary(int startIndex,char[] hex) {
        /*
         * 0 to 16까지 Binary value
         * */
        String[] binary = {
                "0000", "0001", "0010", "0011",
                "0100", "0101", "0110", "0111",
                "1000", "1001", "1010", "1011",
                "1100", "1101", "1110", "1111",
        };

        String res = "";

        for (int i = startIndex; i < hex.length; i++) {
            if (hex[i] >= '0' && hex[i] <= '9') {
                res += binary[hex[i] - '0'];
            } else {
                res += binary[hex[i] - 'A' + 10];
            }
        }
        return res;
    }
}
  • 조금은 생소할 수 있는 구조로 코드를 변경했습니다.입력을 통해 16진수를 받고 해당 수를 2진법으로 변경시켜 출력하는 간단한 기능을 수행합니다.
  • 아직 배우지 않은 메서드를 일부러 정의하여 사용해본 예제입니다.당장은 예제가 완벽히 이해가 가지 않더라도 꼭 따라해보면서 구조를 눈에 익히길 바랍니다.

String 클래스와 char 배열의 관계

  • 지금까지 우리는 여러개의 문자, 즉 문자열을 저장할 때는 String타입의 변수를 사용했습니다.C언어를 배우신분이면 아시겠지만 사실 문자를 연이어 늘어놓은것이 문자열이고 이러한 문자열은 문자배열인 char배열과 같은 뜻입니다.
  • 그러면 자바에서는 char배열이 아닌 String클래스로 왜 문자열을 다룰까요?간단하게 말하면 좀 더 편하게 문자열을 사용하기 위해서입니다.즉, String클래스는 char배열에 기능(메서드)을 추가한 것입니다.
  • 언급한 C언어에서는 문자열을 char배열로 다루지만 객체지향언어인 자바는 이러한 문자열을 클래스에 메서드와 함께 묶어서 클래스 단위로 제공합니다.
  • 또한 앞서나온 메서드에 관한 설명도 덧붙이겠습니다. 클래스 내에서 하나의 동작을 동작하기위한 기능을 메서드(함수)라고 할 수 있습니다.바로 위의 예제에서 hexToBinary처럼 말이죠.이렇게 따로 메서드를 정의하면 동일한 기능을 수행하려고 할 때, 두번 정의하지않고 기존에 있는 메서드를 호출하면 원하는 기능을 수행할 수 있기 때문에 편리합니다.
  • 그러면 char배열과 String 클래스의 차이점은 무엇일까요? char배열의 경우 배열 요소에 접근하여 요소를 변경할 수 있습니다. 즉 문자열에 대한 수정이 가능하다는 것입니다.하지만 String 클래스의 경우 요소 하나하나에 접근 할수 없기에 변경 불가합니다.이러한 특성을 String이 Immutable하다고 합니다.
  • 이제 예제를 통해 char배열과 String 클래스간의 변환에 대해 알아보겠습니다.
public class ArrayEx14 {
    public static void main(String[] args) {
        String src = "ABCDE";

        for (int i = 0; i < src.length(); i++) {
            char c = src.charAt(i);
            System.out.println("c = " + c);
        }
        char[] chars = src.toCharArray();
        System.out.println(chars);
        System.out.println("chars = " + chars);//주솟값의 hascode의 16진수 출력됨
		String src1 = new String(chars);
    }
}
  • toCharArray()라는 메서드를 통해 String을 손쉽게 char배열로 변환할수 있습니다.또한 이렇게 생성된 배열은 String src1 = new String (chars);와 같이 손쉽게 String 클래스로 변경 할 수 있습니다.

Command Line Interface(CLI)로 입력받기

  • 이번에 알아볼 주제는 Scanner를 통해 사용자로부터 값을 받는 방법말고 다른 간단한 방법을 알아 볼 것입니다.
  • 우선 시작하기 전에 아래 그림과 같은 새까만 창을 보신분들이 있을것입니다.
  • 이러한 창은 사용자가 OS와 직접적으로 소통하여 명령을 내릴 수 있는 창입니다.이를 CLI라 얘기하는 동시에 맥 OS에서는 터미널, 윈도우에서는 cmd 또는 최근엔 powershell이라고도 합니다.
  • 우리가 이번에 입력을 하려는 방법은 이러한 창을 통해 진행 할 것입니다.
  • 우선 예제를 입력하고 나서 실습을 진행합시다.
public 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]);
        }
    }
}
  • 먼저 저희가 만든 자바 파일이 있는 곳으로 이동해줘야합니다.그러려면 먼저 우리가 만들 파일의 경로를 먼저 찾아낸후, 해당 경로로 이동하면 됩니다.경로 찾기를 모르시는 분은 본인의 운영체제 + 절대경로 찾기라고 검색하셔서 찾으시면 됩니다.
  • 이제 ArrayEx16를 실행시킴과 동시에 값을 입력해봅시다.
  • java ArrayEx16.java hong chang sub이라고 명령어를 입력합니다.
  • 결과가 보이시나요? 설명을 하자면, 저희가 밥 먹듯이 써왔던 public static void main(String[] args)의 args라는 String 타입의 배열에 대한 비밀이 풀린것입니다.컴파일과 함께 값을 넣어주게 되면 입력된 값이 args라는 String타입의 배열의 요소로 초기화되게 됩니다.위와 같이 입력한 순서대로 args의 0,1,2,..순으로 초기화됩니다.
  • 물론 이런 방식으로 입력받은 값들은 main메서드 내에서만 접근 가능합니다.(main메서드의 중괄호 밖에서는 접근 할 수 없습니다)
  • 이번에는 제가 사용하는 IDE에서 args 배열에 값을 넣어보겠습니다.(저는 InteliJ입니다. 아마 이클립스도 비슷할것입니다…)
  • Run이라는 munu에서 Edit Configuration을 클릭하면 아래와 같이 창이 나옵니다.이러한 창에서 Program Arguments라 적힌 부분에 다음과 같이 본인이 입력하고 싶은 값들을 띄어쓰기로 구분해서 넣어줍니다.이후 IDE에서 해당 파일을 실행시켜보면 CLI창과 동일한 결과를 얻을 수 있을 것입니다.
  • 우리가 이렇게 함수에 입력해주는 값들을 용어적으로 매개변수(Argument)라고 하니 알아 둡시다.
  • CLI로 매개변수를 입력받는 마지막 예제는 사칙연산 계산기 문제입니다.깃헙 참고하여 실습해보시길 바랍니다.

다차원 배열

  • 지금껏 우리는 1차원적으로 한줄로 밖에 나열하지 못하는 배열만 배웠습니다.이제부터는 배열로 평면을 그리듯이 2차원 배열을 배울 것입니다. 이러한 배열을 다차원 배열이라하며 3차원 이상의 배열을 만드는 것도 가능해질 것입니다.
  • int[][] score = new int[4][3];와 같은 코드가 있습니다. 해당 코드는 4행 3열짜리 2차원 배열을 선언하는 코드입니다.사실 행과 열에 대한 개념이 어색하신 분들은 한번에 해당 배열의 구조를 상상하는것은 힘들 것입니다.표로써 나타내어 보면 아래와 같습니다.행이 직사각형의 높이, 열이 직사각형의 너비라고 생각하면 조금 외우기 쉽습니다..
  • 실제로 위와 같은 코드로 2차원 배열을 선언하게 되면, 모든 요소에 0이 저장됩니다.일전에 언급된 배열요소타입의 기본값이 자동으로 저장되는것 입니다.
  • 요소들의 index값의 경우 위의 표처럼 (열의 길이-1) 또는 (행의 길이-1)로 설정되며 해당 인덱스로 요소에 접근 가능합니다.

2차원 배열의 초기화

  • 만약 위와 같은 테이블 형태의 시험 성적 데이터를 이차원 배열로 초기화 하려면 어떻게 해야 될까요? 생각보다 직관적인 코드로 이차원 배열을 초기화 시킬 수 있습니다.다음과 같이 중괄호를 이용하여 각각의 열마다 배열안에 배열을 정의해 초기화시켜줍니다.
	int[][] score = {
					{100,100,100},
					{20,20,20},
					{30,30,30},
					{40,40,40},
					{50,50,50},
					};
  • 그러면 위와 같은 코드가 실행된 후, 2차원 배열 score가 메모리에 어떤 형태로 만들어지는지 알아봅시다.아래의 그림과 같습니다.3개의 요소를 가지는 일차원 배열이 총 5개가 세로로 나열되면서 2차원배열을 구성합니다.즉, 배열의 배열로 구성됩니다.
  • 그러면 score.length의 값은 얼마일까요?배열 참조변수 score가 참조하고 있는 배열의 길이를 알아보면 됩니다.아래의 그림을 보시면 아시겠지만 값은 5입니다.그리고 score[0].length의 길이가 3입니다.다시 한번 설명드리면 요소를 담고 있는 배열의 배열이 하나가 있고, 해당 배열의 요소로 들어가는 배열이 있는 구조입니다.방금 설명과 아래 그림을 번갈아보면 여러번 보시면 이해 할 수 있으실 것입니다.
  • 그러면 이제 2차원 배열을 초기화하는 예제를 만나봅시다.
public class ArrayEx18 {
    public static void main(String[] args) {
        int[][] score = {
                {100, 100, 100},
                {20, 20, 20},
                {30, 30, 30},
                {40, 40, 40},
                {50, 50, 50},
        };
        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) {
            System.out.println("tmp = " + tmp);
            for (int element : tmp) {
                System.out.println("element = " + element);
                sum += element;
            }
        }
        System.out.println("sum = " + sum);
    }
}
  • 여기서 눈여겨 봐야할 부분은 ForEach문에서 tmp라는 int타입의 배열을 출력하는 부분입니다.tmp라는 참조변수 자체를 출력할 경우, 메모리 주소를 해쉬코드를 변경한 값의 16진수가 출력됩니다.즉, 해당 주소를 따라들어가면 실제 우리가 초기화한 배열의 요소들이 저장되어있음을 이해하면 완벽합니다.
  • 아래의 예제는 18번예제를 조금 더 예제답게 만든 쉬운 문제이니 따라해 보시길 바랍니다.
public 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, engTotal = 0, mathTotal = 0;

        System.out.println("번호\t국어\t영어\t수학\t총점\t평균");
        System.out.println("==============================");

        for (int i = 0; i < score.length; i++) {
            int sum = 0;
            double avg = 0.0;

            korTotal += score[i][0];
            engTotal += score[i][0];
            mathTotal += score[i][0];
            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 / (double) 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);
    }
}

가변 배열

  • 마지막으로 알아볼 주제는 가변 배열입니다.사실 바로 앞에서 배운 2차원 배열과 동일한 구조인데 단 한가지만 다를 뿐입니다.우선 가변 배열을 생성하고 초기화하는 코드를 먼저 보겠습니다.
	int[][] score = {
					{100,100,100,100},
					{20,20,20},
					{30,30},
					{40,40},
					{50,50,50},
					};
  • 코드만 봐도 감이 오시지 않나요? 바로 배열의 배열들의 길이가 각각 다른 경우입니다.혹시나 말이 이해가 안가시면 아래의 그림을 바로 보시길 바랍니다.score라는 참조변수가 가르키는 배열이 가르키는 배열들의 크기가 다른것입니다.
  • 만약 가변 배열이 이해가 가지 않는다면, 2차원 배열을 우선적으로 이해하고 오시면 쉽게 이해 가능합니다.
  • 이상으로 배열에 대한 포스팅을 마무리 하겠습니다.더 많은 예제의 경우 깃헙을 참조해 주시길 바랍니다.