4장. 제어문

4 minute read

목표

자바가 제공하는 제어문을 학습하세요. issue

학습할 것

선택문

기본적으로 if 이라는 키워드를 사용해서 진행하게 된다.

public static void ifStatement_1() {
    boolean itItTrue = true;
    if (itItTrue) {
        System.out.println("It is true");
    } else {
        System.out.println("It is false");
    }
}

if문 안에는 boolean결과가 들어가게 된다. 그 결과로 {}에 있는 결과가 실행된다. 만약 그렇지 않은 경우에는 else에 해당하는 구문이 실행된다.

if (1) {
    /* 
    Occured error!!
    Required type: boolean
    Provided: int
    */
}

c++을 사용했을 경우에는 저 if 문 안에 boolean 말고 다른 변수를 사용했어도 동작을 했었는데 자바에서는 반드시 boolean 값을 넣어야 한다.

if (a > 5) {
    System.out.println("a > 5");
} else if (b > 5) {
    System.out.println("a <= 5 && b > 5");
}

여기서 if else문에 조건문은 당연히 앞에 있는 a > 5가 아닌 친구들만 동작하게 된다. 한마디로 b > 5 && a <= 5인 친구들만 해당하는 친구들이 실행되는 것이다.

반목문

반복문은 forwhile을 이용한 두가지 방법이 있다.

while

while('boolean 변수') {
    // 계속 실행된다!!
}

'boolean 변수'에 해당하는 값이 true이게 되면 지속적으로 밑에 있는 값이 실행되게 된다. while같은 경우 기본적으로 boolean값을 바꿔주지 않은 경우에는 무한으로 동작하게 된다. (해당 부분은 나중에 큰 문제를 만들 수 있다.)

int number = 5;
while(number-- > 0) {
    System.out.println(number);
}

---

int number = 5;
while(true) {
    if(number < 0) {
        break;
    }

    number -= 1;
    System.out.println(number);
}

해당 모습처럼 어떤 조건에 해당 조건 값 (위에는 number가 0보다 작냐라는 조건) 것이 지속적으로 변경 되어야 한다. 그렇게 해서 확실하게 빠져나오도록 해야 한다.

for

위와 같이 while문은 지속적으로 해당 값을 계속 확인하는 로직이 필요하다. 그런 부분을 for은 선언과 함께 바로 정의할 수 있다. 밑에 예에서 보여주겠다.

for (int i = 0 ; i < 5 ; i++) {
    System.out.println(i);
}

위에는 숫자를 0~4까지 출력하는 코드이다. for문을 값을 선언할 수 있으며 그것에 대한 조건문과 값을 변경할 수 있는 공간이 존재한다. 해당 부분이 순서대로 동작하게 되면서 for문을 동작한다.

for (초기선언 ; 조건 ;  변경) {
    코드 실행부
}

순서: 초기 선언 -> 조건 확인 -> 코드 실행 -> 값 변경 -> 조건 -> 코드 실행 -> 값 변경 -> …

int k = 5;
for (; k < 10 ; k = 100) {
    System.out.println("k = " + k);
}

--- 
# console
k = 5

꼭 초기선언문을 해야되는 것이 아니고 값 변경 부분에서 +, - 로만 동작하는 것은 아니다. 자신이 원하는대로 조건문이든 값 변경을 할 수 있다. 그리고 초기 선언으로 만들어진 변수는 해당 블락 (for 문) 안에서 존재하고 for문에서 나오면 사용하지 못합니다.

for-each

자바에서 for문을 사용하는 것을 편하게 하기 위해서 만들어진 친구라고 생각하면 될 것 같습니다.

LinkedList<Integer> integerLinkedList = new LinkedList<>();

integerLinkedList.add(1);
integerLinkedList.add(2);
integerLinkedList.add(3);

for (Integer i :
        integerLinkedList) {
    System.out.println(i);
}

위에서 링크드리스트가 있따고 가정했을 때 해당 객체를 바로 받아서 사용할 수 있다. Integer i는 쭈욱 순환하면서 값을 하나씩 가져오게 된다. 그러기 때문에 for문에서 느끼는 코드가 길어지거나 실수할 수 있는 부분을 많이 줄여준다.

반복문 비교

LinkedList<Integer> integerLinkedList = new LinkedList<>();

integerLinkedList.add(1);
integerLinkedList.add(2);
integerLinkedList.add(3);

System.out.println("fori");
for (int i = 0; i < integerLinkedList.size(); i++) {
    System.out.println(integerLinkedList.get(i));
}

