Java 공부 8일차(배열이란?)2편

임선구·2025년 1월 5일

몸 비틀며 Java

목록 보기
9/58

오늘의 잔디


오늘의 공부


배열 리펙토링

배열 리펙토링 - 변수 값 사용

이제 배열을 사용해서 코드를 단계별로 리펙토링 해보자.
먼저 변수 값을 사용한 부분을 변경해보자.

//변수 값 사용
System.out.println("학생1 점수: " + students[0]);
System.out.println("학생2 점수: " + students[1]);
System.out.println("학생3 점수: " + students[2]);
System.out.println("학생4 점수: " + students[3]);
System.out.println("학생5 점수: " + students[4]);

변수명이 students 로 같기 때문에 숫자가 반복되는 부분만 해결하면 반복문을 도입할 수 있을 것 같다. for 문을 사용해서 문제를 해결해보자.

참고: 리펙토링
리펙토링(Refactoring)은 기존의 코드 기능은 유지하면서 내부 구조를 개선하여 가독성을 높이고, 유지보수를 용이하게 하는 과정을 뜻한다. 이는 중복을 제거하고, 복잡성을 줄이며, 이해하기 쉬운 코드로 만들기 위해 수행된다. 리펙토링은 버그를 줄이고, 프로그램의 성능을 향상시킬 수도 있으며, 코드의 설계를 개선하는 데에도 도움이 된다.
쉽게 이야기해서 작동하는 기능은 똑같은데, 코드를 개선하는 것을 리펙토링이라 한다.

Array1Ref2

package array;
public class Array1Ref2 {
 public static void main(String[] args) {
 int[] students; //배열 변수 선언
 students = new int[5]; //배열 생성
 //변수 값 대입
 students[0] = 90;
 students[1] = 80;
 students[2] = 70;
 students[3] = 60;
 students[4] = 50;
 //변수 값 사용
 for (int i = 0; i < students.length; i++) {
 System.out.println("학생" + (i + 1) + " 점수: " + students[i]);
 }
 }
}
  • 반복문을 사용해서 배열을 통해 값을 사용하는 부분을 효과적으로 변경했다.
  • 배열의 인덱스는 0부터 시작하기 때문에 반복문에서 i = 0 을 초기값으로 사용했다.
  • students.length
    • 배열의 길이를 제공하는 특별한 기능이다.
    • 참고로 이 값은 조회만 할 수 있다. 대입은 할 수는 없다.
    • 현재 배열의 크기가 5이기 때문에 여기서는 5가 출력된다.
  • for문의 조건이 i < students.length 이기 때문에 i0,1,2,3,4 까지만 반복한다.
    • i 가 5가 되면 5 < 5 가 되면서 조건이 거짓이 되고, 반복을 종료한다

배열 리펙토링 - 초기화

배열은 {} 를 사용해서 생성과 동시에 편리하게 초기화 하는 기능을 제공한다.

int[] students;students = new int[]{90, 80, 70, 60, 50}; //배열 생성과 초기화 

Array1Ref3

package array;
public class Array1Ref3 {
 public static void main(String[] args) {
 int[] students;
 students = new int[]{90, 80, 70, 60, 50}; //배열 생성과 초기화
 for (int i = 0; i < students.length; i++) {
 System.out.println("학생" + (i + 1) + " 점수: " + students[i]);
 }
 }
}

이해를 돕기 위해 배열 변수의 선언과 배열의 생성 및 초기화를 두 줄로 나누었지만 다음과 같이 한줄도 가능하다.

int[] students = new int[]{90, 80, 70, 60, 50}; //배열 변수 선언, 배열 생성과 초기화

배열 리펙토링 - 간단한 배열 생성

배열은 {} 만 사용해서 생성과 동시에 편리하게 초기화 하는 기능을 제공한다.

배열의 편리한 초기화

int[] students = {90, 80, 70, 60, 50}; 

단 이때는 예제와 같이 배열 변수의 선언을 한 줄에 함께 사용할 때만 가능하다.
물론 이렇게 하더라도 자바가 내부에서 배열 요소의 크기를 보고 new int[5] 을 사용해서 배열을 생성한다. 따라서 기존 코드를 조금 더 편리하게 사용할 수 있는 편의 기능이라 생각하면 된다.

