Chapter 4. 조건문과 반복문
1. 조건문 - if, switch
제어문(control statement) : 프로그램의 흐름(flow)을 바꾸는 역할을 하는 문장들. 조건문과 반복문이 있음.
▼ if 구문의 구조
if (조건식) {
// 조건식이 참(true)일 때 수행될 문장들을 적는다.
}
if 구문에 사용되는 조건식은 일반적으로 비교 연산자와 논리 연산자로 구성된다.
자바에서 조건식의 결과는 반드시 true 또는 false가 되어야 한다.
괄호 { }를 사용해서 작성하는 블럭(block)의 끝에는 ;를 붙이지 않는다.
블럭{ } 안의 문장들은 탭(tab)으로 들여쓰기(indentation)를 해줘야 한다.
문장이 한 줄 이라면 블럭{ }은 생략 가능하다.
▼ if-else 구문의 구조
if (조건식) {
// 조건식이 참(true)일 때 수행될 문장들을 적는다.
} else {
// 조건식이 거짓(false)일 때 수행될 문장들을 적는다.
}
조건식의 결과에 따라 이 두 개 의 블럭{ } 중 어느 한 블럭{ }의 내용이 수행되고 전체 if문을 벗어나게 된다.
조건식의 비교 연산 횟수가 줄어든다는 장점이 있다.
▼ if-else if 구문의 구조
if (조건식1) {
// 조건식1의 연산결과가 참일 때 수행될 문장들을 적는다.
} else if (조건식2) {
// 조건식 2의 연산결과가 참일 때 수행될 문장들을 적는다.
} else if (조건식3) { // 여러 개의 else if를 사용할 수 있다.
// 조건식 3의 연산결과가 참일 때 수행될 문장들을 적는다.
} else { // 마지막에는 보통 else블럭으로 끝나며, else블럭은 생략 가능하다.
// 위의 어느 조건식도 만족하지 않을 때 수행될 문장들을 적는다.
}
첫 번째 조건식부터 순서대로 평가해서 결과가 참인 조건식을 만나면, 해당 블럭{ }만 수행하고 'if-else if'문 전체를 벗어난다.
만약 결과가 참인 조건식이 하나도 없으면, 마지막에 있는 else블럭의 문장들이 수행된다.
else블럭은 생략 가능하므로 생략되었을 때에는 if-else if문의 어떤 블럭도 수행되지 않을 수 있다.
▼ 중첩 if 구문의 구조
if (조건식1) {
// 조건식1의 연산결과가 true일 때 수행될 문장들을 적는다.
if (조건식2) {
// 조건식1과 조건식2가 모두 true일때 수행될 문장들
} else {
// 조건식1이 true이고, 조건식2가 false일 때 수행되는 문장들
}
} else {
// 조건식1이 false일 때 수행되는 문장들
}
중첩문에서는 괄호의 생략에 조심해야 한다. 만약 괄호를 생략하고 else문을 적을 경우, 가장 가까운 if 구문의 else 구문으로 간주된다. 아래와 같이 코드를 작성할 경우 그렇다.
if (num >= 0)
if (num != 0)
sign = '+';
else
sign = '-';
위와 같이 코드를 작성한다면 가장 하단의 if 구문은 if (num >= 0) 구문의 if문처럼 보이지만, 실제 컴파일 할 경우 결과는 아래와 같다. 따라서 가급적이면 블럭{ }을 넣어 if블럭과 else블럭의 관계를 확실히 해주는 것이 좋다.
if (num >= 0) {
if (num != 0) {
sign = '+';
} else {
sign = '-';
}
}
switch 구문은 단 하나의 조건식으로 많은 경우의 수를 처리한다. 그 과정은 아래와 같다.
- 조건식을 계산한다.
- 조건식의 결과와 일치하는 case문으로 이동한다.
- 이후의 문장들을 수행한다.
- break문이나 switch문의 끝을 만나면 switch문 전체를 빠져나간다.
▼ switch 구문의 구조
switch (조건식) {
case 값1 :
// 조건식의 결과가 값1과 같을 경우 수행될 문장들
// ...
break;
case 값2 :
// 조건식의 결과가 값2와 같을 경우 수행될 문장들
// ...
break; // switch문을 벗어난다.
default :
// 조건식의 결과와 일치하는 case문이 없을 때 수행될 문장들
// ...
}
default는 if문의 else블럭과 같은 역할로, 조건식의 결과와 일치하는 case문이 하나도 없는 경우에는 default문으로 이동한다.
switch문에서 break문은 각 case문의 영역을 구분하는 역할로, 만약 break를 생략하면 다른 break를 만나거나 swtich문 블럭{ }의 끝을 만날 때까지 나오는 문장들을 모두 수행한다. (default까지) 물론 경우에 따라서 고의적으로 break문을 생략하는 경우도 있다.
[ switch문의 제약조건 ]
- switch문의 조건식 결과는 정수 또는 문자열이어야 한다.
- case문의 값은 정수 상수만 가능하며, 중복되지 않아야 한다. (변수, 실수는 불가능. 문자열은 JDK 1.7부터 허용)
아래는 case문의 예시다.
(추측 상 JDK 1.7에서는 문자열의 정수 값을 1로 취급하는 것 같다.)
public static void main(String[] args) {
int num, result;
final int ONE = 1;
...
switch(result){
case '1' : // OK. 문자 리터럴(정수 상수 49와 동일)
case ONE : // OK. 정수 상수
case "YES" : // OK. 문자열 리터럴. JDK 1.7부터 허용
case num : // 에러. 변수는 불가.
case 1.0 : // 에러. 실수도 불가.
...
}
그러나 실제로 JDK 1.7 이상을 사용하는 InteliJ IDEA를 사용했을 때에는 문자열이 case문으로 사용이 불가능하다고 나왔다. (개인적으로는 문자열 리터럴도 사용하는 JDK가 항상 1.7 이상이라는 보장이 없으니 쓰지 않는 것이 좋아보인다.)
가위바위보 구현과 같은 예제에서는 if문보다 switch문이 더 알아보기 쉽고 간결한 때가 있다. 그러나 점수에 따른 성적 비교와 같은 경우에는 switch문의 case가 너무 많아지게 되므로 if문을 쓰는 것이 더 좋다.
난수(임의의 수)를 얻기 위해서는 Math.random()을 사용해야 하는데, 이 메서드는 0.0과 1.0 사이의 범위에 속하는 하나의 double값을 반환한다. 이때 0.0은 포함되고, 1.0은 포함되지 않는다. (즉, 표기하자면 [0.0, 1.0)이다.)
만약 1과 3 사이의 난수를 구하고 싶다면 아래와 같은 과정을 거쳐야 한다.
① 각 변에 3을 곱한다.
0.0 * 3 <= Math.random() * 3 < 1.0 * 3
0.0 <= Math.random() * 3 < 3.0
② 각 변을 int형으로 변환한다.
(int)0.0 <= (int)(Math.random() * 3) < (int)3.0
0 <= (int)(Math.random() * 3) < 3
③ 각 변에 1을 더한다.
0 + 1 <= (int)(Math.random() * 3) + 1 < 3 + 1
1 <= (int)(Math.random() * 3) + 1 < 4
switch문도 if문과 같이 중첩하여 사용이 가능하다. 단, 이때 break를 빼먹기 쉬우니 조심하자. 예제는 교재 p.155에 있다.
2. 반복문 - for, while, do-while
for문이나 while문은 단 한 번도 수행되지 않을 수 있지만, do-while문에 속한 문장은 무조건 최소한 한 번은 수행되어야 한다.
▼ for 구문의 구조
for (초기화; 조건식; 증감식) {
// 조건식이 참일 때 수행될 문장들을 적는다.
}
for문의 경우 초기화, 조건식, 증감식, 블록{ }으로 구성되어 있다. 이때 초기화, 조건식, 증감식의 경우 생략이 가능한데, 생략할 경우 무한 반복문이 된다. 이 경우 if문을 통해 무한 반복문을 빠져나올 수 있는 조건을 걸어주는 것이 중요하다.
for(;;) { ... } // 초기화, 조건식, 증감식 모두 생략. 조건식은 참이 된다.
또한 초기화와 증감식의 경우, 변수 여러개를 콤마(,)로 연결하여 아래의 예시와 같이 쓸 수 있다.
for(int i = 1, j = 10; i <= 10; i++, j--) { ... } // i는 1부터 10까지 1씩 증가하고,
// j는 10부터 1까지 1씩 감소한다.
for문 역시 중첩해서 사용이 가능하며, 만약 배열이나 콜렉션에 있는 요소에 접근할 때에는 향상된 for문(enhanced for statement)을 사용해 접근이 가능하다. 향상된 for문의 구조는 아래와 같다.
▼ 향상된 for 구문의 구조
for (타입 변수명 : 배열 또는 컬렉션) {
// 반복할 문장
}
이때, 이러한 구조는 JAVA 외에 Python에서도 존재한다. 파이썬에서도 기본적인 for문의 문법인 for(초기화;조건식;증감식)을 사용하지만, 위와 같이 향상된 for구문으로 리스트에 있는 원소에 접근이 가능하다. 비교를 위해 JAVA와 Python의 향상된 for구문을 아래에 적어둔다.
- JAVA에서 향상된 for문의 구조
int [] arr = {10, 20, 30, 40, 50};
for(int i = 0; i < arr.length; i++) { // 기존의 for문
System.out.println(arr[i]);
}
for (int tmp : arr) { // 향상된 for문
System.out.println(tmp);
{
- Python에서 향상된 for문의 구조
arr = [10, 20, 30, 40, 50]
for i in range(len(arr)): # 기존의 for문
print(arr[i])
for tmp in arr: # 향상된 for문
print(tmp)
while문은 if문과 switch문을 바꿔 쓸 수 있는 것처럼, for문과 while문을 바꿔 쓸 수 있다. 두 반복문은 각자 적합한 경우가 다르다.
while문의 조건식은 for문과 달리 생략이 불가능하다. 만약 무한 반복문을 만들고 싶다면 while(true)라고 적으면 된다.
▼ while 구문의 구조
while(조건식) {
// 조건식의 연산결과가 참(true)인 동안, 반복될 문장들을 적는다.
}
만약 지연 시간이 필요하다면 블록{ } 안이 비어있는 for문을 2000000000번 반복하게 시키는 경우도 존재한다. 또한 for문 뒤에 세미콜론(;)을 붙일 경우, 빈 문장을 반복하는 것으로 인식하니 주의하자.
do-while문의 경우, for문이나 while문과는 다르게 끝에 세미콜론(;)을 꼭 붙여야 한다.
▼ do-while 구문의 구조
do {
// 조건식의 연산 결과가 참일 때 수행될 문장들을 적는다.
} while (조건식);
break문은 자신이 포함된 가장 가까운 반복문을 벗어난다.
continue문은 반복문의 끝으로 이동하여 다음 반복으로 넘어간다. for문의 경우 증감식으로 이동하며, while문과 do-while문은 조건식으로 넘어간다. 전체 반복 중에 특정 조건을 만족하는 경우를 제외하고자 할 때 유용하다.
break문은 근접한 단 하나의 반복문만 벗어날 수 있기 때문에, 여러 개의 반복문이 중첩된 경우에는 break문으로 중첩 반복문을 완전히 벗어날 수 없다. 이때는 중첩 반복문 앞에 이름을 붙이고 break문과 continue문에 이름을 지정해줌으로써 하나 이상의 반복문을 벗어나거나 반복을 건너뛸 수 있다.
아래는 이름을 붙이는 예제다.
class FlowEx33
{
public static void main(String[] args)
{
Loop1 : for (int i = 2; i <= 9; i++) { // continue Loop1은 이 for문을 반복
for (int j = 1; j <= 9; j++){ // continue는 이 for문을 반복
if(j == 5)
break Loop1;
// break;
// continue Loop1;
// continue;
System.out.println(i + "*" + j + "=" + i * j);
} // break시 여기로 이동
System.out.println();
} // break Loop1시 여기로 이동
}
}
결과는 아래와 같다.
- break Loop1;을 실행했을 때(2*5가 출력되어야 하는데 이후 전부 종료됨)
- break;를 실행했을 때 (*5가 출력될 때 종료되고 다음으로 넘어감)
- continue Loop1;을 실행했을 때 (println()의 줄바꿈이 적용되지 않고, *5에서 출력이 멈춤)
- continue;를 실행했을 때 (*5의 출력을 제외하고 전부 출력됨)
Chapter 5. 배열(Array)
1. 배열(array)
배열(array)이란 "같은 타입"의 여러 변수를 하나의 묶음으로 다루는 것이다.
변수와 달리 배열은 각 저장공간이 연속적으로 배치되어 있다.
▼ 배열의 선언방법과 선언 예
선언방법 | 선언 예 |
타입[] 변수이름; | int[] score; String[] name; |
타입 변수이름[]; | int score[]; String name[]; |
배열을 선언한 다음에는 배열을 생성해야 한다. 배열을 생성하기 위해서는 연산자 'new'와 함께 배열의 타입과 길이를 지정해주어야 한다.
▼ 배열의 생성
타입[] 변수이름; // 배열을 선언 (배열을 다루기 위한 참조변수 선언)
변수이름 = new 타입[길이]; // 배열을 생성 (실제 저장공간을 생성)
// 실제 예시
int[] score;
score = new int[5];
// 선언과 생성을 동시에 하기
int[] score = new int[5];
[ 배열의 선언과 생성과정 ]
- int score[];
int형 배열 참조변수 score을 선언한다. 데이터를 저장할 수 있는 공간은 아직 마련되지 않았다. - score = new int[5];
연산자 'new'에 의해서 메모리의 빈 공간에 5개의 int형 데이터를 저장할 수 있는 공간이 마련된다.
그리고 각 배열 요소는 자동적으로 int의 기본값(default)인 0으로 초기화된다.
끝으로 대입 연산자 '='에 의해 배열의 주소가 int형 배열 참조변수 score에 저장된다.
생성된 배열의 각 저장공간을 '배열의 요소(element)'라고 하며, '배열이름[인덱스]'의 형식으로 배열의 요소에 접근한다. 인덱스(index)는 배열의 요소마다 붙여진 일련번호로 각 요소를 구별하는데 사용된다.
인덱스의 범위는 0부터 '배열길이-1'까지다.
배열을 다룰 때에는 이 index의 범위를 벗어난 값을 index로 사용하지 않아야 한다.
배열의 길이는 양의 정수여야 하며 최대값은 int타입의 최대값인 약 20억이다. 물론 배열의 길이가 0인 배열도 생성 가능하다.
이러한 배열의 길이는 '배열이름.length'를 통해 길이 정보를 얻을 수 있다.
배열은 한 번 생성하면 길이를 변경할 수 없기 때문에, 이미 생성된 배열의 길이는 변하지 않는다. 따라서 '배열이름.length'는 상수다.
[ 배열의 길이를 변경하는 방법 ]
- 더 큰 배열을 새로 생성한다. 이때 길이가 너무 길 경우 메모리가 낭비되므로 기존의 2배 정도의 길이로 생성한다.
- 기존 배열의 내용을 새로운 배열에 복사한다.
배열의 초기화 시 원하는 값으로 초기화를 하려면 각 요소마다 값을 지정해줘야 한다. 이때 생성과 초기화를 동시에 할 경우에는 new int[]의 생략이 가능하다.
// 방법 1
int[] score = new int[5];
score[0] = 50; // 각 요소에 직접 값을 저장
score[1] = 60;
score[2] = 70;
score[3] = 80;
score[4] = 90;
// 방법 2
int[] score = new int[5];
for (int i = 0; i < score.length; i++) {
score[i] = i * 10 + 50;
}
// 방법 3
int[] score = new int[] {50, 60, 70, 80, 90}; // 배열의 생성과 초기화를 동시에
// 방법 4
int[] score = {50, 60, 70, 80, 90}; // new int[]를 생략할 수 있음
// 아래는 주의 할 점
int score[];
score = new int[] {50, 60, 70, 80, 90}; // OK
score = {50, 60, 70, 80, 90}; // 에러. new int[]를 생략할 수 없음.
배열의 원소를 출력하는 방법에는 for문과 print()를 사용하는 방법도 있지만, 'Arrays.toString(배열이름)' 메서드를 사용하는 방법도 있다. 이 메서드는 배열의 모든 요소를 '[첫번째 요소, 두번째 요소, ...]'와 같은 형식의 문자열로 만들어서 반환한다.
import java.util.*;
int[] iArr = {100, 95, 80, 70, 60};
System.out.println(Arrays.toString(iArr));
// 이 경우 [100, 95, 80, 70, 60]이 출력된다.
이때 그냥 iArr을 출력할 경우, '타입@주소'가 출력된다.
또한 char배열은 println로 출력할 경우, 각 요소가 구분자 없이 그대로 출력된다. (이는 println 메서드가 char배열일 때만 이렇게 동작하도록 작성되었기 때문이다.)
[ 배열을 복사하는 방법 ]
- for문을 이용하는 경우
int[] arr = new int[5];
...
int[] tmp = new int[arr.length * 2]; // 기존 배열보다 길이가 2배인 배열 생성
for (int i = 0; i < arr.length; i++)
tmp[i] = arr[i];
arr = tmp; // 참조변수 arr이 새로운 배열을 가리키게 한다.
위와 같은 경우, 이전에 arr이 가리키던 길이 5의 배열은 더 이상 사용할 수 없게 된다.
- System.arraycopy()를 이용하는 경우
배열의 복사는 일반적으로 for문보다는 System.arraycopy()를 사용하는 것이 효율적이다. for문은 배열의 요소 하나하나에 접근해서 복사하지만, arraycopy()는 지정된 범위의 값들을 한 번에 통째로 복사한다. 각 요소들이 연속적으로 저장되어 있다는 배열의 특성 때문에 이렇게 처리하는 것이 가능하다.
// for문 복사
for (int i = 0; i < arr.length; i++)
tmp[i] = arr[i];
// arraycopy() 복사
System.arraycopy(arr, 0, tmp, arr.length); // arr[0]에서 tmp로 arr.length개의 데이터를 복사
[ 배열의 활용 ]
- 총합과 평균
for문으로 배열의 값을 모두 더하고, 배열의 개수로 나누어 총합과 평균을 구하는 코드를 요약하면 아래와 같다.
int sum = 0; // 총점을 저장하기 위한 변수
int average = 0; // 평균을 저장하기 위한 변수
int[] score = {100, 88, 100, 100, 90};
for (int i = 0; i < score.length; i++) {
sum += score[i]; // for문을 통해 배열의 총합을 구함
}
average = sum / float(score.length); // 계산 결과를 float로 얻기 위한 형변환
- 최대값과 최소값
for문을 통해 배열에 저장된 최대값과 최소값을 찾는 예제다. if문을 통해 배열 안에 있는 값과 최대값과 최소값을 저장한 변수를 비교하여 값을 구한다. 코드를 요약하면 아래와 같다.
int[] score = {79, 88, 91, 33, 100, 55, 95};
int min = score[0];
int max = score[0];
for (int i = 0; i < score.length; i++) {
if (score[i] > max) {
max = score[i];
} else if (score[i] < min) {
min = score[i];
}
}
- 섞기(shuffle)
Math.random() 메서드를 통해 임의의 위치에 있는 값과 배열의 첫 번째 요소를 교환한다. 이를 for문을 통해 100번 반복하여 배열이 잘 섞이도록 한다. 코드를 요약하면 아래와 같다.
int[] numArr = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
for (int i = 0; i < numArr.length; i++) {
int n = (int)(Math.random() * 10); // 0~9중의 한 값을 임의로 얻는다.
int tmp = numArr[i];
numArr[i] = numArr[n];
numArr[n] = tmp;
}
- 임의의 값으로 배열 채우기
Math.random() 메서드와 for문을 통해 배열을 임의의 값으로 채울 수 있다. 코드를 요약하면 아래와 같다.
import java.util.*;
int[] code = {-4, -1, 3, 6, 11};
int[] arr = new int[10];
for (int i = 0; i < arr.length; i++) {
int tmp = (int)(Math.random() * code.length);
arr[i] = code[tmp]; // code 배열에 있는 값으로 arr 배열을 임의로 채움
- 정렬하기(sort)
if문과 인덱싱을 통해 배열의 옆과 값을 비교하면서 배열을 정렬할 수 있다.
정렬(sort)에는 여러가지 방법이 있는데, 책에 나온 것은 인접한 원소를 비교한 뒤 자리를 교환하는 Bubble sort를 구현했다.
▶ sort의 종류
- Bubble Sort : 인접한 두 개의 원소를 비교하여 자리를 교환
- Selection Sort : 전체 원소 중 n번째로 작은 원소를 찾아 n번째와 교환
- Insertion Sort : 정렬되어 있는 부분집합에 새로운 원소의 위치를 삽입(집합 두 개의 원소를 하나씩 비교)
- Shell Sort : 자료를 부분집합으로 나눈 뒤 삽입 정렬(Insertion Sort)를 실행.
- Quick Sort : 기준(pivot)을 정한 뒤 해당 값을 기준으로 정렬
- Heap Sort : 최대 힙 트리나 최소 힙 트리를 구성하여 정렬
- Merge Sort : 부분집합으로 분할 한 뒤 각각의 집합을 정렬하고 결합하여 전체를 정렬
Bubble Sort를 요약하면 아래 코드와 같다. 아래 코드는 오름차순 정렬을 하는 방법이다.
int[] numArr = new int[10];
for (int i = 0; i < numArr.length; i++) {
numArr[i] = (int)(Math.random() * 10); // 배열을 0~9중 임의의 값으로 채움
}
// 정렬(sort) 시작
for (int i = 0; i < numArr.length - 1; i++) {
boolean changed = false; // 자리바꿈이 발생했는지를 체크한다.
for(int j = 0; j < numArr.length - 1 - i; j++) {
if (numArr[j] > numArr[j + 1]) { // 오른쪽의 값이 왼쪽보다 작으면 서로 바꾼다.
int tmp = numArr[j + 1];
numArr[j + 1] = numArr[j];
numArr[j] = tmp;
changed = true; // 자리바꿈이 발생했으니 changed를 true로
}
}
if (!changed) break; // 자리바꿈이 없으면 반복문을 벗어난다.
}
Bubble sort는 최대 '배열의 길이 - 1'만큼 정렬하면 완성되기 때문에 바깥의 for문의 반복은 '배열의 길이 - 1회'만큼 진행한다.
안쪽의 경우 오른쪽으로 최대값이 점점 정렬되기 때문에 비교할 값의 개수는 정렬을 반복할 수록 줄어든다. 따라서 안쪽 for문은 '배열의 길이 - 1 - i'번을 진행해주면 된다.
- 빈도수 구하기
빈도수를 저장할 배열을 만들고, for문을 통해 배열의 값을 세어 빈도수 배열에 저장하면 된다.
int numArr = new int[10];
int counter = new int[10];
for (int i = 0; i < numArr.length; i++) {
numArr[i] = (int)(Math.random() * 10);
} // numArr 배열을 0~9중 임의의 값으로 전부 채움
for (int i = 0; i < numArr.length; i++) {
counter[numArr[i]]++;
}
2. String 배열
배열의 타입이 String인 경우에도 일반 배열을 선언 및 초기화하는 방법과 동일하다. 아래 코드는 배열의 선언 및 초기화의 예시다.
// 각각 요소 넣기
String[] name = new String[3];
name[0] = "Kim";
name[1] = "Park";
name[2] = "Yi";
// 괄호{ }를 사용하여 초기화하기
String name[] = new String[] {"Kim", "Park", "Yi"};
String name[] = {"Kim", "Park", "Yi"}; // new String[] 생략 가능
책에 나와있는 16진수를 2진수로 바꾸는 예제를 보면 16진수를 char형 배열에 저장한다. 이때 16진수는 A가 10, B가 11, C가 12, D가 13, E가 14, F가 15이며 그 아래의 숫자는 10진수와 동일하다.
if (hex[i] >= '0' && hex[i] <= '9') {
result += binary[hex[i] - '0'];
}
위 코드는 책에 있는 예제 5-13의 일부인데, 이 부분은 바로 char형 배열에 있는 숫자가 0부터 9까지인지 확인하고, char형에는 현재 숫자가 아닌 유니코드 형태로 저장되어 있기 때문에 0~9 사이의 값이라면 binary라는 이름의 String 배열에서 해당 유니코드 값만큼 빼서 2진수를 출력하는 형태다.
따라서 그 외의 경우를 세는 else는 char형 배열 안의 원소가 A부터 F까지일 때이므로, 이때는 '0'이 아닌 'A'를 빼고, A의 값인 10만큼 추가로 더해주게 된다.
▼ 타입에 따른 변수의 기본값(default value)
자료형 | 기본값 |
boolean | false |
char | '\u0000' |
byte, short, int | 0 |
long | 0L |
float | 0.0f |
double | 0.0d 또는 0.0 |
참조형 변수 | null |
String클래스는 char 배열과 동일하다. JAVA에서는 char배열보다 String클래스를 이용하는데, 그 이유는 String클래스가 char배열에 여러 가지 기능(메서드)을 추가하여 확장한 것이기 때문이다.
다만 char배열은 내용을 수정할 수 있지만, String객체는 수정이 불가능하다.
▼ String클래스의 주요 메서드
메서드 | 설명 |
char charAt(int index) | 문자열에서 해당 위치(index)에 있는 문자를 반환한다. ex) String str = "ABCDE"; char ch = str.charAt(3); // ch에 'D'를 저장 |
int length() | 문자열의 길이를 반환한다. ex) arr.length |
String substring(int from, int to) | 문자열에서 해당 범위(from~to)에 있는 문자열을 반환한다. (to는 범위에 포함되지 않음) ex) String tmp = str.substring(1, 4); |
boolean equals(Object obj) | 문자열의 내용이 obj와 같은지 확인한다. 같으면 결과는 true, 다르면 false가 된다. ex) String str = "abc" if (str.equls("ABC")) |
char[] to CharArray() | 문자열을 문자배열(char[ ])로 변환해서 반환한다. ex) char[] chArr = {'A', 'B', 'C'}; String str = new String(chArr); char[] tmp = str.CharArray(); |
Scanner클래스의 nextLine() 이외에도 화면을 통해 사용자로부터 값을 입력받을 수 있는 방법이 있는데, 바로 커맨드 라인을 이용하는 것이다.
프로그램을 실행할 때 클래스 이름 뒤에 공백문자로 구분하여 여러 개의 문자열을 프로그램에 전달할 수 있다.
먼저 문자열을 전달받으면 문자열의 갯수와 출력을 반환하는 코드를 아래와 같이 짠다.
class sample
{
public static void main(String[] args)
{
System.out.println("Variable Number : " + args.length);
for(int i = 0; i < args.length; i++){
System.out.println("args[" + i + "] = \"" + args[i] + "\"");
}
}
}
이때 먼저 프로그램을 실행하기 전에 컴파일 과정을 거쳐야 하는데, javac를 쓸 때 한글이 있으면 컴파일 과정에서 오류가 발생하여 영어로 대체하였다.
이후 java sample 명령어를 통해 교재 p.212 페이지의 예제가 실행되는 것을 확인할 수 있었다.
만약 abc나 123을 입력하지 않는다면, 크기가 0인 배열이 생성되어 args.length의 값은 0이 된다. 입력이 없다고 해서 배열을 생성하지 않으면 해당 배열을 가리키는 참조변수 args 값이 null이 되고, 배열 args를 사용하는 모든 코드에서 오류가 나기 때문에 JVM은 입력이 없을 경우 길이가 0인 배열을 생성한다.
3. 다차원 배열
다차원 배열은 2차원 이상의 배열로, 메모리의 용량이 허용하는 한 차원의 제한은 없다.
주로 1, 2차원 배열이 사용되며, 그 이상의 n차원 배열도 어렵지 않게 다룰 수 있다. 이 교재에서는 2차원 배열에 대해 설명한다.
▼ 2차원 배열의 선언
선언 방법 | 선언 예 |
타입[][] 변수이름; | int[][] score; |
타입 변수이름[][]; | int score[][]; |
타입[] 변수이름[]; | int[] score[]; |
2차원 배열은 주로 테이블 형태의 데이터를 담는데 사용된다. 가로줄이 행(row), 세로줄이 열(column)이다. 2차원 배열의 요소에 접근하는 방식은 '배열이름[행index][열index]'이다.
2차원 배열의 초기화는 아래와 같다.
int[][] arr = new int[][] {{1, 2, 3}, {4, 5, 6}};
int[][] arr = {{1, 2, 3}, {4, 5, 6}}; // new int[][]가 생략됨
2차원 배열의 길이는 인덱스가 없을 때에는 행의 값을, 인덱스로 지정했을 때는 해당 행의 열의 값을 반환한다.
즉, 위에서 쓴 arr 배열의 arr.length는 2, arr[0].length는 3이 된다.
2차원 이상의 다차원 배열을 생성할 때, 전체 배열 차수 중 마지막 차수의 길이를 지정하지 않고, 추후에 각기 다른 길이의 배열을 갖도록 설정할 수 있다. 아래는 그 예시 코드다. 각 열은 다른 갯수의 행을 가지고 있으며, 이 경우 배열 참조 변수 score[i]는 각각 다른 길이의 배열을 참조하게 된다.
int[][] score = new int[5][];
score[0] = new int[4];
score[1] = new int[3];
score[2] = new int[2];
score[3] = new int[2];
score[4] = new int[3];
위의 경우 score.length의 값은 여전히 5지만, score[i].length의 경우 각각마다 다르다.
[ 다차원 배열의 활용 ]
- 좌표에 X표하기
교재 p.220-221에 나온 코드를 요약하자면 다음과 같다.
먼저 2차원 shipboard라는 이름의 byte 배열에 상대의 배 위치를 0과 1로 표시한다. 또한 행열을 표시할 수 있도록, board라는 2차원 배열을 만들어 행열의 숫자를 표기한다.
이후 x, y라는 이름의 int타입 변수에 사용자로부터 scanner의 nextLine() 메서드를 통해 배의 위치 좌표를 입력을 받는다. 이 결과값을 byte 배열의 위치와 비교하여 배가 있으면 board 배열에 O 표시를, 없으면 X표시를 한다.
board byte가 2차원 배열이기 때문에 2차원 배열의 활용과 관련이 있다.
- 빙고
5X5 크기의 빙고판에 차례로 1부터 25까지의 숫자를 저장한 다음, 사용자로부터 입력을 받아 일치하는 숫자가 있으면 빙고판에서 해당 숫자를 0으로 바꾼다.
- 행렬의 곱셈
수학에서 두 개의 행렬(matrix) m1과 m2가 있을 때 이 두 행렬을 곱한 결과를 출력하는 코드다. 기본적으로 두 행렬을 곱하려면 m1의 열의 길이와 m2의 행의 길이가 일치해야 한다. 두 값이 일치하면 결과로 나오는 매트릭스는 m1의 행의 길이와 m2의 열의 길이만큼의 크기를 가진다.
쉽게 말하면 3*2 행렬과 2*3의 행렬은 곱셈 가능하다. 밑줄친 안쪽 부분의 값이 동일하기 때문이다. 이때 결과값으로 나오는 행렬은 바깥에 있는 노란 형관펜으로 체크한 3*3 사이즈의 행렬이다.
따라서 배열에서는 앞쪽 다차원 배열의 열과 뒤쪽 다차원배열의 행을 곱한 값을 계산하여 결과 매트릭스에 저장하면 된다.
- 단어 맞추기
먼저 답이 되는 word라는 이름의 String 배열에 영어 단어와 한글 단어를 입력한다. 이후 사용자에게 앞쪽의 영어 단어의 뜻을 물어 입력한 값이 뒤쪽의 한글 단어와 일치하는지 확인하는 코드다.
'JAVA' 카테고리의 다른 글
Chapter 8, 9 요약 (1) | 2022.09.17 |
---|---|
Chapter 7 요약 (0) | 2022.09.01 |
Chapter 6 요약 (0) | 2022.08.26 |
Chapter 1, 2, 3 요약 (0) | 2022.08.12 |