17 min read

<Java>반복문

<Java>반복문
  • 지난 시간에 이어 제어 흐름의 두번째 문법 반복문에 대해 알아봅시다.
  • 말 그대로 동일한 코드를 조건에 따라 n회 반복하는 역할을 수행합니다.
  • 주어진 조건을 만족할 경우, 주어진 코드들은 반복하기에 조건식이 포함되며, if문과 마찬가지로 조건식의 결과가 true이면 참이고, false면 거짓입니다.
  • 또한 지금부터 배울 for와 while은 서로 상호 변환이 가능합니다.

for 반복문

  • for 반복문의 경우, 반복 횟수를 명확히 알고 있을 때 유리합니다.추후 다룰while에 비해 구조는 복잡하지만, 코드자체는 더욱 직관적입니다.아래의 코드를 보며 설명드리겠습니다.
for(int i = 1; i <= 5; i++){
	System.out.println("for loop");
}
  • 뭔가 직관적으로 “for loop”라는 문장을 5번 출력할것 같지 않나요? 실제로 초기화된 i값이 조건을 만족할 경우 증가하는 횟수만큼 반복됩니다. 1에서 2로 3에서 4로 그리고 5까지 i값이 증가할 때 마다 for문 블럭 내의 코드가 실행됩니다.
  • for문의 구조를 일반화 시키면 아래와 같이 정의할 수 있습니다.
for(초기화;조건식;증감식){
	//block 내 수행될 문장
}
//for문 탈출시 진행되는 Line => 조건식이 false일 때
  • 제일 먼저 초기화 코드가 실행되며 그 이후부터는 조건식이 참인 경우, 블럭 내의 코드를 수행하고 증감식을 실행시킵니다.반대로 조건식이 거짓인 경우, for 블럭을 탈출하여, 블럭 바로 아래 코드로 빠져나갑니다.
  • 초기화 코드의 경우 반복문에 사용될 변수를 초기화 하는 부분이며 처음에 한번만 수행됩니다. 보통 변수 한개로 for문을 제어하지만, 둘 이상의 변수가 필요한 경우에 두가지 변수를 서로 구별하며 초기화할 수 있습니다.
  • 조건식 코드는 결과가 true면 반복을 계속 진행하며, false인 경우에는 반복을 중단하고 for문을 빠져나갑니다.조건식에 조작을 가해 무한반복을 진행하는 코드를 짤 수도 있으니 조심해야합니다.
  • 증감식 코드는 앞서 초기화 코드에서 선언한 변수의 값을 증가 또는 감소시킵니다.반복이 진행될때마다 변수의 값은 점진적으로 변하다가 결국 조건식의 결과에 false가 되는 값이 되면 for을 벗어납니다.주로 ++ 또는 --를 사용합니다.
  • 만약 세가지 부분 모두 생략된 경우는 조건식이 항상 참인경우로 무한 반복을 진행합니다.
  • 지금까지 For 반복의 개념적인 내용을 설명했습니다.이제 아래 예제를 따라하며 이해를 하면 됩니다.
public class FlowEx12 {
    public static void main(String[] args) {
        for (int i = 1; i <= 5; i++) {
            System.out.println(i);
        }
        for (int i = 1; i <= 5; i++) {
            System.out.print(i);
        }
    }
}
  • i의 값을 세로로 한번, 가로로 한번 출력하는 예제입니다.아래의 표와 같이 i값이 증가함에 따라 조건식의 값이 true면 블럭 내의 코드를 실행합니다.i값의 6인경우 조건식에 위배되기에 for 반복문을 빠져나옵니다.
public class FlowEx13 {
    public static void main(String[] args) {
        int sum = 0;

        for (int i = 1; i <= 10; i++) {
            sum += i;
            System.out.printf("1부터 %d까지의 합 : %d \n",i,sum);
        }
    }
}
  • 위 예제의 경우, sum이라는 변수에 증가하는 i의 값을 누적시켜 합을 구하는 예제입니다.1부터 i까지의 합을 변수 i가 증가할때마다 출력해줍니다.
public class FlowEx14 {
    public static void main(String[] args) {
        System.out.println("I \t J");
        for (int i = 1, j = 10; i <= 10; i++,j--) {
            System.out.printf("%d \t %d \n",i,j);
        }
    }
}
  • 이번 예제는 조금 특이한 경우입니다.for문에 사용될 변수의 갯수가 2개인 case입니다.i와 j를 각각 초기화 부분에 적어주며, 이후에 조건식을 채워준뒤, 증감식에 i와 j 모두에게 증가 또는 감소를 진행시켜줍니다.
  • 여러개의 변수가 필요한 For문은 자주 사용되는 구조는 아니지만 알고 있으면 도움이 됩니다.

