2차원 배열

TIL·2022년 12월 12일
0

Java (최은빈)

목록 보기
15/27

배열의 초기화 (new 연산자, 힙메모리)

  • 정수: 0
  • 실수: 0.0f, 0.0
  • 논리: false
  • 문자: \0, \u0000 (0문자)
  • 문자열: null


2차원 배열의 선언

  • int[][] scores = new int[5][3];
  • int[][] scores2 = {{}};
  • scores3 = new int[][] {{}};
public class Arrays2dExample {
    // 이중 for문 : 행 = 외부 for문, 열 = 내부 for문
    // 사실 이차원 배열이라는 개념은 물리적으로 존재X -> 행을 세로로 펴서 넣음 (개발자 편하라고 만든 개념)
    // int[][] arr = new int[5][3]; 와 int[] arr = new int[15]; 는 하드웨어 적으로 같다.
    // = row-major order <-> column-major order

    public static void main(String[] args) {

        //// 이차원 배열 선언
        // 1. 배열의 크기만 아는 경우 (나중에 데이터 초기화)
//        int[][] scores = new int[5][3];
//        scores[0][0] = 10;
//        scores[0][1] = 20;
//        scores[0][2] = 30;

        final int ROW = 5; // 학생 // 가독성을 위해 상수 처리 (정해놓은 값)
        final int COL = 3; // 과목

        int[][] socres = new int[ROW][COL]; // 0 으로 초기화 (모든 행의 열의 길이는 동일)
        for (int i = 0; i < ROW; i++) {
            for (int j = 0; j < COL; j++) {
                // 가로 3번
                System.out.print(socres[i][j] + " ");
            }
            System.out.println();
        }

        // 2. 데이터 아는 경우 (한줄 초기화)
        int[][] scores2 = {{10, 20, 30}, {40, 50, 60}, {70, 80, 90}, {10, 20, 30}, {40, 50, 60}};

        // 3. 데이터 아는 경우 (두줄로 작성 가능)
        int[][] scores3;
        scores3 = new int[][] {{10, 20, 30}, {40, 50, 60}, {70, 80, 90}, {10, 20, 30}, {40, 50, 60}};


        //// 이차원 배열의 원소 입력
        // 그림은 이중이지만 실제는 하나의 일차원 배열, 각 행의 첫번째 주소만 알고 있는 것
        // 각 행의 주소가 저장되므로 각 행은 연속되지 않아도 되고, 각 행 내부의 열은 연속되어 주소값의 유추가 가능하다.
        // scores.length = 5 // scores 는 각 행에 대한 첫번째 열의 주소를 가짐
        // sores[i].length = 3 // 각 행에 접근하면 열에 접근 가능
        Scanner scanner = new Scanner(System.in);
        String[] subjects = {"국어", "수학", "영어"};
        for (int i = 0; i < ROW; i++) { // ROW == scores.length() // 학생(5)
            // 세로 5번
            System.out.printf("%d번 학생의 성적을 입력하시오\n", i+1);
            for (int j = 0; j < COL ; j++) { // COL == scores[i].length() // 과목(3)
                // 가로 3번
                System.out.printf("%s 성적: ", subjects[j]);
                socres[i][j] = scanner.nextInt();
            }
        }
        System.out.println();


        //// 이차원 배열의 원소 출력
        for (int i = 0; i < ROW; i++) {
            // 세로 5번
            System.out.printf("%d번 학생 성적입니다\n", i+1);
            for (int j = 0; j < COL ; j++) {
                // 가로 3번
                System.out.printf("%s 성적: %d\n", subjects[j], socres[i][j]);
            }
        }
        System.out.println();


        //// 각 학생들의 과목 평균
        double[] averages = new double[ROW]; // 일차원 배열

        for (int i = 0; i < ROW; i++) {
            // 세로 5번
            int sum = 0;
            for (int j = 0; j < COL ; j++) {
                // 가로 3번
                sum += socres[i][j];
            }
            // 세로 5번
            averages[i] = sum / (double) COL;
        }

        for (int i = 0; i < averages.length; i++) {
            // 세로 5번 (averages.length)
            System.out.printf("%d번 학생의 평균: %f\n", i+1, averages[i]);
        }

        scanner.close();
    }
}



2차원 배열의 원소가 참조타입인 경우

  • 각 행의 각 열에서 데이터값 대신 String Pool의 주소를 저장 (이중)
  • 구조를 이해하는 것은 나중에 최적화 할때 중요



row-major order vs. column-major order

  • row-major order : 메모리 기본
  • column-major order 가 속도 더 빠르다.
    디비 쿼리는 행단위보다 열단위로 데이터를 가져오는 경우가 많고, 이 경우 데이터가 연속(147)되므로 가져오는 속도 더 빠르다 (SAP 에서 사용하는 HANA DB)



1차원 <-> 2차원

public class Array2dToArray1dExample {
    public static void main(String[] args) {
        final int ROW = 3;
        final int COL = 3;

        //// 2d -> 1d
        int[][] arr2d = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
        int[] arr1d = new int[ROW * COL]; // {1, 2, 3, 4, 5, 6, 7, 8, 9}

        // int k = 0;
        for (int i = 0; i < ROW; i++) {
            // 세로 3번
            for (int j = 0; j < COL; j++) {
                // 가로 3번
                // arr1d[k] = arr2d[i][j];
                // k++;
                arr1d[i * COL + j] = arr2d[i][j]; // i=0,1,2 / j=0,1,2 / COL=3
                // 행 * 열개수 + 열
            }
        }
        System.out.println(Arrays.deepToString(arr2d));
        System.out.println(Arrays.toString(arr1d));
        System.out.println();

        //// 1d -> 2d
        // 총 개수와 열의 길이만 알면됨 (C 에서는 열의 길이만 정의해도 배열 선언 가능) // int[][3] = {{},{},{}}
        int[] arr1d2 = {1, 2, 3, 4, 5, 6, 7, 8, 9};
        int[][] arr2d2 = new int[ROW][COL];

        for (int i = 0; i < ROW; i++) {
            // 세로 3번
            for (int j = 0; j < COL; j++) {
                // 가로 3번
                arr2d2[i][j] = arr1d2[i * COL + j]; // 행 * 열개수 + 열
            }
        }
        System.out.println(Arrays.deepToString(arr2d2));
        System.out.println(Arrays.toString(arr1d2));
    }
}