오류

int[] students;
students = {90, 80, 70, 60, 50}; 

Array1Ref4

package array;
public class Array1Ref4 {
 public static void main(String[] args) { //배열 생성 간략 버전, 배열 선언과 함께 사용시 new int[] 생략 가능
 int[] students = {90, 80, 70, 60, 50};
 for (int i = 0; i < students.length; i++) {
 System.out.println("학생" + (i + 1) + " 점수: " + students[i]);
 }
 }
}

이제 학생의 점수를 추가해도 {90, 80, 70, 60, 50} 의 내용만 변경하면 된다. 그러면 나머지 코드는 변경하지
않아도 된다.
배열을 사용한 덕분에 프로그램을 전체적으로 잘 구조화 할 수 있었다.

2차원 배열 - 시작

지금까지 학습한 배열은 단순히 순서대로 나열되어 있었다. 이것을 1차원 배열이라 한다.
이번에 학습할 2차원 배열은 이름 그대로 하나의 차원이 추가된다. 2차원 배열은 행과 열로 구성된다.

2차원 배열은 int[][] arr = new int[2][3] 와 같이 선언하고 생성한다. 그리고 arr[1][2] 와 같이 사용하
는데, 먼저 행 번호를 찾고, 그 다음에 열 번호를 찾으면 된다.
행은 영어로 row(로우), 열은 영어로 column(컬럼)이라 한다. 자주 사용하는 단어이니 알아두자.

2차원 배열의 사용법은 [] 가 하나 추가되는 것을 제외하고는 앞서본 1차원 배열과 같다.

그림의 배열에 들어있는 데이터는 다음과 같다.
arr[행][열] , arr[row][column]

그림의 2차원 배열 데이터

  • arr[0][0]: 1
  • arr[0][1]: 2
  • arr[0][2]: 3arr[1][0]: 4
  • arr[1][1]: 5
  • arr[1][2]: 6

코드를 통해서 2차원 배열의 사용법을 알아보자.

ArrayDi0

package array;
public class ArrayDi0 {
 public static void main(String[] args) {
 // 2x3 2차원 배열을 만든다.
 int[][] arr = new int[2][3]; //행(row), 열(column)
 arr[0][0] = 1; //0행, 0열
 arr[0][1] = 2; //0행, 1열
 arr[0][2] = 3; //0행, 2열
 arr[1][0] = 4; //1행, 0열
 arr[1][1] = 5; //1행, 1열
 arr[1][2] = 6; //1행, 2열
 //0행 출력
 System.out.print(arr[0][0] + " "); //0열 출력
 System.out.print(arr[0][1] + " "); //1열 출력
 System.out.print(arr[0][2] + " "); //2열 출력
 System.out.println(); //한 행이 끝나면 라인을 변경한다.
 //1행 출력
 System.out.print(arr[1][0] + " "); //0열 출력
 System.out.print(arr[1][1] + " "); //1열 출력
 System.out.print(arr[1][2] + " "); //2열 출력
 System.out.println(); //한 행이 끝나면 라인을 변경한다.
 }
}
  • 이 코드는 2차원 배열을 만들고, 배열에 값을 1부터 6까지 순서대로 직접 입력한다.
  • 다음과 같은 결과를 만들기 위해 0행에 있는 0,1,2열을 출력한다. 그리고 다음으로 1행에 있는 0,1,2열을 출력한
    다.

실행 결과

1 2 3 //[0][0], [0][1], [0][2]
4 5 6 //[1][0], [1][1], [1][2]

2차원 배열 - 리펙토링1

구조 개선 - 행 출력 반복

구조 변경
코드 구조를 보면 비슷한 부분이 반복된다.

//0행 출력
System.out.print(arr[0][0] + " "); //0열 출력
System.out.print(arr[0][1] + " "); //1열 출력
System.out.print(arr[0][2] + " "); //2열 출력
System.out.println(); //한 행이 끝나면 라인을 변경한다.
//1행 출력
System.out.print(arr[1][0] + " "); //0열 출력
System.out.print(arr[1][1] + " "); //1열 출력
System.out.print(arr[1][2] + " "); //2열 출력
System.out.println(); //한 행이 끝나면 라인을 변경한다. 

