본문 바로가기
Java/Java Basic

[Java Basic] 8. 배열 2 - 배열 관련 작업

by Rosmary 2022. 6. 30.
728x90
반응형

 

 

 

 

배열은 연관있는 데이터를 묶어놓은 타입이다. 학생들의 시험 과목 점수 등 통계가 필요한 부분에서 많이 사용한다. 따라서 배열을 총합, 평균 구하기와 같이 간단한 산술 뿐만 아니라 데이터의 정렬 등의 작업에 많이 사용한다. 지난 포스팅에서 언급했듯이 배열을 다루기 위해 for문을 많이 사용하는데, 배열마다 자주 사용하는 작업을 매번 코드로 사용하기에는 번거로운 점이 많기 때문에 Java에서는 java.lang 패키지 내의 Array 클래스에 일부 작업을 수행할 메서드를 미리 구성해놓았다. 아직 패키지, 클래스, 메서드에 대한 포스팅을 진행하지 않았으나, 배열 작업을 일일히 코딩하지 않고 할 수 있는 방법이 있다고 우선 알아두면 될 듯 하다.

 

 

1. 정수형 배열 총합 구하기

 

지난 포스팅 마지막 부분에서 필자가 한 학급 내 세 학생의 국어, 영어, 수학 점수를 배열로 저장했는데, 이 부분부터 진행해보려 한다. 각 학생이 세 과목에서 받은 총점을 기준으로 등수를 부여한다고 하면, 각 배열의 합을 for 문을 사용하여 계산해야 한다.

 

 

각 학생의 총합을 출력하기 위해서는 각 과목의 점수를 0값에 합하는 방식으로 진행해야한다. 따라서 총합을 저장할 정수형 변수를 0으로 생성하고, for문을 사용하여 각 과목의 점수를 총합을 저장할 변수에 추가 합산해주면 된다.

 

 

2. 정수열 배열의 평균 구하기

 

배열의 총합을 구할 수 있다면 평균을 구하는 것은 어렵지 않다. 총합에서 단순히 배열의 크기만큼 나눗셈을 진행해주면 되니까. 단, 배열이 정수형 데이터를 저장하고 있고, 배열의 크기 역시 정수형 타입이므로, 정확한 평균 점수를 구하기 위해서 평균을 구하는 연산의 피연산값 둘 중 하나를 실수형으로 형전환(Type Casting) 해 주어야 한다.

 

 

 

3. 배열 크기 변경 및 복사

 

이제, 각 학생의 총점도 배열에 추가한다고 가정해보자. 하지만 이미 점수를 저장하는 _score 배열의 경우 이미 크기 3인 배열로 선언되어 크기를 변경하는 것이 불가능하다. 따라서 배열의 크기를 변경해야 하는 상황이 발생하는 경우, 새 배열을 생성하고, 해당 배열에 기존 배열의 값을 복사하는 방법을 사용해야 한다. 참고로 평균점수는 기존 배열에 추가가 불가능하다. 데이터 타입이 float이기 때문에. 

 

*  서로 다른 타입의 자료를 하나로 묶는 방법은 추후 클래스 관련 내용을 포스팅 할 때 언급할 예정이다.

 

지면이 부족하니 필자는 A 학생에 대해서 총합을 추가하는 배열을 생성해보려 한다.

 

 

여기서 주요하게 보아야 할 부분은 배열의 복사다. 새 배열에 기존 배열 값을 복사하는 것을 for 문으로 진행하되, 새 배열의 마지막에 추가해야하는 총점만 for 문 밖에서 진행한다. 위의 예시에서는 통제변수 i의 조건식을 A_score 배열 크기로 제한했는데, new_A_score의 배열크기로 제한하려면 아래와 같이 코드를 수정하면 된다.

 

사실 for 문 안에서 if-else에 의해 연산이 거듭 진행되기 때문에, 첫 예제에 비해 자원 소모가 조금 더 심하다.

 

참고로 원본 배열의 손상을 방지하기 위해 완전한 복사본으로 작업하고자 하는 경우, java.util 패키지의 Arrays 클래스 내 copyOf 매서드(함수)로 배열을 복사하는 것도 가능하다. 단, 복사로 생성된 배열의 경우, 배열 크기를 수동으로 지정하더라도 Arrays.copyOf 메서드 내 인자로 지정된 배열의 크기에 맞게 변경된다.

 

 

특정 배열을 복사 + 크기 변경을 하기 위한 별도의 매서드가 기본 패키지인 java.lang의 System 클래스에 존재한다. System.arraycopy() 매서드가 그것이다.

 

System.arraycopy(복사할 배열명, 복사를 시작할 index, 새 배열명, 새 배열 내 복사 배열의 첫 index, 복사할 배열 크기)

 

 

 

 

4. 배열의 역순 정렬

 

배열의 역순 정렬 역시 for 문을 활용하여 작업을 진행한다. 먼저 좌우 가장 끝단에 위치한 값을 교체하고, 그 다음 양 끝단에 위치한 값을 교체하는 방법으로 배열을 역순 정렬할 수 있다.

 