2차원 배열의 Equals and Copy

  • DeepCopy : 데이터 복사 (원본 수정 X) - 이중 for문
  • Shallow Copy : 주소 복사 (원본 수정 O) - for문

public class Array2EqualsCopyExample {
    public static void main(String[] args) {
        int[][] arr1 = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
        int[][] arr2 = {{1, 3, 3}, {4, 5, 6}, {7, 8, 9}};

        // Arrays.deepToString() : 원소 출력 (printf + for 대신)
        System.out.println(Arrays.deepToString(arr1)); // 이중 for문 // 일차원 배열은 Arrays.toString()
        System.out.println(Arrays.deepToString(arr2) + "\n");

        // deepCopy(), isDeepEquals() : 사용자 정의 함수
        System.out.println("arr1 = " + arr1);
        int[][] copy = deepCopy(arr1);
        System.out.println("copy = " + copy); // 주소는 다름
        System.out.println("isDeepEquals(arr1, copy) => " + isDeepEquals(arr1, copy));
        copy[0][0] = 10;
        System.out.println(arr1); // 원본은 수정 안됨

        // Arrays.deepEquals() : 2차원 배열의 원소가 같은지 체크 
        // 이차원 배열 복사는 자바에서 제공되는 클래스 없음. 만들어야함
        System.out.println("Arrays.deepEquals(arr1, arr2) => " + Arrays.deepEquals(arr1, copy) + "\n");
        System.out.println("===============================================\n");

        // copy()
//        int[] copy2 = copy(arr1);
        // 이중 for문이 아닌 일중 for문 이므로, 실제 데이터 값이 저장된 주소만 가져옴
        // 이중인데 일중으로 복사하거나, 주소를 전달함으로서 원본 수정 가능하도록 복사 하는 것 = shallow cpoy

        // shaloow copy : 주소 복사 (원본 수정 O)
        // 행의 첫 인덱스 주소만 알면 열에 접근 가능하므로 일중 for문
        int[][] shallowCopyk = new int[3][3];
        for (int i = 0; i < shallowCopyk.length; i++) { // 이중 for문 아닌 일중 for문
            // 세로 3번
            shallowCopyk[i] = arr1[i];
        }
        shallowCopyk[0][0] = 10;
        System.out.println(Arrays.deepToString(arr1)); // 원본도 수정되버림!
    }

    // 두 이차원 배열이 내용물이 같은지 확인하는 함수
    public static boolean isDeepEquals(int[][] arr1, int[][] arr2) {
        if (arr1.length != arr2.length) return false; // 행개수 다른 경우
        if (arr1[0].length != arr2[0].length) return false; // 열개수 다른 경우

        for (int i = 0; i < arr1.length; i++) {
            // 세로 3번
            for (int j = 0; j < arr1[0].length; j++) {
                // 가로 3번
                if (arr1[i][j] != arr2[i][j]) return false; // 원소 다른 경우
            }
        }
        return true; // 세가지 경우를 제외하면 같은 함수
    }

    // 이차원 배열을 복사하는 함수
    public static int[][] deepCopy(int[][] arr) {
        int[][] copy = new int[arr.length][arr[0].length]; // 새로운 배열 선언

        for (int i = 0; i < arr.length; i++) {
            // 세로 3번
            for (int j = 0; j < arr[0].length; j++) {
                // 가로 3번
                copy[i][j] = arr[i][j]; // 새로운 주소에 원본의 데이터 넣기
            }
        }
        return copy;
    }

    // 일차원 배열을 복사 하는 함수
    public static int[] copy(int[] arr) {
        int[] copy = new int[arr.length];
        for (int i =0; i < arr.length; i++) {
            copy[i] = arr[i];
        }
        return  copy;
    }
}



복사 메서드

  1. Object.clone()
  • 배열도 객체이므로 이 메서드를 사용하여 전체 배열 복사를 수행할 수 있음
  • 배열 일부 복사할 수 없음
  • 객체 복사는 Cloneable 인터페이스의 clone() 재정의 가능

  1. System.arraycopy(src, srcPos, dest, destPos, length)
  • 가장 좋은 방법 (자바 아닌 navitc c++ 로 되어 있기 때문)
  • 복사할 총 요소 수원본 및 대상 배열 인덱스 위치 지정가능
  • System.arraycopy(src, 3, dest, 2, 5) : src의 3번째 인덱스부터 dest의 2번째 인덱스까지 5개의 요소를 src에서 dest로 복사

  1. Arrays.copyOf(original, newLength)
  • 배열의 처음 몇 개 요소 복사 가능
  • 배열의 전체 복사 가능
  • 가독성이 좋고 사용하기 쉬움
  • 내부적으로 System.arraycopy() 호출

  1. Arrays.copyOfRange(original, from, to)
  • 시작 인덱스가 0이 아닌 일부 배열 원소 복사 가능

  1. 성능비교
    https://stackoverflow.com/questions/18638743/is-it-better-to-use-system-arraycopy-than-a-for-loop-for-copying-arrays

0개의 댓글

관련 채용 정보