코드를 보면 행을 출력하는 부분이 거의 같다. 차이가 있는 부분은 행에서 arr[0] 으로 시작할지 arr[1] 로 시작할지의 차이다.

다음과 같이 행(row)에 들어가는 숫자만 하나씩 증가하면서 반복하면 될 것 같다.

//row를 0, 1로 변경하면서 다음 코드를 반복
System.out.print(arr[row][0] + " "); //0열 출력
System.out.print(arr[row][1] + " "); //1열 출력
System.out.print(arr[row][2] + " "); //2열 출력
System.out.println(); //한 행이 끝나면 라인을 변경한다. 

반복문을 사용하도록 코드를 변경해보자.

ArrayDi1

package array;
public class ArrayDi1 {
 public static void main(String[] args) {
 // 2x3 2차원 배열을 만듭니다. int[][] arr = new int[2][3]; //행(row), 열(column)
 arr[0][0] = 1; //0행, 0열
 arr[0][1] = 2; //0행, 1열
 arr[0][2] = 3; //0행, 2열
 arr[1][0] = 4; //1행, 0열
 arr[1][1] = 5; //1행, 1열
 arr[1][2] = 6; //1행, 2열
 for (int row = 0; row < 2; row++) {
 System.out.print(arr[row][0] + " "); //0열 출력
 System.out.print(arr[row][1] + " "); //1열 출력
 System.out.print(arr[row][2] + " "); //2열 출력
 System.out.println(); //한 행이 끝나면 라인을 변경한다.
 }
 }
}
  • for문을 통해서 행(row)을 반복해서 접근한다. 각 행 안에서 열(column)이 3개이므로 arr[row][0] ,
    arr[row][1] , arr[row][2] 3번 출력한다. 이렇게하면 for문을 한번 도는 동안 3개의 열을 출력할 수 있
    다.
    • row=0 의 for문이 실행되면 arr[0][0] , arr[0][1] , arr[0][2] 로 1 2 3 이 출력된다.
    • row=1 의 for문이 실행되면 arr[1][0] , arr[1][1] , arr[1][2] 로 4 5 6 이 출력된다.

실행 결과

1 2 3 
4 5 6 

구조 개선 - 열 출력 반복

다음 부분을 보면 같은 코드가 반복된다.

System.out.print(arr[row][0] + " "); //0열 출력
System.out.print(arr[row][1] + " "); //1열 출력
System.out.print(arr[row][2] + " "); //2열 출력 

다음과 같이 열(column)에 들어가는 숫자만 하나씩 증가하면서 반복하면 될 것 같다.

//column를 0, 1, 2로 변경하면서 다음 코드를 반복
System.out.print(arr[row][column] + " "); //column열 출력

코드를 수정해보자.

ArrayDi2

package array;
public class ArrayDi2 {
 public static void main(String[] args) {
 // 2x3 2차원 배열을 만듭니다.
 int[][] arr = new int[2][3]; //행(row), 열(column)
 arr[0][0] = 1; //0행, 0열
 arr[0][1] = 2; //0행, 1열
 arr[0][2] = 3; //0행, 2열
 arr[1][0] = 4; //1행, 0열
 arr[1][1] = 5; //1행, 1열
 arr[1][2] = 6; //1행, 2열
 for (int row = 0; row < 2; row++) {
 for (int column = 0; column < 3; column++) {
 System.out.print(arr[row][column] + " ");
 }
 System.out.println(); //한 행이 끝나면 라인을 변경한다.
 }
 }
}
  • for문을 2번 중첩해서 사용하는데, 첫번째 for문은 행을 탐색하고, 내부에 있는 두번째 for문은 열을 탐색한다.
  • 내부에 있는 for문은 앞서 작성한 다음 코드와 같다. for문을 사용해서 열을 효과적으로 출력했다.
System.out.print(arr[row][0] + " "); //0열 출력
System.out.print(arr[row][1] + " "); //1열 출력
System.out.print(arr[row][2] + " "); //2열 출력

2차원 배열 - 리펙토링2

구조 개선 - 초기화, 배열의 길이