배열 데이터를 증가시키기 위해 세 학생의 시험 성적을 하나의 배열로 통합하고 이 과정을 진행해보려 한다.

 

 

양 끝단의 값을 교체하면서 가운데로 이동하는 형태이기 때문에 지금까지 사용한 for 문들과 달리 통제 변수의 조건식이 전체 배열 크기의 1/2 값으로 제한된다. 만약 이전의 for 문과 마찬가지로 통제변수의 조건식이 전체 배열 길이로 제한된다면, 이 과정이 두 번 진행되어 원래 배열과 동일한 결과가 출력된다.

 

 

 

 

5. 배열의 정렬

 

이번에는 모든 학생이 받은 시험 점수를 내림차순 및 오름차순으로 정리하는 방법에 대해 진행해보려한다. 세 학생의 모든 점수를 하나의 배열에 몰아넣고, 이를 오름차순으로 정렬해보려한다.

 

배열값을 정렬하는 방법은 여러가지가 있다 - 이 방법은 사실 "자료구조"와 "알고리즘" 과목의 주제라, 다른 주제의 포스팅에서 다룰 예정이다 - 그 중 필자는 버블 정렬이라고 불리는 방법으로 배열의 오름차순 정리를 진행할 것이다.

 

버블 정렬 절차. 출처: https://www.programiz.com/dsa/bubble-sort

 

 버블 정렬은 쉽게 설명하자면 배열의 바로 옆에 위치한 값과 대소 비교를 통해 자리를 바꾸는 방법이다. 우선 코드를 먼저 보자.

 

 

위의 코드 결과로 콘솔에 new_arr가 오름차순 정렬된 것을 확인할 수 있다. 코드가 조금 복잡해보이는데, 하나씩 분석하면 어려운 내용은 아니다. 안쪽에 있는 for 문은 new_arr[0] 부터 바로 오른쪽에 위치한 값과의 대소 연산을 통해 자리바꿈을 실시하도록 하는 코드다. 이 for 문이 한 사이클을 완료하면, 배열의 가장 오른쪽에는 배열 내 가장 큰 수가 저장되게 된다.

확인을 위해 바깥쪽 for 문에 break 조건을 달아보자.

 

 

따라서 안쪽에 위치한 for 문의 통제변수 조건은 new_arr.length - 1 - i가 된다. 물론 new_arr.length - 1 도 동일한 결과가 나오지만, 불필요한 연산이 추가되기 때문에 자원 소모가 조금 더 심하다.

 

다음으로 i가 1인 경우 결과를 살펴보자. 이 경우 배열의 우측 두 번째 값에 배열에서 두 번째로 큰 정수가 저장된다. 

 

 

따라서  안쪽에 위치한 for 문은 최대 (배열 크기 - 1 )번 반복되어야 완전한 오름차순 정렬이 마무리된다.

 

물론, 배열이 정렬이 잘 되어 있다면 최대 반복 횟수까지 가지 않아도 그 전에 작업이 마무리될 수 있다. 위의 예시도 i 값이 6인 경우 최종 오름차순 결과와 동일한 상태가 되는 것을 알 수 있다. 

 

 

 

바깥 루프문의 전체 사이클을 수행하는 것이 불필요한 행위가 될 수 있는 배열이 들어올 수 있다. 이럴 때는 위의 코드를 아래와 같이 효율적으로 변경할 수 있다.

 

 

코드를 분석해보면 알겠지만, 오름차순과 내림차순의 정렬은 안쪽 for 문의 if 문에 정의된 조건에서 부등호만 변경해주면 된다.

 

 

위의 코드를 분석했으면 느끼겠지만, 배열의 정렬은 직접 코드로 일일이 작성하기에는 시간과 노력이 많이 들어간다(안 그런 코드가 어디있겠냐마는).  Java는 Arrays라는 클래스에서 특정 배열을 오름차순으로 정렬해주는 sort 메서드(함수)를 제공한다. 이 메서드를 사용하면 훨씬 간결한 코드 작성이 가능해진다.

 

Arrays.sort() 메서드는 dual pivot quicksort라는 정렬 방법을 사용한다.

 

 

단, Arrays.sort() 매서드는 오름차순만 지원할 뿐, 내림차순에 대한 지원은 없다. 따라서 Arrays.sort() 매서드를 사용하여 배열의 내림차순을 진행하고 싶다면, Arrays.sort() 매서드를 배열에 적용한 뒤, 역순으로 재정렬을 진행해야 한다. 사실 추가 패키지와 클래스를 사용하면 조금 더 쉽긴 한데... 너무 주제를 벗어난 내용이라 추후 기회가 된다면 언급하려 한다.

 

앞서 언급한대로, 이 내용은 패키지와 클래스에 대한 이해가 있어야 한다. 지금 당장 저 내용이 이해되지 않는다고 해도 큰 문제가 되는 부분은 아니다. 단지 이런 기능도 Java에서 지원하는 것만 알고 있으면 된다.

 

 