중첩 for문

  • if문을 중첩 시킬 수 있는 것처럼 for문도 중첩을 시킬 수 있습니다.즉, 하나의 for문안에 또 다른 for문을 넣는것이라고 생각하면 됩니다.
  • 중첩 for문의 대표적인 예가 별찍기 예제들입니다.예제를 통해 이해해봅시다.
public class FlowEx16 {
    public static void main(String[] args) {
        for (int i = 1; i <= 5; i++) {
            for (int j = 1; j <= 10; j++) {
                System.out.print("*");
            }
            System.out.println();
        }
    }
}
  • 코드를 따라 친후 결과를 한번 예상해보고 실행해봅시다.
  • 아마 (5 by 10) 크기의 별로 이루어진 직사각형이 출력될 것입니다.
  • 설명을 조금 보태면, 변수 i로 이루어진 for문의 경우 총 5번의 반복을 진행하고 변수 j로 이루어진 내부 for문은 총 10번의 반복을 수행합니다. 직사각형의 크기와 이러한 분석을 합쳐보면 세로로의 반복이 외부 for문, 가로로의 반복이 내부 for문이라는 것을 알 수 있습니다.결론적으로 세로의 길이가 5번인 것은 외부 for의 5회 반복에 의해 생성되었고 가로의 길이는 내부 for문에 의해 생성됨을 알수 있습니다.
  • 다음으로 계단식 별찍기 예제를 진행해보겠습니다.
public class FlowEx17 {
    public static void main(String[] args) {
        int num = 0;

        System.out.print("*을 출력할 라인의 수 입력 : ");
        Scanner sc = new Scanner(System.in);
        num = sc.nextInt();
        for (int i = 0; i < num; i++) {
            for (int j = 0; j <= i; j++) {
                System.out.print("*");
            }
            System.out.println();
        }

    }
}
  • 직사각형 별 찍기 예제와 달라진 점은 내부 For문의 조건식 부분이 변경된 것 뿐입니다.조금 더 설명을 보태보겠습니다.입력으로 들어온 num이 3이라는 가정하에, 외부 for문의 i값의 0이면 조건식을 만족하기에 내부 for문으로 들어갑니다. 내부 for문의 j=0인 상황이면 조건식 0 <= 0를 만족하기에 별 한개를 출력합니다.이후, 증감식 j++가 수행되며, 조건식 1 <= 0으로 결과가 false이기에 내부 for문을 빠져나와 개행을 위한 println을 수행합니다.여기까지가 첫줄의 별 한개가 찍히는 과정을 서술했습니다.이후 동일한 메커니즘으로 두번째줄에 별 2개, 세번째 줄은 3개순으로 출력됩니다.
  • 해당 예제도 직사각형 예제와 마찬가지로 세로의 길이를 외부 for문이 결정하고 가로의 길이를 내부 for문이 결정한다고 생각하면 이해하는데 수월할 것입니다.
  • 사실 필자의 경우에도 처음 이중 For문의 별찍기를 접했을 때, 이해가 가지 않았습니다.이러한 분들의 경우 A4용지 하나를 꺼내고, i와 j값을 일일히 증가시켜보며 별을 실제로 그려보면 직관적으로 이해하실 수 있을 것입니다.
public class FlowEx20 {
    public static void main(String[] args) {
        for (int i = 1; i <= 5; i++) {
            for (int j = 1; j <= 5; j++) {
                System.out.printf("[%d, %d] ", i ,j);
            }
            System.out.println();
        }
    }
}
  • 위 예제의 경우 아래와 같은 결과를 출력합니다.
  • 이중 For 문을 완벽히 이해하기 위한 마지막 문제를 하나 드리겠습니다.
  • 위의 결과를 아래와 같이 주황색 박스가 쳐진 곳만 출력하게 해봅시다.해당 문제를 수월하게 해결하시면 이중 For문을 이해했다고 봐도 무방합니다. 정답의 경우 깃헙에 올려놓을테니 참고하시면 됩니다.

Enhanced For statement(향상된 for문)

  • 처음 for문을 배우시는 분들에게 해당 문법은 어색하게 느껴질 수도 있습니다.하지만 실제 개발이나 코딩테스트를 풀 경우 이러한 방법으로 for문을 자주 사용합니다.
  • 그러기에 초심자의 경우 기억은 하고 있는 정도로 봐주시면 될것 같습니다.
  • 예제를 통해 바로 보겠습니다.
