자바가 제공하는 제어문을 학습하기
: 조건식의 연산 결과에 따라 실행할 문장이 달라져서, 조건에 따라 프로그램의 실행 흐름을 변경할 수 있다.
boolean
타입이어야 한다.: 가장 기본적인 조건문
if(조건식1) {
// 조건식1이 true일 때, 실행될 문장들
}else if(조건식2) {
// 조건식1이 false이고 조건식2가 true일 때, 실행될 문장들
}else {
// 조건식1과 조건식2 모두 false일 때, 실행될 문장들
}
: if문에서 조건에 따라 분기해야 할 내용이 많아졌을 때 사용한다.
switch(조건식) {
case 값1:
// 조건식 == 값1 일 때, 실행될 문장들
break; // switch문을 벗어난다.
case 값2:
// 조건식 == 값2 일 때, 실행될 문장들
break;
default:
// 조건식의 결과와 일치하는 case문이 없을 때, 실행될 문장들
}
자바 12 버전부터 switch
문에 대해 바뀐 부분이 있어 이에 대해 알아보자.
기존 switch
문이다.
int result;
switch (str) {
case "a":
result = 1;
break;
case "b": case "c":
result = 2;
break;
default:
result = -1;
}
자바 12 버전 - ,
(콤마)를 사용하여 여러 case를 한 줄에 나열한다.
int result;
switch (str) {
case "a":
result = 1;
break;
case "b", "c":
result = 2;
break;
default:
result = -1;
}
자바 12 버전 - switch operator가 람다식을 통해 결과를 반환할 수 있다.
switch fall through
가 존재하지 않아 break
를 쓰지 않아도 된다.int result = switch (str) {
case "a" -> 1;
case "b", "c" -> 2;
default -> -1;
};
자바 13 버전 - switch operator가 yield
키워드를 사용하여 결과를 반환할 수 있다.
:
를 이용한 switch문에서도 사용가능하다.int result = switch (str) {
case "a"-> 1;
case "b", "c" -> {
System.out.println("{} 블록으로 멀티 라인 수행");
yield 2;
}
default -> -1;
};
int result = switch (str) {
case "a":
yield 1;
case "b", "c": {
System.out.println("{} 블록으로 멀티 라인 수행");
yield 2;
}
default:
yield -1;
};
: 어떤 작업이 반복적으로 수행되도록 할 때 사용된다.
: 반복횟수를 알고 있을 때 사용한다.
for(변수초기화식; 조건식; 변수증감식) {
// 조건식이 참일 때, 반복적으로 수행될 문장
}
: 반복문으로 배열과 컬렉션에 접근할 때, 좀 더 간편한 방법으로 처리할 수 있게 자바 5버전부터 추가된 문법이다.
for(변수타입 변수명 : 배열 또는 컬렉션) {
// 반복적으로 수행될 문장
}
: 반복횟수를 모를 때 사용한다.
while(조건식) {
// 조건식이 참일 때, 반복적으로 수행될 문장
}
: while
문과는 다르게 블럭안에 있는 문장들이 최소한 한 번은 수행된다.
{
// 반복적으로 수행될 문장
} while(조건식);
: 자신이 포함된 가장 가까운 반복문을 벗어난다.
int sum = 0;
int i = 0;
while(true) {
i(sum > 100) break; // break문이 수행되면 while문을 완전히 벗어난다.
++i;
sum += i;
}
: 자신이 포함된 가장 가까운 반복문에서 다음 반복으로 넘어간다.
// 1 ~ 10수 중에 3의 배수를 제외하고 출력하는 코드
for(int i = 0; i <= 10; i++) {
if(i % 3 == 0) {
continue; // continue문이 수행되면 다음 반복으로 넘어간다.
}
System.out.println(i);
}
: 자바에서 독립된 단위테스트를 지원해주는 프레임워크
: JUnit5에서는 이전 버전의 JUnit(단일 프로젝트)과는 다르게 3가지 하위 프로젝트의 여러 모듈로 구성되었다.
@TestFactory
– 동적 테스트를위한 테스트 팩토리 인 메소드를 나타냅니다.@DisplayName
– 테스트 클래스 또는 테스트 메서드에 대한 사용자 지정 표시 이름을 정의합니다.@Nested
– 주석이 달린 클래스가 중첩 된 비 정적 테스트 클래스임을 나타냅니다.@Tag
– 테스트 필터링을위한 태그 선언@ExtendWith
– 사용자 지정 확장을 등록하는 데 사용됩니다.@BeforeEach
– 주석이 달린 메소드가 각 테스트 메소드 이전에 실행됨을 나타냅니다 (이전 @Before ).(여러번 비포)@AfterEach
– 각 테스트 메서드 (이전에는 @After ) 후에 주석이 추가 된 메서드가 실행됨을 나타냅니다.@BeforeAll
– 주석이 추가 된 메서드가 현재 클래스의 모든 테스트 메서드보다 먼저 실행됨을 나타냅니다 (이전 @BeforeClass ).(한번 통합전으로 전)@AfterAll
– 현재 클래스 (이전의 @AfterClass )의 모든 테스트 메서드 후에 주석이 추가 된 메서드가 실행됨을 나타냅니다.@Disable
– 테스트 클래스 또는 메서드를 비활성화하는 데 사용됩니다 (이전에는 @Ignore ).<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.7.0'
@Test
: 단위 테스트 메서드에 선언하는 것으로, 독립적으로 테스트를 수행한다.
test_메서드이름_조건_예상결과
테스트 라이프 사이클 관리
@BeforeAll / @BeforeClass, @AfterAll / @AfterClass
: 테스트 클래스 실행 전과 후에 동작할 메서드 위에 선언한다.
static
으로 선언한다.@BeforeEach / @Before, @AfterEach / @After
: 각 테스트 메서드가 실행되기 전과 후에 동작할 메서드위에 선언한다.
@Before
용도@After
용도@Disabled
: 테스트 클래스 또는 테스트 메서드를 비활성화한다.
@DisplayNameGeneration
: 테스트 실행 결과에서 보이는 테스트 클래스 혹은 메서드 표시 이름 생성기를 선언한다.
Method
와 Class
레퍼런스 정보를 활용하여 표시 이름을 생성한다. DisplayNameGenerator
의 ReplaceUnderscores
@DisplayName
: 테스트 실행 결과에서 보이는 테스트 클래스 혹은 메서드 표시 이름을 직접 지정한다.
@DisplayNameGeneration
보다 우선순위가 높다.: 테스트 케이스의 수행 결과를 판별함
assertEquals(expected, actual(, delta))
assertSame(obj1, obj2)
/ assertNotSame(obj1, obj2)
assertArrayEquals(arr1, arr2)
assertTrue((message,) boolean)
/ assertFalse(boolean)
true
이면 message를 표시fail(message)
: 테스트 결과를 실패로 만든다.assertNull(obj)
/ assertNotNull(obj)
assertAll((heading, )executables...)
assertThrows(expectedType, executable)
assertTimeout(duration)duration, executable)
: Matcher 라이브러리 중 하나로서, 테스트 표현식을 작성할 때 문맥적으로 자연스러운 문장을 만들어준다.
assertThat(expected, matchers)
JUnit
보다는 assertj
의 assertThat()
사용을 추천한다.assertThat(expected).matcher1().matcher2() ...
live-study 대시 보드 요구사항
- 깃헙 이슈 1번부터 18번까지 댓글을 순회하며 댓글을 남긴 사용자를 체크할 것.
- 참여율을 계산하기. 총 18회 중에 몇 %를 참여했는지 소숫점 두 자리까지 보여줄 것.
- (깃헙 자바 라이브러리)[https://github-api.kohsuke.org/] 사용하면 편리함.
import org.kohsuke.github.*;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Main {
private static final String REPOSITORY_PATH = "whiteship/live-study";
private static final String TOKEN = "personal_access_token";
private static final int COUNT_WEEK = 15;
public static void main(String[] args) throws IOException {
// Personal access token을 통해 깃헙에 연결
// Personal access token은 깃헙에서 생성 가능
GitHub github = new GitHubBuilder().withOAuthToken(TOKEN).build();
// 레포지토리 접근
GHRepository repository = github.getRepository(REPOSITORY_PATH);
Map<String, int[]> participant = getParticipant(repository);
printParticipant(participant);
}
private static Map<String, int[]> getParticipant(GHRepository repository) throws IOException {
// 참가자 맵
// key : user name, value : participation counting every week
Map<String, int[]> participant = new HashMap<>();
// 이슈 순회
for (int i = 1; i <= COUNT_WEEK; i++) {
GHIssue ghIssue = repository.getIssue(i);
// 댓글 순회
List<GHIssueComment> comments = ghIssue.getComments();
for (GHIssueComment comment :
comments) {
String userName = comment.getUser().getLogin();
if (!participant.containsKey(userName)) {
participant.put(userName, new int[COUNT_WEEK + 1]);
}
int[] countParticipationEveryWeek = participant.get(userName);
countParticipationEveryWeek[i]++;
}
}
return participant;
}
private static void printParticipant(Map<String, int[]> participant) {
participant.forEach((key, value) -> {
int countIsParticipateEveryWeek = 0;
for (int countParticipation :
value) {
if (countParticipation > 0) countIsParticipateEveryWeek++;
}
System.out.printf("%-20s의 참여율은 %.2f%%입니다.%n", key, (countIsParticipateEveryWeek / (double) COUNT_WEEK) * 100);
});
}
}
kimzerovirus 의 참여율은 6.67%입니다.
KwangJongJeon 의 참여율은 73.33%입니다.
jessi68 의 참여율은 13.33%입니다.
ufonetcom 의 참여율은 33.33%입니다.
JIN-096 의 참여율은 80.00%입니다.
ssonsh 의 참여율은 100.00%입니다.
DominicStrength 의 참여율은 6.67%입니다.
Ohzzi 의 참여율은 86.67%입니다.
dongsub-joung 의 참여율은 13.33%입니다.
moo1o 의 참여율은 6.67%입니다.
Azderica 의 참여율은 100.00%입니다.
jymaeng95 의 참여율은 100.00%입니다.
ggomjae 의 참여율은 6.67%입니다.
binghe819 의 참여율은 20.00%입니다.
fpdjsns 의 참여율은 60.00%입니다.
...
: 노드들을 포인터로 연결한 자료구조
Head
포인터는 첫번째 노드를 뜻한다.Head
포인터부터 순서대로 접근하므로 속도가 느리다.next
포인터만 가진다.next
포인터와 prev
포인터를 가진다.next
포인터가 헤드 노드를 가리킨다.import java.util.Objects;
public class ListNode {
private int data;
private ListNode next;
public ListNode(int data) {
this.data = data;
this.next = null;
}
public int getData() {
return data;
}
public ListNode next() {
return next;
}
public ListNode linking(ListNode node) {
next = node;
return next;
}
public boolean hasNext() {
return Objects.nonNull(next);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ListNode listNode = (ListNode) o;
return data == listNode.data && Objects.equals(next, listNode.next);
}
@Override
public int hashCode() {
return Objects.hash(data, next);
}
@Override
public String toString() {
return String.valueOf(data);
}
}
public interface LinkedList {
ListNode add(ListNode head, ListNode nodeToAdd, int position);
ListNode remove(ListNode head, int positionToRemove);
boolean contains(ListNode head, ListNode nodeToCheck);
void print(ListNode head);
}
import java.util.Objects;
public class LinkedListImpl implements LinkedList {
@Override
public ListNode add(ListNode head, ListNode nodeToAdd, int position) {
if (position == 0) {
if (head == null) {
return nodeToAdd;
}
nodeToAdd.linking(head);
head = nodeToAdd;
return head;
}
ListNode node = head;
for (int i = 0; i < position - 1; i++) {
if (Objects.isNull(node)) throw new IllegalArgumentException();
node = node.next();
}
nodeToAdd.linking(node.next());
node.linking(nodeToAdd);
return head;
}
@Override
public ListNode remove(ListNode head, int positionToRemove) {
if (positionToRemove == 0) {
if (Objects.isNull(head)) throw new IllegalArgumentException();
head = head.next();
} else {
ListNode node = head;
for (int i = 0; i < positionToRemove - 1; i++) {
if (Objects.isNull(node)) throw new IllegalArgumentException();
node = node.next();
}
if(Objects.isNull(node.next())) throw new IllegalArgumentException();
node.linking(node.next().next());
}
return head;
}
@Override
public boolean contains(ListNode head, ListNode nodeToCheck) {
ListNode node = head;
while (!Objects.isNull(node)) {
if (node.getData() == nodeToCheck.getData()) {
return true;
}
node = node.next();
}
return false;
}
@Override
public void print(ListNode head) {
ListNode node = head;
while (Objects.nonNull(node)) {
System.out.print(node);
node = node.next();
}
}
public int size(ListNode head) {
if (Objects.isNull(head)) return 0;
ListNode current = head;
int size = 1;
while (current.hasNext()) {
size++;
current = current.next();
}
return size;
}
}
public interface Stack {
boolean isEmpty();
void push(int data);
int pop();
void print();
}
import java.util.EmptyStackException;
public class ArrayStack implements Stack {
private int top;
private int stackSize;
private int[] stackArr;
public ArrayStack(int stackSize) {
top = -1;
this.stackSize = stackSize;
stackArr = new int[stackSize];
}
@Override
public boolean isEmpty() {
return top == -1;
}
public boolean isFull() {
return top == stackSize - 1;
}
@Override
public void push(int data) {
if(isFull()) throw new StackOverflowError();
stackArr[++top] = data;
}
@Override
public int pop() {
if(isEmpty()) {
throw new EmptyStackException();
}
int data = stackArr[top];
top--;
return data;
}
@Override
public void print() {
final StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < top; i++) {
int data = stackArr[i];
sb.append(data + ", ");
}
sb.append(stackArr[top]).append(']');
System.out.println(sb);
}
}
public interface Stack {
boolean isEmpty();
void push(int data);
int pop();
void print();
}
import linkedlist.LinkedListImpl;
import linkedlist.ListNode;
import java.util.EmptyStackException;
public class ListNodeStack implements Stack {
private final LinkedListImpl linkedList = new LinkedListImpl();
private ListNode top;
@Override
public boolean isEmpty() {
return linkedList.size(top) == 0;
}
@Override
public void push(int data) {
top = linkedList.add(top, new ListNode(data), 0);
}
@Override
public int pop() {
if(isEmpty()) {
throw new EmptyStackException();
}
int data = top.getData();
top = linkedList.remove(top, 0);
return data;
}
@Override
public void print() {
linkedList.print(top);
}
}
public interface Queue {
void add(int data);
int remove() throws Exception;
int peek() throws Exception;
boolean isEmpty();
}
public class ArrayQueue implements Queue {
private static final int LENGTH_INTERVAL = 1;
private int[] array;
private int front;
public ArrayQueue() {
this.array = new int[0];
this.front = -1;
}
@Override
public void add(int data) {
front += 1;
if (array.length == front) {
int[] temp = new int[array.length + LENGTH_INTERVAL];
for (int i = 0; i < array.length; i++) {
temp[i] = array[i];
}
temp[front] = data;
array = temp;
} else {
array[front] = data;
}
}
@Override
public int remove() throws Exception {
if (isEmpty()) {
throw new Exception("No more queue");
}
int data = array[0];
for (int i = 1; i <= this.front; i++) {
array[i - 1] = array[i];
}
front -= 1;
return data;
}
@Override
public int peek() throws Exception {
if (isEmpty()) {
throw new Exception("No more queue");
}
return array[0];
}
@Override
public boolean isEmpty() {
if (this.front == -1) {
return true;
} else {
return false;
}
}
}
ListNode
를 사용import linkedlist.LinkedListImpl;
import linkedlist.ListNode;
public class ListNodeQueue implements Queue{
private final LinkedListImpl linkedList = new LinkedListImpl();
private ListNode head;
private int last;
@Override
public void add(int data) {
ListNode node = new ListNode(data);
head = linkedList.add(head, node, last);
last++;
}
@Override
public int remove() throws Exception {
if (isEmpty()) {
throw new Exception("No more queue");
}
int data = head.getData();
head = linkedList.remove(head, 0);
last--;
return data;
}
@Override
public int peek() throws Exception {
if (isEmpty()) {
throw new Exception("No more queue");
}
return head.getData();
}
@Override
public boolean isEmpty() {
return last == 0;
}
public void print() {
linkedList.print(head);
}
}
Reference
- 자바의 정석 3rd Edition, 남궁성 지음
- [4주차] 제어문
- 4주차 : 제어문
- 제어문
- 자바가 제공하는 제어문을 학습하세요