System.out.println("for-each");
for (Integer i :
        integerLinkedList) {
    System.out.println(i);
}

System.out.println("iter");
for (Iterator i = integerLinkedList.iterator(); i.hasNext(); ) {
    System.out.println(i.next());
}
--- console

fori
1
2
3

for-each
1
2
3

iter
1
2
3

위와 같이 똑같은 동작을 하는 것 처럼 보인다. 실제로 foreachiterator은 비슷한 동작을 하게 된다. 하지만 for-i을 이용한 경우에는 약간 다른 동작을 하게 된다.

ArrayList 같은 경우 랜덤 access가 가능하기 때문에 O(1)으로 모든 원소를 접근할 수 있다. 하지만 위와 같은 linkedList 같은 경우 랜덤 access가 불가능하기 때문에 원소를 찾는데 최대 O(N)에 시간이 걸리게 된다. foreachiterator는 순서대로 차근차근하나 접근하는 것이지만 for-i는 코드 영역을 접근할 때마다 값을 찾으러 연산을 진행해야된다.

for-i_vs_foreach

그러기 때문에 해당 데이터가 어떻게 저장되어 있는지, 자료구조가 어떤지에 따라서 for 문에 대한 연산이 달라질 수 있습니다.

array를 이용한 반복문

int[] integers = new int[4];
integers[0] = 1;
integers[1] = 2;
integers[2] = 3;

for (int i:
        integers) {
    System.out.println(i);
}

for (int i = 0; i < integers.length; i++) {
    System.out.println(integers[i]);
}

과제 코드

github

ref

iterator vs foreach

라이브 방송

  • add, ?, poll, remove
    • 이렇게 뭔가 똑같은 기능을 하는 것 같은데 두가지 있는 경우가 있다. 위에 경우에는 하나는 exception을 던지고 하나는 null을 던지다.
    • 이런 것처럼 api의 일관성에 있음. 코드를 짜면서 한번 생각을 해보세요
    • Consitancy
  • junit5은 public을 빼도 됨

  • junit5에서 @Nested라는 키워드로 계층적으로 할 수 있음. 내부의 내부 같은 구조?

  • replace conditional관련한 블로그
    • https://refactoring.guru/replace-conditional-with-polymorphism
    • https://www.baeldung.com/java-replace-if-statements
  • @TestInstance(PER_CLASS) 라는 키워드를 사용하면
    • @Test라는 키워드 친구마다 매번 똑같은 instacne를 만듬
public class Test{
    prviate int number;

    @Test
    void test1() {
        sout(number++);
    }

    @Test
    void test2() {
        sout(number++);
    }
}

--- 
0
0

이렇게 결과가 나오게 된다. 하지만 매번 인스턴스를 만들지 않고 라이프 사이클을 class로 관리해서 사용할 수 있다.

@TestInstance(TestIntance.Lifecycle.PER_CLASS)
public class Test{
    prviate int number;

    @Test
    void test1() {
        sout(number++);
    }

    @Test
    void test2() {
        sout(number++);
    }
}

--- 
0
1

이렇게 가능하다. 하지만 순서는 보장을 하지 않는다. 테스트 자체 알고리즘으로 순서가 정해지기 때문에 순서와 관려해서 문제가 발ㅇ새할 수 있다.

  • 테스트 코드를 미리 작성하는 경우: 어떤 기능을 구현해야 되는지에 대해서 명확한 경우. 테스트 통과에 따라서 기능 구현 완성이 좋아는 것이기 떄문에

  • 스팍 테스트 코드
    • 테스트용 라이브러리
    • given, when, then 키워드 제공
    • https://d2.naver.com/helloworld/568425
  • 자료구조를 사용하는데 있어서 공간복잡도 & 시간복잡도 따지는 것은 기본

  • 나는 어떤 엣지케이스, 추가적인 요구사항을 생각해서 개발을 진행하면 정말 같이 일하기 편할 것 같다. 조금 더 생각하고 계속 발전하도록 진행하면 정말 같이 일하고 싶다.
    • https://www.notion.so/4-c45c85fea0254d5d8fbc911d7f66c046
  • 반복문 없이 반복하는 방법 -> 재귀
    • 꼬리 재귀.
    • 시간 복잡도 O(N), 공간복잡도 O(N) -> 재귀에서는 콜 스택까지 공간복잡도 생각~~~
    • 자바는 꼬리 재귀 최적화가 안되어 있음.

베스트 글 모음

Leave a comment