public class FlowEx22 {
    public static void main(String[] args) {
        int[] arr = {10, 20, 30, 40, 50};
        int sum = 0;

        for (int i = 0; i < arr.length; i++) {
            System.out.printf("%d ",arr[i]);
        }
        System.out.println();
        for (int i : arr) {
            System.out.printf("%d ",i);
            sum += i;
        }
        System.out.println();
        System.out.println("sum = " + sum);
    }
}
  • 두가지 for문 모두 배열 arr의 요소 하나하나 접근하여 출력합니다.
  • 두번째 for문인 enhanced for의 경우 arr이라는 배열안의 모든 요소를 접근 할때 사용하면 효율적입니다.즉, 배열 또는 컬렉션의 모든 요소에 접근하여 읽을 경우에는 이러한 구조를 사용하면 좋습니다.
  • 배열과 컬렉션의 경우 특정한 datatype의 변수들을 모아둔 공간이라 생각하시면 됩니다.

while 반복문

  • 앞서 배운 for문에 비해 while문은 구조가 간단합니다.if문처럼 조건식과 블럭으로만 이루어져 있습니다.만약 while문의 조건이 참인 경우에는 블럭 내의 코드를 계속 반복합니다.즉, 조건이 거짓이 되어야 해당 while문의 블럭을 빠져나올 수 있다는 뜻입니다.
  • 다시한번 정리해보겠습니다. while문의 경우 조건식이 참이면 블럭 내의 코드를 수행하러 들어가고, 거짓인 경우에만 while문을 벗어납니다.
  • 조건식이 참인 경우, 블럭 내의 코드를 전부 수행하고나서 다시 조건식을 검사합니다.해당 과정에서 또 다시 조건식이 참이면 블럭 내의 코드를 반복실행하고 해당 코드가 끝이 난 경우 다시 한번 조건식 검사를 진행합니다.이러한 과정을 반복하며 조건식이 거짓인 경우가 생기면 while문을 빠져나올 수 있습니다.
  • while의 조건식은 for문과 다르게 생략이 불가합니다.for문처럼 생략을 통해 무한반복을 진행하려면, while(true){}처럼 항상 참인 값을 조건식에 넣어주면 됩니다.
  • 이제 예제를 통해 while문을 이해해봅시다.
public class FlowEx23 {
    public static void main(String[] args) {
        int i = 5;

        while (i-- != 0) {
            System.out.println("i = " + i);
        }
    }
}
  • 변수 i의 값만큼 해당 블럭의 값을 반복합니다.시작하는 i의 값이 5이니 조건식을 만족합니다.만약 여기서 i-- 후위형의 연산이 제대로 이해가 되지 않으신 분들은 연산자 파트로 가셔서 다시 한번 보시고 오시길 바랍니다.
  • 만약 해당 예제가 이해가 된다면 후위형 연산을 전위형 연산으로 변경했을 경우를 한번 생각해보시는 것도 while문을 이해하는데 도움이 될 것입니다.
public class FlowEx25 {
    public static void main(String[] args) {
        int num = 0, sum = 0;
        System.out.print("숫자를 입력하세요 : ");
        Scanner sc = new Scanner(System.in);
        String input = sc.nextLine();
        num = Integer.parseInt(input);
        while (num != 0) {
            sum += num % 10;
            System.out.printf("sum=%3d num=%d\n", sum, num);

            num /= 10;
        }
        System.out.println("각 자리수의 합 : " + sum);
    }
} 
  • 사용자로부터 숫자를 입력받고, 이 숫자의 각 자리의 합을 구하는 예제입니다.
  • 어떤 수를 10으로 나머지 연산하면 마지막 자리 숫자를 얻을 수 있습니다.해당 값을 sum이라는 변수에 누적시켜 각 자리의 합을 구하면 됩니다.
  • 예를 들어 1234를 입력 할 경우 10이 출력됩니다.
public class FlowEx27 {
    public static void main(String[] args) {
        boolean flag = true;
        int sum = 0;
        Scanner sc = new Scanner(System.in);
        System.out.println("합계를 구할 숫자들을 입력하세요(끝내려면 0 입력) : ");

        while (flag) {
            int i = sc.nextInt();
            if (i == 0) {
                flag = false;
            } else {
                sum += i;
            }
        }
        System.out.println("sum = " + sum);
    }
}
  • 위 예제의 경우 숫자들을 입력하고 나서 해당 숫자들의 합을 구해주는 프로그램입니다.
  • flag라는 boolean타입의 변수를 사용하여 숫자 입력을 제어합니다.
  • 만약 0을 입력하게 되면, 해당 값이 0이면 flag값을 false로 변경시키기에 더 이상 숫자입력을 위한 반복을 하지 않고, sum 값을 출력합니다.

do-while 반복문

  • do-while문은 while문의 변형으로 기본적인 구조는 while과 같으나 조건식과 블럭 순서를 바꿔놓은 형태입니다.
  • 쉽게 말하면 조건 검사를 하기전에 블럭 내 코드를 한번 실행하고나서 조건검사를 진행합니다.즉, 블럭 내 코드가 무조건 최소 한번은 실행되는것입니다.
  • 아래의 예제를 통해 쉽게 이해할 수 있습니다.