이 코드를 보니 2가지 개선할 부분이 있다.

    1. 초기화: 기존 배열처럼 2차원 배열도 편리하게 초기화 할 수 있다.
    1. for 문에서 배열의 길이 활용: 배열의 길이가 달라지면 for 문에서 row < 2 , column < 3 같은 부분을 같이 변경해야 한다. 이 부분을 배열의 길이를 사용하도록 변경해보자. 배열이 커지거나 줄어들어도 for문의 코드를
      변경하지 않고 그대로 유지할 수 있다.

코드를 개선해보자.

ArrayDi3

package array;
public class ArrayDi3 {
 public static void main(String[] args) {
 // 2x3 2차원 배열, 초기화
 int[][] arr = {
 {1, 2, 3},
 {4, 5, 6}
 };
 // 2차원 배열의 길이를 활용
 for (int row = 0; row < arr.length; row++) {
 for (int column = 0; column < arr[row].length; column++) {
 System.out.print(arr[row][column] + " ");
 }
 System.out.println();
 }
 }
}

초기화
1차원 배열은 다음과 같이 초기화 한다.

{1,2,3}

2차원 배열은 다음과 같이 초기화 한다. 밖이 행이 되고, 안이 열이 된다. 그런데 이렇게 하면 행열이 잘 안느껴진다.

{{1, 2, 3},{4, 5, 6}}

이렇게 라인을 적절하게 넘겨주면 행과 열이 명확해진다. 따라서 코드를 더 쉽게 이해할 수 있다.

{
 {1, 2, 3},
 {4, 5, 6}
}

배열의 길이

for 문에서 2차원 배열의 길이를 활용했다.

  • arr.length 는 행의 길이를 뜻한다. 여기서는 2가 출력된다.
    • {{},{}} 를 생각해보면 arr 배열은 {} , {} 2개의 배열 요소를 가진다.
  • arr[row].length 는 열의 길이를 뜻한다. 여기서는 3이 출력된다.
    • arr[0]{1,2,3} 배열을 뜻한다. 이 배열에는 3개의 요소가 있다.
    • arr[1]{4,5,6} 배열을 뜻한다. 이 배열에는 3개의 요소가 있다

잘 동작하는지 확인해보자.

이제 배열의 초기화 부분만 다음과 같이 변경하면 바로 적용된 결과를 확인할 수 있다. 나머지 코드는 변경하지 않아도
된다.

{1, 2, 3},
{4, 5, 6},
{7, 8, 9}

구조 개선 - 값 입력

이번에는 배열에 직접 1,2,3 숫자를 적어서 값을 할당하는 것이 아니라, 배열의 크기와 상관없이 배열에 순서대로 1씩
증가하는 값을 입력하도록 변경해보자.

package array;
public class ArrayDi4 {
 public static void main(String[] args) {
 // 2x3 2차원 배열, 초기화
 int[][] arr = new int[2][3];
 int i = 1;
 // 순서대로 1씩 증가하는 값을 입력한다.
 for (int row = 0; row < arr.length; row++) {
 for (int column = 0; column < arr[row].length; column++) { arr[row][column] = i++;
 }
 }
 // 2차원 배열의 길이를 활용
 for (int row = 0; row < arr.length; row++) {
 for (int column = 0; column < arr[row].length; column++) {
 System.out.print(arr[row][column] + " ");
 }
 System.out.println();
 }
 }
}
  • 중첩된 for 문을 사용해서 값을 순서대로 입력한다.
  • arr[row][column] = i++ 후의 증감 연산자( ++ )를 사용해서 값을 먼저 대입한 다음에 증가한다.
    • 코드에서 int i = 1 으로 i 가 1부터 시작한다.

2차원 배열 선언 부분인 new int[2][3]new int[4][5] 처럼 다른 숫자로 변경해도 잘 동작하는 것을 확인할 수 있다.

new int[4][5]로 변경시 출력

1 2 3 4 5 
6 7 8 9 10 
11 12 13 14 15 
16 17 18 19 20 

향상된 for문

