Java/라이브스터디

제어문

목표

자바가 제공하는 제어문 학습하기.

학습목표

  • 선택문
  • 반복문

과제

  1. JUnit5로 테스트코드 작성하는 방법 익히기
  2. live-study 대시보드 만드는 코드 작성하기
  3. LinkedList 구현하기
  4. stack 구현하기
  5. ListNode로 stack 구현하기
  6. Queue 구현하기

 


 

선택문

여러개의 조건 중에서 부합하는 조건의 구문이 실행

if-then & if-then-else

if(/*조건*/){
    /* 조건이 True 일 경우 수행될 코드 */
}

조건은 반드시 boolean 타입의 반환을 해야하며, 해당 조건의 반환값이 True일 경우 블록 내부의 코드가 실행되는 방식으로 진행됩니다.

 

if(/*조건*/){
    /* 조건이 True 일 경우 수행될 코드 */
}else if(/*조건 2*/{
    /* 조건 2가 True일 경우 수행될 코드 */
}else{
    /* 아무조건도 충족시키지 못할 경우 수행될 코드 */
}

if-then구문에 else를 추가하여 조건을 연속으로 검사할 수 있습니다.
여러개의 조건 중 하나의 조건이 충족되면 해당 코드를 수행한 다음, 구문을 탈출합니다.

 

 

int speed = 5;
if(speed < 3){
    System.out.println("천천히 갑니다");
}else if(speed < 7){
    System.out.println("적당한 속도입니다.");
}
else{
    System.out.println("빠른 속도입니다.");
}

===== 출력 결과 =====

적당한 속도입니다.

하나의 조건안에는 논리적 AND와 OR 연산을 이용하여 여러개의 조건을 검사할 수 있습니다.
조건내에 || 또는 &&를 사용하여 여러개의 조건을 검사할 때, 선행되는 조건에서 이미 결과가 결정되면 후행조건은 실행되지 않습니다.

 

 

switch

자바 12버전 이전

switch(/*검사할 항목*/){
    case /*검사표본*/:
        /*코드*/
    case /*검사표본*/:
        /*코드*/
    case /*검사표본*/:
        /*코드*/
    default:
        /*코드*/
        break;
}

자바 12버전 이전의 switch의 경우 Stmt, 즉 구문입니다.
검사할 항목에 대하여 여러가지의 case를 이용하여 분기를 시키고, 특정 case에 부합하면 해당 case에 대한 코드를 실행합니다.
break가 명시되어있다면 switch문 자체를 탈출하지만, 그렇지 않을 경우 다음 case를 계속해서 검사합니다.

값의 반환은 불가능하며, 하나의 case에 여러개의 조건을 넣는 것 또한 불가능합니다.

 

 

자바 12버전 이후

switch(/*검사 항목*/){
    case /*검사표본*/,/*검사표본*/,/*검사표본*/ -> {
        /* 실행코드 */
    }
    case /*검사표본*/,/*검사표본*/,/*검사표본*/ -> {
        /* 실행코드 */
    }
    default ->{
        /* 실행 코드 */
    }
}

자바 12버전 이후에서의 switch는 case에 여러개의 검사표본을 달아줄 수 있게 되었습니다.

break를 달지않아도 특정 조건에 부합되면 해당 코드를 실행하고 swtich를 탈출합니다.

 

기존의 : 도 사용은 가능하지만, break를 사용하여 구문의 탈출을 명시해주어야합니다.
:와 ->를 섞어서 사용하는 것은 불가능합니다.

 

자바 13버전 이후

 

int a = 1;
String str =switch (a){
    case 1,2,3 ->{
        yield "123";
    }
    case 4,5,6 ->{
        yield "456";
    }
    default -> "7";
};
System.out.println(str);

=== 출력 결과 ===
123

자바 13버전에 들어와서는 구문보다는 연산자의 느낌이 짙어졌습니다.
yield를 통하여 값의 반환이 가능해졌으며, 코드가 길지않다면 반환될 값을 바로 지정하는 것도 가능해졌습니다.

 

 

반복문

특정 구문이 정해진 횟수만큼 반복해서 실행
주의할 사항으로는 무한루프가 생길 수 있습니다.

while과 do-while

while(/*조건*/){
    /* 반복 수행될 코드*/
}
int time = 10;
while(time > 0){
    System.out.println(time--);
}

== 출력 결과 ==
10
9
8
7
6
5
4
3
2
1

조건의 결과값이 true인 동안 계속해서 블록내의 코드를 반복하는 구문입니다.
주로 조건은 계속해서 값이 변화하는 변수를 사용하여 지정합니다.

 

조건에 true를 걸어줄 경우, 무한루프가 발생합니다.

 

do{

}while(/* 조건 */)

while문과 거의 동일하나 무조건 한번은 do블록이 실행됩니다.
이외의 부분은 모두 while문과 동일합니다.

 

 

int time = 10;
do {
    System.out.println(time--);
}while(time > 1000000);

== 출력 결과 ==
10

조건에 분명히 부합하지 않지만 1회 실행되는 모습을 보여줍니다.

 

 

for

for(/*초기 값*/; /*종료 조건*/; /*값의 증감*/){
    /*반복 수행될 코드*/
}
  • 초기 값 : for문의 첫 방문시에만 실행되는 선언문
  • 종료 조건 : for문의 블록이 한번 끝날때마다 체크되는 조건문
  • 값의 증감 : for문의 블록이 한번 끝날떄마다 실행되는 구문

각 값, 조건, 증감은 ;으로 구분합니다.

 

int[] arr = {1,2,3,4,5};

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

== 출력 결과 ==
1
2
3
4
5

 

인덱스를 통한 배열의 접근 시에 사용됩니다.

 

for(;;){

}

와 같이 선언 시에는 무한루프가 발생합니다.

 

 

for-each

for(/*참조변수*/ : /*참조 대상*/){
    /*반복 수행될 코드*/
}

향상된 for문이라고도 불리우며, 배열이나 ArrayList, Map, Set 등등에 접근 시에 인덱스를 사용하지 않고 접근할 때 사용합니다.

1회 반복 할때마다 참조대상의 맨 처음값부터 순서대로 참조변수에 들어갑니다.

 

 

분기문

break

int count = 10;

while(count > 0){
	break; // 바로 탈출함
	count--;
}

선택문이나 반복문의 특정 시점에서 탈출하고 싶을 때 사용됩니다.

 

continue

int count = 10;

while(count > 0){
	continue; //count가 변하지 않아서 무한루프 발생
	count--;
}

선택문이나 반복문의 특정 시점에서 코드의 실행을 멈추고 바로 반복조건으로 향하고 싶을 때 사용됩니다.

 

return

public void Method(){
	return;
}

메소드의 탈출을 위하여 사용됩니다.
메소드의 반환타입에 따라서 값을 반환하는 것이 가능합니다.

 

 


 

0. JUnit5 학습하기

자바용 단위 테스트 프레임워크 JUnit : JUnit Platform + JUnit Jupiter + JUnit Vintage

  • JUnit Platform
    • JVM에서 테스트 프레임워크를 시작하기 위한 기반역할.
    • TestEngine API를 정의
    • JUnit4 기반 Runner 제공.
  • JUnit Jupiter
    • 테스트코드 작성에 필요한 junit-jupiter-api 모듈
    • 테스트 실행을 위한 junit-jupiter-engine 모듈
  • JUnit Vintage
    • JUnit3 나 JUnit4 기반 테스트를 실행하기 위한 TestEngine 제공.

자바에서 테스트 코드를 작성하기 위한 프레임워크입니다.
테스트 코드란, 새롭게 구성한 코드가 제대로 작동하는지 확인하기 위하여 사용되는 코드를 의미합니다.

 

만일 이미 정상작동하는 거대한 로직의 코드에 특정 기능을 추가하는 상황에서 해당 코드가 제대로 동작하는지 확인하기 위하여 매번 전체 코드를 동작시키기는 번거롭기때문에 테스트 코드를 이용하면 새롭게 추가된 코드가 전체 코드내에서 조화롭게 작동하는지 확인이 가능합니다.

테스트 코드는 어노테이션을 통하여 구분할 수 있으며, 관련 어노테이션은 https://junit.org/junit5/docs/current/user-guide/#writing-tests-annotations에서 확인할 수 있습니다.

어노테이션 : 기능을 가진 주석

 

1. live-study 대시보드 만드는 코드 작성하기

  • 깃헙 이슈 1번부터 18번까지 댓글을 순회하며 댓글을 남긴 사용자를 체크 할 것.
  • 참여율을 계산하세요. 총 18회에 중에 몇 %를 참여했는지 소숫점 두자리가지 보여줄 것.
  • Github 자바 라이브러리를 사용하면 편리합니다.
  • 깃헙 API를 익명으로 호출하는데 제한이 있기 때문에 본인의 깃헙 프로젝트에 이슈를 만들고 테스트를 하시면 더 자주 테스트할 수 있습니다.
더보기
package com.example.live_study;

import org.kohsuke.github.*;

import java.io.IOException;
import java.util.*;

public class DrawingDashBoard {
    public static void main(String[] args) throws IOException {
        String token = "/ my git token /";
        GitHub git = new GitHubBuilder().withOAuthToken(token).build();
        // git 연결

        GHRepository repo = git.getRepository("whiteship/live-study");
        // repository 연결

        List<GHIssue> Issues = repo.getIssues(GHIssueState.ALL);
        // 이슈 목록 받아오기

        TreeMap<String, ArrayList<Integer>>UserList = new TreeMap<>();
        for (GHIssue item : Issues) {
            // 이슈 하나씩 받아오기.
            for (GHIssueComment reply : item.getComments()) {
                // 해당 이슈에 달려있는 댓글 체크
                ArrayList<Integer> list = new ArrayList<>();
                String LoginID = reply.getUser().getLogin();
                if(UserList.get(LoginID) != null){
                    list = UserList.get(LoginID);
                    UserList.remove(LoginID);
                }
                list.add(item.getNumber());
                UserList.put(LoginID, list);
            }
        }

        System.out.println("|참여자(총원:" + UserList.size() + ")|1주차|2주차|3주차|4주차|5주차|6주차|7주차|8주차|9주차|10주차|11주차|12주차|13주차|14주차|15주차|16주차|17주차|18주차|참여율(%)|");
        System.out.println("|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|");
        Iterator<String> UserIter = UserList.keySet().iterator();
        while(UserIter.hasNext()){
            String ID = UserIter.next();
            ArrayList<Integer> list = UserList.get(ID);
            Collections.sort(list);
            double per = ((double)list.size()/18)*100;

            int idx = 0;
            int checked = list.get(idx++);
            System.out.print("|"+ID+"|");
            for (int i = 1; i <= 18; i++) {
                if(i == checked){
                    System.out.print(":white_check_mark:|");
                    if(idx < list.size())
                        checked = list.get(idx++);
                }else{
                    System.out.print("|");
                }
            }
            System.out.println(String.format("%.2f", per) + "%|");
        }

        System.out.println();

    }
}
//:white_check_mark:|

 

2. LinkedList 구현하기

  • LinkedList에 대해 공부하세요.
  • 정수를 저장하는 ListNode 클래스를 구현하세요.
  • ListNode add(ListNode head, ListNode nodeToAdd, int position)를 구현하세요.
  • ListNode remove(ListNode head, int positionToRemove)를 구현하세요.
  • boolean contains(ListNode head, ListNode nodeTocheck)를 구현하세요.

 

 

연결 리스트

  • 기본 구조
    • 현재 가지고 있는 값
    • 다음 노드의 주소 값

특징

연결리스트의 어느 위치에서라도 삭제나 삽입이 단일시간내에 가능합니다.
Head노드는 첫번째 노드를 가르키는 필드만 존재함.

단일 연결리스트

  • 단방향 연결리스트.
  • 한쪽 방향으로만 나아감.

이중 연결리스트

  • 양뱡향 연결리스트.
  • 자기자신의 이전과 이후의 노드를 기억.

원형 연결리스트

  • 단일리스트의 마지막 노드가 맨 처음 노드를 가리킴.

구현 계획

  • Head노드를 0번 인덱스로 잡는다.
  • Head노드의 value에 현재 연결 리스트 내의 원소 개수를 적는다.
더보기
package LinkedList;

public class ListNode {
    int value;
    ListNode next;

    ListNode(){
        this.value = 0;
        this.next = null;
    }

    ListNode(int value){
        this.value = value;
        this.next = null;
    }

    ListNode add(ListNode head, ListNode nodeToAdd, int position){
        if(position <= 0 || position > head.value + 1){
            System.out.println("Out of range");
        }else{
            ListNode Seeker = head;
            while(--position > 0){
                Seeker = Seeker.next;
            }
            // 삽입 위치까지 이동

            nodeToAdd.next = Seeker.next;
            Seeker.next = nodeToAdd;
            head.value++;
        }

        return head;
    }

    ListNode remove(ListNode head, int positionToRemove){
        if(positionToRemove <= 0 || positionToRemove > head.value){
            System.out.println("Out of range");
        }else{
            ListNode Seeker = head;
            while(positionToRemove-- > 1){
                Seeker = Seeker.next;
            }

            Seeker.next = Seeker.next.next;
            head.value--;
        }

        return head;
    }

    boolean contains(ListNode head, ListNode nodeTocheck){
        if(head.value > 0){
            ListNode Seeker = head;

            do{
                Seeker = Seeker.next;
                if(Seeker.value == nodeTocheck.value){
                    return true;
                }
            }while(Seeker.next != null);
        }

        return false;
    }
}

구현

 

package LinkedList;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class ListNodeTest {

    ListNode Head = new ListNode();

    @BeforeEach
    void Setting(){
        Head = new ListNode();

        Head = Head.add(Head, new ListNode(10), 1);
        Head = Head.add(Head, new ListNode(4), 1);
        Head = Head.add(Head, new ListNode(32), 1);
        Head = Head.add(Head, new ListNode(12), 1);
    }
    
    @Test
    void add() {
        Head = Head.next;
        assertEquals(12, Head.value);

        Head = Head.next;
        assertEquals(32,Head.value);

        Head = Head.next;
        assertEquals(4,Head.value);

        Head = Head.next;
        assertEquals(10,Head.value);
    }

    @Test
    void remove() {
        Head = Head.remove(Head,1);
        assertEquals(32, Head.next.value);

        Head = Head.remove(Head, 2);
        assertEquals(32, Head.next.value);

        Head = Head.remove(Head, 1);
        assertEquals(10, Head.next.value);
    }

    @Test
    void contains() {
        assertEquals(true, Head.contains(Head,new ListNode(32)));
        assertSame(false, Head.contains(Head, new ListNode(1)));
    }
}

테스트코드 맛보기

 

 

3. stack 구현하기

  • int 배열을 사용해서 정수를 저장하는 Stack을 구현하세요.
  • void push(int data)를 구현하세요.
  • int pop()을 구현하세요.

Stack

선입 후출 ( First in - Last out ) 구조의 자료구조.

데이터의 삽입과 반출 모두 맨 끝에서 일어난다.

구현 계획

  • 사용자의 정의에 따라 스택 사이즈를 지정
  • 사용자의 정의가 없으면 스택 사이즈는 10
  • 현재 스택에 들어있는 원소 개수를 가르키는 back_idx 변수 사용
더보기
package MyStack;

public class STACK {
    int[] arr;
    int back_idx;
    STACK(){
        arr = new int[10];
        back_idx = 0;
    }

    STACK(int value){
        arr = new int[value];
        back_idx = 0;
    }

    void push(int value){
        if(back_idx > arr.length){
            System.out.println("Out of range");
            return;
        }
        arr[back_idx++] = value;
    }

    int pop(){
        if(back_idx == 0){
            System.out.println("Out of range");
            return Integer.MIN_VALUE;
        }
        return arr[--back_idx];
    }
}

구현

 

 

package MyStack;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Qualifier;

import static org.junit.jupiter.api.Assertions.*;

class STACKTest {

    STACK mystack;

    @BeforeEach
    void setting(){
        mystack = new STACK(4);

        mystack.push(14);
        mystack.push(12);
        mystack.push(31);
        mystack.push(51);
    }


    @Test
    void pop(){
        assertEquals(51, mystack.pop());
        assertEquals(31, mystack.pop());
        assertEquals(12, mystack.pop());
        assertEquals(14, mystack.pop());
    }
}

테스트 코드

 

4. ListNode로 stack 구현하기

  • ListNode head를 가지고 있는 ListNodeStack 클래스를 구현하세요.
  • void push(int data)를 구현하세요.
  • int pop()을 구현하세요.
더보기
package LinkedList;

public class ListNodeStack {
    ListNode Head = new ListNode();

    void push(int data){
        Head = Head.add(Head, new ListNode(data), Head.value + 1);
    }

    int pop(){
        ListNode Seeker = this.Head;
        if(Seeker.next == null){
            System.out.println("Out of range");
            return Integer.MIN_VALUE;
        }

        while(Seeker.next.next != null){
            Seeker = Seeker.next;
        }

        int ret = Seeker.next.value;
        Seeker.next = null;
        return ret;
    }
}

구현

 

package LinkedList;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class MainTest {
    ListNodeStack LNS;

    @BeforeEach
    public void Setting(){
        LNS = new ListNodeStack();

        LNS.push(41);
        LNS.push(421);
        LNS.push(531);
        LNS.push(1);
        LNS.push(-20);
    }

    @Test
    public void pop(){
        assertEquals(-20, LNS.pop());
        assertEquals(1, LNS.pop());
        assertEquals(531, LNS.pop());
        assertEquals(421, LNS.pop());
        assertEquals(41, LNS.pop());
        assertEquals(Integer.MIN_VALUE, LNS.pop());
    }
}

테스트 코드

 

5. Queue 구현하기

  • 배열을 사용해서 한번
  • ListNode를 사용해서 한번.

Queue

선입 선출 ( First In - First Out ) 구조의 자료구조.

데이터의 삽입은 맨 뒤에서 일어나고 데이터의 반출은 맨 앞에서 일어난다.

 

더보기
package MyQueue;

public class QUEUE {
    int[] arr;
    int back_idx;
    QUEUE(){
        this.arr = new int[10];
        back_idx = 0;
    }

    QUEUE(int size){
        this.arr = new int[size];
        back_idx = 0;
    }

    void push(int data){
        if(back_idx >= arr.length){
            System.out.println("Out of range");
            return;
        }

        arr[back_idx++] = data;
    }

    int pop(){
        int ret = arr[0];

        for (int i = 0; i < back_idx - 1; i++) {
            arr[i] = arr[i+1];
        }
        back_idx--;
        return ret;
    }
}

배열로 구현

 

package MyQueue;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class MainTest {
    @Test
    public void mainTEST(){
        QUEUE q = new QUEUE(2);
        q.push(3);
        q.push(-1);

        assertEquals(3, q.pop());
        assertEquals(-1, q.pop());

    }
}

테스트코드

 

 

 

package LinkedList;

public class ListNodeQueue {
    ListNode Head = new ListNode();

    void push(int data){
        Head = Head.add(Head, new ListNode(data), Head.value + 1);
    }

    int pop(){
        if(Head.next == null){
            return Integer.MIN_VALUE;
        }
        ListNode Seeker = Head.next;
        int ret = Seeker.value;
        Head.next = Seeker.next;

        return ret;
    }
}

ListNode로 구현

 

package LinkedList;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class ListNodeQueueTest {


    ListNodeQueue LNQ;
    @BeforeEach
    void Setting() {
        LNQ = new ListNodeQueue();

        LNQ.push(1);
        LNQ.push(-121);
        LNQ.push(14);
        LNQ.push(511);
        LNQ.push(23);
    }

    @Test
    void push() {
        assertEquals(true, LNQ.Head.contains(LNQ.Head,new ListNode(-121)));
        assertEquals(true, LNQ.Head.contains(LNQ.Head,new ListNode(14)));
        assertEquals(false, LNQ.Head.contains(LNQ.Head,new ListNode(-1)));
    }

    @Test
    void pop() {
        assertEquals(1, LNQ.pop());
        assertEquals(-121, LNQ.pop());
        assertEquals(14, LNQ.pop());
        assertEquals(511, LNQ.pop());
        assertEquals(23, LNQ.pop());
    }
}

테스트코드

 

참고 자료