자바 배열(array)
목차
자바 배열에 대하여 알아보겠습니다.
자바에서 변수를 사용하려면 선언 후 사용하면 됩니다. 변수를 사용하면 하나의 값을 저장할 수 있습니다.
그런데 아주 많은 변수들을 사용해야 한다면 좀 불편해집니다. 예를들어 한 학급에 30명의 시험점수를 처리해야 한다면 변수를 30개 만들어야 하는데 기존의 변수로 못하진 않지만 분명 번거롭습니다.
여기서 배열이 등장합니다.
int student_score_1; int student_score_2; int student_score_3; ... int student_score_30;
일단 선언하는데만 30줄이 필요하고 사용도 불편합니다.
배열의 생긴 모양을 보면 ‘아하- 그렇구나’ 라고 무릎을 치게됩니다.
30명의 배열 선언은 int[] student = new int[30]; 입니다.
public static void main(String[] args) { int[] student = new int[30]; for (int i = 0; i < 30; i++) { if(i%10 == 0) System.out.println(); System.out.print(student[i] + ", "); } } [실행결과] 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
30개의 배열을 생성하여 출력해 봤습니다. 자바 static 변수에 대한 내용에서 메모리의 종류에 대하여 다루었습니다만, new 라고 사용하는 것은 동적 메모리인 Heap Memory 입니다.
클래스를 생성하는 위치와 같습니다. 배열을 선언하고 초기화할 때 30개의 int 형 변수를 0으로 초기화 했습니다.
이를 사용하는 것은 인덱스를 사용합니다. 배열의 인덱스는 0부터 시작하는 것에 주의합니다. 30까지면 0부터 29까지 사용합니다. student[0] … student[29] 까지 30개입니다.
난수 30개를 입력한 후 출력해보겠습니다.
public class Main { public static void main(String[] args) { // 정수 30개 배열 int[] student = new int[30]; // 난수 30개 입력 for (int i = 0; i < 30; i++) { student[i] = (int)(Math.random()*10)+1; } // 30개 출력 for (int i = 0; i < 30; i++) { if(i%10 == 0) System.out.println(); System.out.print(student[i] + ", "); } } } [실행값] 5, 1, 3, 3, 7, 4, 9, 5, 5, 6, 4, 10, 9, 8, 3, 1, 9, 6, 10, 4, 7, 3, 4, 8, 9, 3, 2, 8, 8, 6,
1에서부터 10까지 난수를 입력해서 출력해봤습니다. 배열은 for 루프와 궁합이 잘 맞습니다. 인덱스를 0부터 n 까지 이동하며 값을 할당하거나 값을 가져오거나 하는 것이 직관적입니다.
배열 선언과 초기화
배열을 초기화 하는 방법이 하나가 아니라 혼동할 가능성이 있습니다. 외워서 하는 것 보다는 패턴을 익히는 방법이 좋습니다.
public class Main { public static void main(String[] args) { // first array int[] myArray1 = new int[] {10, 20, 30}; System.out.println("첫번째 배열 크기: " + myArray1.length); // 오류 int[숫자] 사용 불가하다 // int[] errorArray = new int[3] {2, 4, 6}; // second array int[] myArray2 = {11, 22, 33, 44, 55}; System.out.println("두번째 배열 크기: " + myArray2.length); // third array int[] myArray3; myArray3 = new int[] {5, 3, 1}; System.out.println("세번째 배열 크기: " + myArray3.length); // fourth array int[] myArray4; myArray4 = new int[2]; System.out.println("네번째 배열 크기: " + myArray4.length); // fifth array int[] myArray5 = new int[10]; myArray5[0] = 3; myArray5[1] = 777; System.out.println("다섯번째 배열 크기: " + myArray5.length); } }
배열의 선언시 초기화를 함께 한다면 배열의 크기를 넣지 않습니다. 이는 컴파일러가 소스코드를 읽고 배열의 크기를 판단하도록 하기 위함입니다.
new 연산자
new 라는 연산자는 C언어로 보면 malloc 과 비슷하게 동적으로 메모리를 할당하는 일을 합니다. 동적 메모리 할당 모델은 컴파일하는 시점에 얼마나 메모리를 할당해야 하는지 알 수 없습니다. 알 수 없다기 보다는 런타임(Runtime)에 판단하도록 하는 것이죠.
JS나 파이썬 등 거의 대부분 동적 타이핑(Dynamic Typing) 언어에는 new 연산자가 생략되어 있는 것과 마찬가지 입니다. 컴파일러 혹은 인터프리터가 읽는 시점을 기준으로 할당할 메모리의 크기를 결정한다는 의미입니다.
자바에서는 배열의 메모리 할당을 명시적으로 보여주기 때문에 위와 같이 여러개의 방식으로 배열을 초기화 할 수 있습니다.
하나하나의 문법을 외우려고 하지말고 컴파일러의 입장에서 생각해보면 이해하는 것이 유리합니다.
int[] myArray1 컴파일러가 이 문장을 만나면 정수형(32bit)의 배열을 사용할 것임을 알 수 있습니다. 그 다음에 할일은 메모리에 할당하는 일인데 아직 몇개인지 알수가 없습니다. 바로 옆을 보니 new int[] {10, 20, 30} 에서 new 키워드로 동적 메모리 할당을 하도록 하고 10, 20, 30 의 세개의 32bit 정수형 공간을 계산해서 메모리에 옮겨놓습니다. 여기까지가 컴파일러가 하는 초기화입니다.
두번째 배열에서 new int[] 는 생략했습니다. 생략해도 컴파일러가 몇개인지 계산할 수 있습니다.
세번째 배열은 선언과 초기화를 분류 했습니다. 가능합니다.
네번째 배열에서는 별도의 값을 입력하지 않고 크기만 줬습니다. 이렇게 하면 공간부터 확보하고 데이터는 나중에 입력해도 됩니다. 자체 초기화에는 정수형에는 0 실수형에는 0.0 문자열 등 참조형에는 null 값이 들어갑니다. 이것도 컴파일러가 충분히 판단할 수 있겠죠?
다섯번째 배열에서는 첫번째 문장에서 10개의 정수 공간을 0으로 초기화한 후 인덱스 연산자 [ ]를 사용하여 개별 할당을 합니다. 할당하지 않은 영역은 컴파일러가 0과 null 값으로 초기화한 것을 알 수 있습니다.
int[] errorArray = new int[3] {2, 4, 6}; 중간에 나오는 이 문법의 문제는 int[3] 에서 프로그래머가 배열의 크기를 지정했기 때문입니다. 컴파일러는 자신이 판단할 문제를 사람이 판단하는 것을 이해할 수 없습니다. 따라서 오류메시지를 출력합니다.
java: array creation with both dimension expression and initialization is illegal
dimension expression 차원의 표현식 int[3] 과 initialization 초기화 {2, 4, 6} 은 illegal (불법이다) 라고 규정합니다. 즉 두개를 동시에 하지 말고 한가지만 하라는 말입니다.
우리 각자가 훌륭한 프로그래머가 되기 위해서는 컴파일러의 오류메시지를 유심히 봐야 합니다. 영어지만 사전 찾아보면 명확한 정의가 있는 기술용어입니다. 무슨 문학적 소질이나 감성을 요구하는 것이 아니므로 사전을 찾을 의지만 있으면 알 수 있습니다.
대부분 프로그래머들이 시행착오(trial and error) 방식으로 문제를 해결합니다. 남들이 보면 매우 비효율적인 모습, 속된말로 ‘삽질’ 처럼 보이는 과정이 실은 자신의 한계를 극복하고 기계를 이해하는 과정입니다.
두뇌가 슈퍼컴퓨터급 천재가 아닌 이상 이 과정을 수도없이 반복하는데요. 이는 평범한 학생이 위대한 프로그래머가 되는 필수적인 과정입니다.
혹은 위대하지 않더라도 프로가 되기 위한 기본 소양입니다.
배열의 사용
배열의 사용은 아래와 같이 배열 인덱스 연산자 [ ] 를 사용합니다. for 루프에서 카운트 인덱스 변수 i 를 사용해도 되기 때문에 많은 수도 한번에 초기화 시킬 수 있습니다.
myArray5[0] = 3; myArray5[1] = 777;
n개 배열에서 인덱스의 순서는 0번 부터 n-1 인 것을 주의합니다. 수학과 달리 0부터 시작하는 이유는 자료형이 0부터 시작하기 때문입니다. 예를 들어 바이트는 256개의 숫자범위를 저장할 수 있는데 부호없는 수를 0~255 까지 저장할 수 있습니다.
인덱스를 0부터 사용하는 것은 자바만 그런게 아니라 모든 프로그래밍 언어 공통이니 알아두면 좋습니다.
자바 배열의 구조
배열을 더 명확하게 이해하는 방법 중 하나는 자바 배열의 구조를 그려보는 것입니다. 배열은 속도가 빠른데 이는 인덱스에 따라 메모리의 인접한 영역에 붙어있기 때문입니다.
위 이미지에서는 메모리의 일부를 시각화 했습니다. 어려운 알고리즘을 짜다가 막힐 때는 이와 같이 그림을 그려놓고 값을 하나씩 조작해보면 문제를 더 쉽게 풀 수 있습니다.
(참고: 현대 OS는 가상메모리를 사용하므로 실제 물리적 배열과는 차이가 날 수 있다)
향상된 for문의 사용
자바의 향상된 for 문이라고 하는데 문법적으로 python 같은 스크립트 언어에서 지원하는 enumeration 과 비슷한 형태입니다.
for문을 매번 초기화 시키고 그러는게 C언어 부터의 정석인데 좀 귀찮으니까 직관적인 문법을 지원하는 것으로 볼 수 있습니다.
그런데 java 는 정적인 언어라서 잘 손이 안가는 방식입니다. 최근에 동적 타이핑이 흐름처럼 되면서 MS의 C#이나 Java 에서도 이런 문법들을 지원하려는 의지가 느껴집니다. 입문 단계에서는 이런 문법이 있다는 정도로 알아두면 좋습니다.
public class Main { public static void main(String[] args) { String[] myStr = {"apple", "kiwi", "orange", "strawberry"}; for(String val : myStr){ System.out.println(val); } } }
apple kiwi orange strawberry
요약
자바에서 배열(Array)의 사용법과 구조에 대하여 알아봤습니다.
배열은 컴퓨터에서 대량의 데이터를 처리하기 위한 유용한 도구입니다. 이는 단순히 프로그래밍에서의 문법이 아니라 컴퓨터적 사고를 확장시킬 수 있는 기본 아이디어입니다.
다차원 배열을 활용해서 할 수 있는 일은 무궁무진합니다. 21세기의 상징인 미려한 2D, 3D 그래픽 기술도 배열에서 시작했습니다. 테트리스 게임의 도형은 배열과 GDI (Graphic Device Interface)로 만들었습니다.
프로그래밍 교재로써만 배열을 보면 매우 딱딱하고 재미없지만 학습을 계속하다보면 실세계의 배열의 매력과 신비에 대하여 알게 될 것 입니다.
이전 학습부터 계속 강조하지만 이제 자바 프로그래밍 중반부 챕터부터는 인간의 사고가 아니라 컴퓨터적인 사고를 가져야 발전할 수 있기 때문에 어렵습니다.
가끔 유명한 프로그래머들의 Speech를 들어 보면 도대체 무슨 생각을 하길래 저런 사고방식인 것일까? 의문이 들 때가 있습니다. 그들은 다년간 컴퓨터앞에서 소스 프로그램을 작성하며 컴퓨터와 인간의 중간적 사고를 체득합니다.
컴퓨터를 이해할 수록 그들의 말이 들리고 모른다면 이해할 수 없을 것 입니다. 배열을 이해하는 것은 그들의 사고를 이해할 수 있는 기본이 될 것 입니다.
외부참조문서
자바 배열 Java Arrays (w3schools.com)