앞서 반복문에서 설명하지 않은 내용이 하나 있는데, 바로 향상된 for문(Enhanced For Loop)이다. 향상된 for문을
이해하려면 배열을 먼저 알아야 한다. 각각의 요소를 탐색한다는 의미로 for-each문이라고도 많이 부른다.
향상된 for 문은 배열을 사용할 때 기존 for 문 보다 더 편리하게 사용할 수 있다.

향상된 for문 정의

for (변수 : 배열 또는 컬렉션) {
 // 배열 또는 컬렉션의 요소를 순회하면서 수행할 작업
}

코드로 확인해보자.
EnhancedFor1

package array;
public class EnhancedFor1 {
 public static void main(String[] args) {
 int[] numbers = {1, 2, 3, 4, 5};
 //일반 for문
 for(int i = 0; i < numbers.length; ++i) {
 int number = numbers[i];
 System.out.println(number);
 }
 //향상된 for문, for-each문
 for (int number : numbers) {
 System.out.println(number);
 }
 //for-each문을 사용할 수 없는 경우, 증가하는 index 값 필요
 for(int i = 0; i < numbers.length; ++i) {
 System.out.println("number" + i + "번의 결과는: " + numbers[i]);
 }
 }
}

일반 for문

//일반 for문
for(int i = 0; i < numbers.length; ++i) {
 int number = numbers[i];
 System.out.println(number);
}

먼저 일반 for문을 살펴보자. 배열에 있는 값을 순서대로 읽어서 number 변수에 넣고, 출력한다.
배열은 처음부터 끝까지 순서대로 읽어서 사용하는 경우가 많다. 그런데 배열의 값을 읽으려면 int i 와 같은 인덱스를 탐색할 수 있는 변수를 선언해야 한다. 그리고 i < numbers.length 와 같이 배열의 끝 조건을 지정해야주어야한다. 마지막으로 배열의 값을 하나 읽을 때 마다 인덱스를 하나씩 증가해야 한다.

개발자 입장에서는 그냥 배열을 순서대로 처음부터 끝까지 탐색하고 싶은데, 너무 번잡한 일을 해주어야 한다. 그래서
향상된 for문이 등장했다.

향상된 for문

//향상된 for문, for-each문
for (int number : numbers) {
 System.out.println(number);
}
  • 앞서 일반 for문과 동일하게 작동한다.
  • 향상된 for문은 배열의 인덱스를 사용하지 않고, 종료 조건을 주지 않아도 된다. 단순히 해당 배열을 처음부터 끝
    까지 탐색한다.
  • : 의 오른쪽에 numbers 와 같이 탐색할 배열을 선택하고, : 의 왼쪽에 int number 와 같이 반복할 때 마다 찾은 값을 저장할 변수를 선언한다. 그러면 배열의 값을 하나씩 꺼내서 왼쪽에 있는 number 에 담고 for문을 수
    행한다. for문의 끝에 가면 다음 값을 꺼내서 number 에 담고 for문을 반복 수행한다. numbers 배열의 끝에 도
    달해서 더 값이 없으면 for문이 완전히 종료된다.
  • 향상된 for문은 배열의 인덱스를 사용하지 않고도 배열의 요소를 순회할 수 있기 때문에 코드가 간결하고 가독성
    이 좋다.

향상된 for문을 사용하지 못하는 경우
그런데 향상된 for문을 사용하지 못하는 경우가 있다.
향상된 for문에는 증가하는 인덱스 값이 감추어져 있다. 따라서 int i 와 같은 증가하는 인덱스 값을 직접 사용해야 하는 경우에는 향상된 for문을 사용할 수 없다.

//for-each문을 사용할 수 없는 경우, 증가하는 index 값 필요
for(int i = 0; i < numbers.length; ++i) {
 System.out.println("number" + i + "번의 결과는: " + numbers[i]);
}

이 예제에서는 증가하는 i 값을 출력해야 하므로 향상된 for문 대신에 일반 for문을 사용해야 한다.

물론 다음과 같이 억지스럽게 향상된 for문을 사용하는 것이 가능하지만, 이런 경우 일반 for문을 사용하는 것이 더 좋
다.

int i = 0;
for (int number : numbers) {
 System.out.println("number" + i + "번의 결과는: " + number);
 i++;
}
profile
끝까지 가면 내가 다 이겨

0개의 댓글