6. 배열 무작위 정렬 - 로또번호

 

이번에는 예시를 조금 바꿔 로또 번호로 넘어오려 한다. 로또의 경우 1부터 45까지의 숫자 중, 무작위 6개를 선정한다. 1~45가 오름차순 정렬된 배열에서 무작위로 뒤섞어 앞의 6개 숫자만 당첨 번호로 지정하는 것을 가정하여 코드를 작성하려 한다.

 

먼저 1부터 45까지의 오름차순 배열을 생성한다.

 

 

다음으로 무작위로 순서를 뒤바꿔주어야한다. 따라서 자리를 무작위로 선정하기 위해 무작위로 번호를 생성하는 매서드를 사용해야 한다. 숫자를 무작위로 생성하는 매서드는 기본 패키지인 java.lang의 Math 클래스 내에 정의된 random() 메서드다. 이 함수는 0~1 사이 무작위 실수값을 반환한다.

 

 

따라서 Math.random() 반환값에 10을 곱하면 이론상 1부터 9 사이 숫자가 출력되게 된다. 필자는 로또 숫자의 범위인 1~45만 출력되면 되므로 10 대신 45를 곱할 것이다.

 

 

무작위로 숫자를 출력하는 법은 배웠으니, 이제 배열 내 두 숫자를 무작위로 선정하고 바꿔보자.

 

 

무작위로 자리를 바꾸기 위해서, 바꾸고자하는 자리(index)를 Math.random 함수로 변수화해야한다. 변수화가 완료되면 index1, index2 변수를 생성하고 배열 내 해당 값을 index로 참조하여 두 값의 교체를 진행하면 된다. 순차 정렬된 배열의 24번째 및 30 번째 위치한 값이 변경된 것이 확인된다. 이제 이 과정을 for 문을 사용하여 여러 번 반복해보자. 그리고 앞 6자리만 출력해보자.

 

 

 

for 문이 충분히 많이 반복되지 않으면 자리 바꿈이 많이 발생하지 않으므로 앞 6자리의 숫자가 1 2 3 4 5 6을 벗어날 확률이 적다. 따라서 이를 방지하기 위해 충분히 많이 반복될 수 있도록 통제변수 조건식의 제한값을 큰 수로 부여해야한다. 만약 로또를 여러 게임 진행하고 싶다면, 위의 코드를 다시 반복하면 된다.

 

 

 

7. 특정 값의 배열 저장 위치 확인

 

배열로 프로그래밍을 진행하다보면, 배열 내 특정 값의 위치를 확인해야하는 경우가 종종 생긴다. 앞의 여러 작업과 마찬가지로 특정 값의 위치를 찾는 작업 또한 for 문을 사용하여 진행할 수 있다. 필자는 무작위 로또 배열에서 23이라는 숫자가 저장된 배열의 위치를 확인해보려 한다.

 

 

for 문으로 배열 내 저장값을 하나씩 조회하면서, 특정 숫자와 일치하는 배열 위치를 찾는 코드다. 이 코드를 기반으로 필자가 선택한 6자리 번호가 로또 당첨번호인지 아닌지 확인하는 코드로 발전시킬 수 있다. 만약 찾고자 하는 숫자가 index 5 이하로 표시되면 해당 숫자는 로또 당첨번호에 포함된다. 

 

 

특정 값의 배열 위치를 찾는 코드 역시 java.util 패키지의 Arrays 클래스에서 binarySearch() 라는 매서드로 제공한다.

이 매서드는 인자로 배열명과, 배열 내 찾고자 하는 값을 지정해주면 되고, 반환되는 값은 int 자료형으로 배열의 index가 반환된다.

 

import java.util.Arrays;

...

    Arrays.binarySearch(배열명, 배열 내 찾을 값);

 

 

따라서 위의 코드를 Arrays.binarySearch 클래스로 간결하게 변환할 수 있다.

 

 


 

Python으로 먼저 프로그래밍을 접하셨던 분들이라면, Python에서 기본으로 제공되는 배열 관련 작업을 일일이 코드를 쳐야하는 Java가 꽤 번거로울 수 있다. 무작위 배치나 역순 정렬은 Python에서 .shuffle()이나 .reverse() 메서드로 쉽게 해결이 되었던 것과 비교하면 말이다. 하지만 Java에서 배열 관련 작업을 하나씩 생각하며 손으로 코딩을 하는 과정을 통해 어떻게 코드를 작성해야 효율적으로 프로그램이 동작하는지 확인할 수 있기 때문에 추후 자료구조나 알고리즘 학습 시, Python 지식만으로 잘 이해가 되지 않는 부분을 빠르게 이해할 수 있다는 장점이 있다. 물론, 연습을 열심히 했다면 말이다.

 

다음 포스팅에서는 String과 문자열 배열에 대해 알아보려 한다.

 

 

 

Fin.

 

 

 

반응형

댓글