public class FlowEx28 {
    public static void main(String[] args) {
        int input = 0, answer = 0;

        answer = (int) (Math.random() * 100) + 1;
        Scanner sc = new Scanner(System.in);

        do {
            System.out.print("1과 100 사이의 정수를 입력하시오 : ");
            input = sc.nextInt();

            if (input > answer) {
                System.out.println("Down");
            } else if (input < answer) {
                System.out.println("Up");
            }
        } while (input != answer);

        System.out.println("정답입니다.");
    }
}
  • 다들 잘 아시는 Up & Down 프로그램을 구현한 예제입니다.최소 한번의 숫자입력을 받은 뒤 해당 숫자가 answer라는 변수와 일치하지 않으면 다시 블럭 내 코드를 반복합니다.이러한 과정은 while문의 조건검사가 false, 즉 input == answer인 경우에 종료되어 반복문을 빠져나옵니다.

break문

  • 조건문의 switch문을 배울 때 언급됬었던 내용입니다. switch문에서 역할과 동일한 역할을 수행합니다.즉, 반복수행을 멈춰주는 역할을 합니다. 다만 break문 기준 자신이 포함된 가장 가까운 반목문을 벗어납니다.주로 if문의 블럭 내에 사용되어 특정 조건을 만족할 경우,반복문을 벗어날 수 있도록 합니다.
  • 사실 FlowEx27의 flag 변수를 사용하는 것과 동일한 역할을 한다고 봐도 무방합니다.
public class FlowEx30 {
    public static void main(String[] args) {
        int sum = 0;
        int i = 0;

        while (true) {
            if (sum > 100) {
                break;
            }
            ++i;
            sum += i;
        }

		/*
        while (sum <= 100) {
            ++i;
            sum += i;
        }
        */

        System.out.println("i = " + i);
        System.out.println("sum = " + sum);
    }
}
  • 실제로 주석 처리 되지 않은 while문과 주석 처리된 while문의 경우 동일한 기능을 수행합니다.

continue문

  • continue문은 반복문 내에서만 사용될 수 있으며, 반복이 진행되는 도중에 continue문을 만나면 해당 변수에 해당하는 코드 블럭을 생략하고 다음 번 반복으로 넘어갑니다. for문의 경우 증감식으로 이동하고, while문의 경우 조건식으로 이동합니다.
  • 이러한 문법의 경우 특정조건을 만족하는 경우를 제외하고 반복을 시키고자 할 때 유용합니다.
public class FlowEx31 {
    public static void main(String[] args) {
        for (int i = 0; i <= 10; i++) {
            if (i % 3 == 0) {
                continue;
            }
            System.out.println("i = " + i);
        }
    }
}
  • 위 예제의 경우 3의 배수일 경우, i값을 출력하지 않도록 하는 프로그램입니다.
  • 이처럼 특정한 조건을 제외하고 반복을 진행할 때 continue가 유용합니다.
public class FlowEx34 {
    public static void main(String[] args) {
        int menu = 0;
        int num = 0;

        Scanner sc = new Scanner(System.in);
        outer:
        while (true) {
            System.out.println("(1) 제곱 연산하기");
            System.out.println("(2) 루트 연산하기");
            System.out.println("(3) 로그 연산하기");
            System.out.print("원하는 메뉴를 선택하세요(종료는 0) : ");

            menu = sc.nextInt();

            if (menu == 0) {
                System.out.println("프로그램을 종료합니다.");
                break;
            } else if (!(1 <= menu && menu <= 3)) {
                System.out.println("잘못된 입력입니다. 숫자를 다시 입력해주세요.");
                continue;
            }
            System.out.println("선택한 menu는 (" + menu + ")입니다.");

            while (true) {
                System.out.println("계산할 값을 입력하세요(계산 종료는 0, 전체 종료는 99) : ");
                num = sc.nextInt();

                if (num == 0) {
                    break;
                }
                if (num == 99) {
                    break outer;
                }
                switch (menu) {
                    case 1:
                        System.out.println("result = " + num * num);
                        break;
                    case 2:
                        System.out.println("result = " + Math.sqrt(num));
                        break;
                    case 3:
                        System.out.println("result = " + Math.log(num));
                        break;
                }
            }
        }
    }
}
  • 마지막 예제입니다. outer:라고 적힌 부분의 경우 해당 반복문에 이름을 붙여 주는 문법입니다.이름을 붙이면 break문 사용시 탈출할 반복문을 명시적으로 지정해 줄 수 있기 때문에 사용됩니다.
  • 해당 예제는 제곱,루트,로그 계산을 진행해주는 계산기 프로그램입니다.무한 반복을 통해 사용자가 직접 종료하기 전까지는 연산을 멈추지 않습니다.
  • 마지막으로 이번 예제를 진행하며 반복문에 대해 숙달합시다.

Ref: 자바의 정석 - 남궁성