diff --git a/10week/collection.foreach vs stream.foreach.md b/10week/collection.foreach vs stream.foreach.md new file mode 100644 index 00000000..7a427e70 --- /dev/null +++ b/10week/collection.foreach vs stream.foreach.md @@ -0,0 +1,62 @@ +--- + +# **Collection.forEach 와 Stream.forEach 의 차이** + +--- + +## Stream의 foreach와 for-loop는 다르다. + +- stream 은 java8 부터 람다와 함께 제공된 방법이며 컬렉션 요소를 하나씩 참조해 함수형 인터페이스를 통한 반복적 처리를 가능하게 해준다. + +- 이에 for문을 stream으로 대신해 처리하는 경우가 빈번하다. + +**하지만 무턱대고 아무곳에서나 for문을 stream으로 바꿔 구현하는 행위는 옳지 않다.** + +- 물론 Stream을 사용하면 가독성이 높아져 코드 이해도가 높아진다. 하지만 다음 예시에서는 사용을 다시 생각해 봐야할 필요가 있다. + +## 🤔 **만약 Stream의 모든 요소를 도는 forEach를 사용시 종료 조건이 있는 경우에는 어떡할까?** + +- 일단 Stream 에서 강제적으로 루프를 탈출 할 수 있는 방법은 없다. + +![https://s3-us-west-2.amazonaws.com/secure.notion-static.com/82dc64e7-1599-473e-a586-8f92e1e40e1b/_2021-04-29__11.11.17.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/82dc64e7-1599-473e-a586-8f92e1e40e1b/_2021-04-29__11.11.17.png) + +- 다음 코드의 결과를 보면 같은 결과 값을 내지만 기존의 for 문에서는 50까지 수행이되면 이후는 수행 되지 않고 반복문이 종료가 되지만 stream 을 사용한 아래의 코드는 return이후 코드만 처리되지 않을뿐 50이 넘어서도 모든 조건을 확인 후에 종료하게 된다. + +![https://s3-us-west-2.amazonaws.com/secure.notion-static.com/32e7a32a-0778-423e-acb6-bff49ad79166/_2021-04-29__11.12.21.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/32e7a32a-0778-423e-acb6-bff49ad79166/_2021-04-29__11.12.21.png) + +- 다음과 같이 같은 조건문을 가지더라도 모든 조건을 다 확인하는 stream에서는 중간에 종료가 불가능 하여 비효율 적이다. forEach 연산의 올바른 사용법인 Filter 를 쓰더라도 stream은 지연 연산을 지원 하기 때문에 100번 모두를 검사하게 된다. +- 또한 forEach연산을 최종 연산중 가장 기능이 적고 Stream 스럽지 못하므로 print 문을 사용할때만 쓰자. + +--- + +## Collection.forEach 와 Stream.forEach 의 차이점 + +![https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2b59e6d0-45e5-489c-99af-ce367ebb60e0/_2021-04-29__11.46.29.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2b59e6d0-45e5-489c-99af-ce367ebb60e0/_2021-04-29__11.46.29.png) + +- 코드만 봤을 경우에는 그렇게 큰 차이는 없다. +- Collection.forEach의 경우 따로 객체를 생성하지 않고 forEach메소드를 호출하는데 Iterable 인터페이스를 상속하고 있기 때문이다. +- stream.forEach의 경우는 Collection 인터페이스의 메소드인 stream() 을 통해 stream 객체를 생성해야만 forEach 호출이 가능하다. +- 출력문을 봐도 별 차이가 없다. + + ![https://s3-us-west-2.amazonaws.com/secure.notion-static.com/dbed982b-2a70-4c9c-859c-ad6c93703462/_2021-04-29__12.07.44.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/dbed982b-2a70-4c9c-859c-ad6c93703462/_2021-04-29__12.07.44.png) + +- 하지만 동시성 부분에서는 차이가 있다. + +**동시성** + +- Collection.forEach의 경우는 중간 과정에 삭제하는 조건문을 넣어두고 수정을 감지하면 Exception을 터트리는 구조로 코드를 작성하면 변경과 동시에 Exception 을 터트린다. (ConcurrentModificationException) +- 반면 Stream.forEach의 경우에는 다르다 Collection 때 처럼 수정을 감지하는 코드를 작성해 놓으면 Stream 은 리스트를 모두 순회 하고 예외를 던진다. (NulPointerException) + +- Collection의 경우에는 Iterator을 사용하고 Stream은 spliterator을 사용한다. 아래의 [Collections.java](http://collections.java) 의 코드를 보면 Synchronized 키워드가 붙어 있다. 반면 stream에는 붙어있지 않다. + + ![https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d5d8eb35-1b32-4693-82e8-3dd59da18eba/_2021-04-29__12.28.13.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d5d8eb35-1b32-4693-82e8-3dd59da18eba/_2021-04-29__12.28.13.png) + +- 결론적으로 둘은 큰 차이가 없지만 단순 반복을 위한 forEach면 Stream사용보다 일반 collection 의 foreach사용을 권장한다. + +--- + +## Reference + +[https://woowacourse.github.io/javable/post/2020-05-14-foreach-vs-forloop/](https://woowacourse.github.io/javable/post/2020-05-14-foreach-vs-forloop/) + +[https://woowacourse.github.io/javable/post/2020-09-30-collection-stream-for-each/](https://woowacourse.github.io/javable/post/2020-09-30-collection-stream-for-each/) \ No newline at end of file diff --git "a/10week/\354\212\244\355\212\270\353\246\274 \354\240\225\353\246\254.md" "b/10week/\354\212\244\355\212\270\353\246\274 \354\240\225\353\246\254.md" new file mode 100644 index 00000000..d0e26ef4 --- /dev/null +++ "b/10week/\354\212\244\355\212\270\353\246\274 \354\240\225\353\246\254.md" @@ -0,0 +1,113 @@ +--- + +# Stream의 특징 정리 (map, filter, sorted, distinct, limit. foreach) + +--- + +## 자바 8 스트림? + +💁 자바 8에서 추가된 스트림은 람다식을 활용한 기술 중 하나로 테이터의 흐름이라는 뜻을 가지고 있다. + +💁 스트림이란 간단하게 설명하면 자바의 컬렉션의 반복을 기존의 반복문 보다 효율적으로 처리하는 방식이라고 생각하면 이해가 쉽다. 스트림 이용시 따로 멀티 스레드 코드를 구현하지 않아도 병렬로 데이터가 처리가 된다. + +💁 스트림은 선언형 코드로 구현이 가능하다. 루프, if 등의 조건문을 이용해 어떻게 동작할지 지정하지 않아도 선언형 방식으로 동작을 지정할수 있어 변하는 요구사항에 효율적으로 대응이 가능하다. 따라서 람다식을 통해 기존 코드의 중복을 줄이고 특정 조건을 필터링 하는 코드를 쉽게 구현이 가능하다. + +💁 위와 같은 특징으로 인해 Sort, Filter, Map 등의 빌딩 블록 연산을 이용해 복잡한 데이터 처리에 유용하며 가독성을 해치지 않는다. + +- 요약 + +--- + +## 스트림과 컬렉션 + +💁 자바의 기존 컬렉션과 스트림 모두 순차적으로 데이터를 저장하는 형식의 자료구조 인터페이스를 제공한다. 하지만 이 둘의 가장 큰 차이점은 언제 계산하느냐 이다. + +🤔 컬렉션 : 자료구조가 포함하는 모든 값을 메모리에 저장 = 즉 컬렉션에 추가 되기전에 계산이 되어야 한다. + +🤔 스트림 : 이론적으로 요청 할때만 요소를 계산하는 고정된 자료구조( 스트림에 자료를 추가하거나, 삭제 불가능 ) 스트림은 요청하는 값만 스트림에서 추출을 하는데 이는 사용자는 알 수 없다. 결과적으로 생산자와 소비자관계를 형성하게 된다. + +🖍 **하나의 스트림은** **스트림은 한번만 사용 가능하다.** + +- 반복자와 동일하게 스트림도 한번만 탐색 할수 있으며 다시 사용 하고 싶다면 새로운 스트림을 만들어야 한다. 또 스트림은 시간적으로 흩어진 값의 집합으로 간주 할수 있다. + +--- + +## 스트림 연산 + +🖍 **중간 연산** + +- filter  나 sorted 같은 중간 연산은 연산 후에 다른 스트림을 반환한다. 이 특성을 이용해 연속해서 질의를 이어 나갈수 있다. 중간 연산은 중요한 특징은 Lazy한 특성을 갖고 있어 단말 연산을 스트림 파이프 라인에 실행하기 전까지는 어떤 연산도 수행도 하지 않는다. 그저 합쳐 놓고 최종 연산으로 한번에 처리 한다. + +🖍 **최종 연산** + +- 최종 연산은 파이프 라인에서 결과를 도출하고 스트림 이외의 결과가 반환 된다. + +☝️ **생성하기** + +- 보통 배열을 이용해 스트림을 만들지만 이외의 방식으로도 스트림을 생성이 가능하다. + +**배열 스트림** + +- 배열은 [Array.stream](http://array.stream) 메소드를 이용해 생성을 한다. + + ![https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c1ff0f64-78d9-4232-becf-1601432db851/_2021-04-28__11.31.17.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c1ff0f64-78d9-4232-becf-1601432db851/_2021-04-28__11.31.17.png) + +**컬렉션 스트림** + +- 컬렉션 타입을 이용해서도 스트림을 생성 가능하다. + + ![https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c030af2b-17b3-4162-a03b-1595e93f3068/_2021-04-28__11.28.35.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c030af2b-17b3-4162-a03b-1595e93f3068/_2021-04-28__11.28.35.png) + +☝️**Filtering** + +- 필터는 스트림 내 요소들을 하나씩 평가해 걸러내는 작업으로 boolean을 리턴하게 된다. + + ![https://s3-us-west-2.amazonaws.com/secure.notion-static.com/58492cb9-7b22-4ef6-9486-832ead9f52db/_2021-04-28__11.38.45.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/58492cb9-7b22-4ef6-9486-832ead9f52db/_2021-04-28__11.38.45.png) + + - 위 코드는 요소중에 동현이라는 값을 가진 요소를 제외 하고 나머지는 필터링 하는 코드이다. + - collect을 이용해 필터링을 하고 결과를 리스트로 반환 해준다. + +☝️ **Mapping** + +- 특정 객체에서 특정 데이터를 선택하는 작업은 데이터 처리과정에서 자주 수행 되는 연산이다. 스트림 에서는 map 과 flatMap을 제공한다. +- Map : 스트림의 각 요소에 함수 적용 + - 각 요소에 적용 돼 새로운 요소로 매핑이 된다. + + ![https://s3-us-west-2.amazonaws.com/secure.notion-static.com/7368d742-5990-4a72-ae87-088dfe3defec/_2021-04-28__12.03.14.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/7368d742-5990-4a72-ae87-088dfe3defec/_2021-04-28__12.03.14.png) + + - 위 코드는 carNames 라는 string 배열의 값을 이용해 Car객체로 생성해주는 매핑이다. +- flatMap : 중간 중첩 구조를 한단계 제거 하고 단일 컬렉션으로 만들어 주는 역할을 한다. + + ![https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d87f0f85-808c-456e-99f4-22fd5b967b82/_2021-04-28__12.23.02.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d87f0f85-808c-456e-99f4-22fd5b967b82/_2021-04-28__12.23.02.png) + +☝️ **Sorting** + +- 스트림에서 sort는 스트림 아이템을 정렬후 새로운 스트림을 생성한다. 다른 정렬과 마찬가지로 comparator 를 이용하며 인자 없이 그냥 호출시 오름차순으로 정렬 된다. + + ![https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0b068347-4fb2-4b89-9905-aa61607f281e/_2021-04-28__12.35.35.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0b068347-4fb2-4b89-9905-aa61607f281e/_2021-04-28__12.35.35.png) + +- 인자를 넘기는 경우에는 comparator 를 이용하게 된다. String 의 경우에는 Comparable 인터페이스가 구현이 되어있어 Comparator 를 바로 사용할수있지만 그렇지 않은경우에는 직접 정의를 해야한다. + +☝️ **Distinct** + +- 사전적의미로는 뚜렷한, 분명한 이란 의미를 가지고 있는데 이는 스트림에서 중복제거를 도와주는 역할을 한다. 즉 스트림에서 사용시 중복된 요소를 모두 제거 하고 새로운 스트림을 생성해준다. 중복된 요소의 판단 기준은 Object.equals(Object) 값이다. + + ![https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e78bd6eb-b284-4954-935b-241db29c77ec/_2021-04-28__12.42.46.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e78bd6eb-b284-4954-935b-241db29c77ec/_2021-04-28__12.42.46.png) + +☝️**Limit** + +- Limit는 어떤 스트림에서 일정 개수 만큼 가져와 새로운 스트림을 생성하는데 사용 방법은 Stream.limit(숫자) 로 숫자만큼 데이터를 셀렉하고 스트림으로 생성하게 된다. + + ![https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c2a2929a-96b3-435e-8a17-abf793293b49/_2021-04-28__12.46.02.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c2a2929a-96b3-435e-8a17-abf793293b49/_2021-04-28__12.46.02.png) + +🔍 forEach에 대해서는 다음 정리에서 다루도록 하겠습니다. + +--- + +## Reference + +[https://futurecreator.github.io/2018/08/26/java-8-streams/](https://futurecreator.github.io/2018/08/26/java-8-streams/) + +[https://12bme.tistory.com/461](https://12bme.tistory.com/461) + +[https://codechacha.com/ko/java8-stream-limit-skip/](https://codechacha.com/ko/java8-stream-limit-skip/) \ No newline at end of file diff --git "a/10week/\354\236\220\353\260\224 \354\273\254\353\240\211\354\205\230 \355\224\204\353\240\210\354\236\204\354\233\214\355\201\254.md" "b/10week/\354\236\220\353\260\224 \354\273\254\353\240\211\354\205\230 \355\224\204\353\240\210\354\236\204\354\233\214\355\201\254.md" new file mode 100644 index 00000000..87226924 --- /dev/null +++ "b/10week/\354\236\220\353\260\224 \354\273\254\353\240\211\354\205\230 \355\224\204\353\240\210\354\236\204\354\233\214\355\201\254.md" @@ -0,0 +1,144 @@ +--- + +# 컬렉션 프레임 워크 (JCF ( Java Collection Framework )) + +--- + +## 컬렉션 프레임 워크란? + +- 컬렉션 프레임 워크란? + +- 컬렉션 프레임 워크란 여러 요소를 담을 수 있는 자료구조를 의미하며 배열과 유사하다고 생각 할 수 있지만 다른 특성을 가진다. +- 가장 대표적인 차이점은 배열은 크기가 고정되어있지만 컬렉션은 그렇지 않다. 크기가 고정되어있지 않아 배열의 고정된 크기로 생길수있는 문제점을 해결해 줄수 있다. + - ex) 배열의 크기 고정으로 인해 생길수 있는 처음에 정한 크기보다 더 큰 데이터를 넣을 시 오류가 발생하고, 데이터 삭제시 인덱스는 비지만 메모리값은 낭비가 되는 문제점들을 보완 할수 있다. +- 대표적으로는 List, Map, Stack, Queue 등이 있다. + +**프레임 워크의 구성 위치** + +- 컬렉션 인터페이스 : 모든 컬렉션 인터페이스는 java.util 패키지에 포함 되어있다. + +![https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0ce0c0ee-aad9-46df-8dbb-18169c811db6/_2021-04-27__11.13.18.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0ce0c0ee-aad9-46df-8dbb-18169c811db6/_2021-04-27__11.13.18.png) + +- 컬렉션 클래스 : 모든 컬렉션 클래스는 java.util or java.utl.concurrent 패키지에 포함된다. (인터페이스에 대한 구현 클래스) +- 컬렉션 기능 : 검색, 정렬, 셔플등의 기능을 지원. + +--- + +## 컬렉션 인터페이스 + +- 컬렉션 인터페이스 들은 제네릭으로 구현 하여 컴파일시 체크 단계를 거치게 되어 에러를 줄일수있다. +- 컬렉션 프레임워크의 대표적 인터페이스 + - List + - Set + - Map + +List와 Set은 Collection 인터페이스에서 정의 하고 있으나 Map은 값으로 이루어져있어 별도로 구현된다. + +![https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a26287fa-0b1c-447f-93d2-f6ff1fe6d3fd/_2021-04-27__11.49.44.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a26287fa-0b1c-447f-93d2-f6ff1fe6d3fd/_2021-04-27__11.49.44.png) + +![https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2bec1033-b4b3-4592-81d2-38dcfa120e08/_2021-04-27__11.51.33.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2bec1033-b4b3-4592-81d2-38dcfa120e08/_2021-04-27__11.51.33.png) + +![https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1b75a0b8-2891-42e9-811b-558d1c96e578/_2021-04-27__12.05.44.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1b75a0b8-2891-42e9-811b-558d1c96e578/_2021-04-27__12.05.44.png) + +💁 **Collection 인터페이스 그룹** + +- 직접 구현을 하지 않고 메서드만 포함하고 있다. + - boolean add(E e) + - boolean addAll(Collection c) + - void clear() + - boolean contains(Object o) + - boolean containsAll(Collection c) + - boolean equals(Object o) + - int hashCode() + - boolean isEmpty() + - Iterator iterator() + - int size() + + 등등.. + +(각 메소드에 대한 자세한 설명은 생략 하겠습니다. 이름만 보고도 무슨 역할을 할지 알수있는 메소드명을 지을수 있도록 노력하자👍) + +💁 **List 인터페이스 그룹** + +- 컬렉션 프레임 워크를 상속 받고있는 List 컬렉션은 객체를 일렬로 늘어 놓은 배열과 유사한 구조를 가지고 있으며 검색, 삭제등의 기능을 제공한다. +- List 컬렉션을 구성하는 대표적인 클래스 (ArrayList, LinkedList, Vector) + + ![https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a7cd2226-ace9-48b0-9939-39425125ab8a/_2021-04-27__12.04.23.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a7cd2226-ace9-48b0-9939-39425125ab8a/_2021-04-27__12.04.23.png) + + ![https://s3-us-west-2.amazonaws.com/secure.notion-static.com/662caacb-6966-47d3-bee5-29f5731fb64a/_2021-04-27__12.05.01.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/662caacb-6966-47d3-bee5-29f5731fb64a/_2021-04-27__12.05.01.png) + +- 대표적인 메소드 + + ![https://s3-us-west-2.amazonaws.com/secure.notion-static.com/28d0bdca-ec79-4792-8dc1-03cc0ab7094d/_2021-04-27__12.16.15.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/28d0bdca-ec79-4792-8dc1-03cc0ab7094d/_2021-04-27__12.16.15.png) + +💁 **Set 인터페이스 그룹** + +- 리스트 구조는 순서가 있는 선형구조로 이루어져 있으나 Set 은 그렇지 않다 따라서 인덱스를 이용해 객체를 검색해 가져오는 메소드가 존재 하지 않는다. 대신 반복자인 Iterator를 제공한다. (List도 Iterator 지원) +- 또한 중복 된 값을 저장 할 수 없으며 Null 값도 하나만 저장 할수 있다. +- Set 인터페이스를 상속하는 대표적인 클래스는 HashSet 과 TreeSet 이 있다. + + ![https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3a9f14a9-3ded-4a4a-b4cd-a91076009ce5/_2021-04-27__12.11.42.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3a9f14a9-3ded-4a4a-b4cd-a91076009ce5/_2021-04-27__12.11.42.png) + + ![https://s3-us-west-2.amazonaws.com/secure.notion-static.com/009cf03a-f594-40b0-8141-482c801a7644/_2021-04-27__12.13.46.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/009cf03a-f594-40b0-8141-482c801a7644/_2021-04-27__12.13.46.png) + +- 대표적인 메소드 + + ![https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c90386c1-6864-4d2a-a5f4-2f92ede36b48/_2021-04-27__12.14.36.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c90386c1-6864-4d2a-a5f4-2f92ede36b48/_2021-04-27__12.14.36.png) + +--- + +💁 **Map 인터페이스** + +- Map은 Key값과 Value 값을 매핑 하여 사용해 Collection과 별도로 구현이 되어있으며, Set과 동일하게 중복값이 존재 할수 없다. +- Map 인터페이스로 구현된 클래스 + + ![https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9c09e86a-99e1-46bb-850d-24739bf8dbd0/_2021-04-27__12.22.15.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9c09e86a-99e1-46bb-850d-24739bf8dbd0/_2021-04-27__12.22.15.png) + +- Map의 주요 메소드 + + ![https://s3-us-west-2.amazonaws.com/secure.notion-static.com/ad88ad29-76de-4f23-90a0-734af25d54c3/_2021-04-27__12.32.38.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/ad88ad29-76de-4f23-90a0-734af25d54c3/_2021-04-27__12.32.38.png) + +--- + +## 컬렉션 알고리즘 + +- 모든 컬렉션의 알고리즘을 담당하며 Static으로 구현된 유틸리티 클래스이다. 주요 메소드는 (Sort, BinarySearch, get, Reverse, Swap, Copy, Min, Max 등이 있다.) + + ![https://s3-us-west-2.amazonaws.com/secure.notion-static.com/56a17939-ba03-45c4-b3a4-36d43518785c/_2021-04-27__1.01.49.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/56a17939-ba03-45c4-b3a4-36d43518785c/_2021-04-27__1.01.49.png) + + ![https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4dc488a8-8bce-41b8-8269-ac80d0044b13/_2021-04-27__1.01.56.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4dc488a8-8bce-41b8-8269-ac80d0044b13/_2021-04-27__1.01.56.png) + + ![https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2d268287-0afa-41cc-b0ff-7f0156b6b71a/_2021-04-27__1.02.05.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2d268287-0afa-41cc-b0ff-7f0156b6b71a/_2021-04-27__1.02.05.png) + + ![https://s3-us-west-2.amazonaws.com/secure.notion-static.com/55bfdd8f-a04a-4043-949d-1fecd655b6b2/_2021-04-27__1.02.17.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/55bfdd8f-a04a-4043-949d-1fecd655b6b2/_2021-04-27__1.02.17.png) + + ![https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b28b80d1-a14f-4009-a77c-c56225f2cfb5/_2021-04-27__1.02.26.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b28b80d1-a14f-4009-a77c-c56225f2cfb5/_2021-04-27__1.02.26.png) + +--- + +## 컬렉션 프레임워크의 모범 사례 + +필요에 따라 상황에 맞는 컬렉션을 선택해야 된다. + +- 크기(size)가 고정되어 있다면 ArrayList보다 **Array**를 사용하라. +- 맵에 삽입된 순서대로 iterate를 하고 싶으면 **TreeMap**을 사용하는 것이 좋다. +- 중복을 허용하고 싶지 않으면 **Set**을 사용하면 된다. +- 몇몇 컬렉션 클래스들을 초기 용량을 지정할 수 있다. 만약 저장할 요소들의 사이즈를 알 경우에 **초기 용량**을 지정함으로써 rehashing이나 resizing이 일어나는 것을 회피할 수 있다. +- 코드를 작성할 때, 구현 클래스가 아닌 **인터페이스**를 기반으로 작성해야 나중에 구현체를 변경할 때 코드를 재작성하는 수고를 줄일 수 있다. +- 런타임에 발생할 수 있는 ClassCastException을 회피하려면 항상 **제네릭(Generics)을** 사용해서 type-safety 한 상태를 유지하라. +- 맵에 키를 사용할 때 JDK에서 제공하는 **immutable** 클래스를 사용하여 사용자 클래스에서 hashCode()와 equals() 구현할 필요가 없게 하라 +- 읽기 전용 및 동기화, 빈 컬렉션 등을 만들 때는 자신만의 구현으로 생성하지 말고 Collections에서 제공하는 **유틸리티 클래스**를 사용하라. 이는 코드 재사용성을 높여주고 안정적이며 유지보수 비용을 줄여 준다. + +[https://gbsb.tistory.com/247](https://gbsb.tistory.com/247) ( 작성자 분 ) + +--- + + + +## Reference + +[https://gbsb.tistory.com/247](https://gbsb.tistory.com/247) + +[https://coding-factory.tistory.com/550](https://coding-factory.tistory.com/550) + +[https://docs.oracle.com/javase/9/docs/api/java/util/List.html](https://docs.oracle.com/javase/9/docs/api/java/util/List.html) \ No newline at end of file diff --git a/8week/racingcar/.gitignore b/8week/racingcar/.gitignore new file mode 100644 index 00000000..76611b07 --- /dev/null +++ b/8week/racingcar/.gitignore @@ -0,0 +1,21 @@ +.DS_Store +.gradle +/build/ +!gradle/wrapper/gradle-wrapper.jar +/out/ +/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr \ No newline at end of file diff --git a/8week/racingcar/build.gradle b/8week/racingcar/build.gradle new file mode 100644 index 00000000..95df5f5c --- /dev/null +++ b/8week/racingcar/build.gradle @@ -0,0 +1,14 @@ +plugins { + id 'java' +} + +group 'org.example' +version '1.0-SNAPSHOT' + +repositories { + mavenCentral() +} + +dependencies { + testCompile group: 'junit', name: 'junit', version: '4.12' +} diff --git a/8week/racingcar/document/README.md b/8week/racingcar/document/README.md new file mode 100644 index 00000000..80371684 --- /dev/null +++ b/8week/racingcar/document/README.md @@ -0,0 +1,84 @@ +# RacingCar 기능정리 + +--- +## Domain + +### Car +* 아는 것 + * 차의 이름 + * 차의 위치 + * 랜덤 범위 +* 하는 것 + * 차 이동 + * 이름 검증 (공백, 길이) + +### Cars +* 아는 것 + * 복수의 차 +* 하는 것 + * 차 전체 이동 + * 가장 멀리간 차의 위치 찾기 + * 우승자 찾기 + * 차 이름 중복 검증 + +### Number +* 아는 것 + * 숫자 값 +* 하는 것 + * 변수형 검 + +--- + +## Service + +### RacingCarGame +* 아는 것 +* 하는 것 + * 게임 시작 + +### RacingCarGameController +* 아는 것 +* 하는 것 + * 차 생성 + * count 생성 + * 경주 실행 + +--- + +## Utils + +### Input +* 아는 것 +* 하는 것 + * 숫자 입력 + * 문자 입력 + +### RandomUtils +* 아는 것 + * 랜덤 라이브러리 +* 하는 것 + * 범위 내의 랜덤값 생 + + +### View +* 아는 것 +* 하는 것 + * 거리 출력 + * 차 이름 출력 + * 우승자 출력 + + + +보장된 숫자 입력 + +보장된 문자 입력 + +보장된 숫자가 아닐 시 에러가 발생한다. + +자동차 이름 중복 제거 + +자동차 이름의 길이가 5이하 보장 + +범위내의 랜덤값 생성 보장 + +String 입력을 각 항목별 분리 \ No newline at end of file diff --git a/8week/racingcar/gradle/wrapper/gradle-wrapper.jar b/8week/racingcar/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..62d4c053 Binary files /dev/null and b/8week/racingcar/gradle/wrapper/gradle-wrapper.jar differ diff --git a/8week/racingcar/gradle/wrapper/gradle-wrapper.properties b/8week/racingcar/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..a4b44297 --- /dev/null +++ b/8week/racingcar/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/8week/racingcar/gradlew b/8week/racingcar/gradlew new file mode 100755 index 00000000..fbd7c515 --- /dev/null +++ b/8week/racingcar/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/8week/racingcar/gradlew.bat b/8week/racingcar/gradlew.bat new file mode 100644 index 00000000..5093609d --- /dev/null +++ b/8week/racingcar/gradlew.bat @@ -0,0 +1,104 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/8week/racingcar/settings.gradle b/8week/racingcar/settings.gradle new file mode 100644 index 00000000..563821bb --- /dev/null +++ b/8week/racingcar/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'racingcar' + diff --git a/8week/racingcar/src/main/java/Application.java b/8week/racingcar/src/main/java/Application.java new file mode 100644 index 00000000..e4975541 --- /dev/null +++ b/8week/racingcar/src/main/java/Application.java @@ -0,0 +1,7 @@ +import service.RacingGame; + +public class Application { + public static void main(String[] args) { + RacingGame.start(); + } +} diff --git a/8week/racingcar/src/main/java/domain/Car.java b/8week/racingcar/src/main/java/domain/Car.java new file mode 100644 index 00000000..06def772 --- /dev/null +++ b/8week/racingcar/src/main/java/domain/Car.java @@ -0,0 +1,54 @@ +package domain; + +public class Car implements Comparable { + private static final int CONTROL_POINT = 4; + private static final int MAX_INPUT_LENGTH = 5; + private final String name; + private int position; + + public Car(final String name) { + validateLength(name); + validateInput(name); + this.name = name; + this.position = 0; + } + + public void movePosition(int randomValue) { + if (isMoveAble(randomValue)) { + this.position = position + 1; + } + } + + private boolean isMoveAble(int randomValue) { + return randomValue > CONTROL_POINT; + } + + private void validateLength(String name) { + if (name.length() > MAX_INPUT_LENGTH) { + throw new IllegalArgumentException("[ERROR] 이름의 길이는 5를 넘을수 없습니다."); + } + } + + public int getPosition(){ + return position; + } + + public String getName() { + return name; + } + + private void validateInput(String buffer) { + if (" ".equals(buffer)) { + throw new IllegalArgumentException("[ERROR] 잘못된 입력입니다."); + } + } + + public boolean isSamePosition(Car other) { + return other.position == this.position; + } + + @Override + public int compareTo(Car other) { + return this.position - other.position; + } +} diff --git a/8week/racingcar/src/main/java/domain/Cars.java b/8week/racingcar/src/main/java/domain/Cars.java new file mode 100644 index 00000000..bb38e583 --- /dev/null +++ b/8week/racingcar/src/main/java/domain/Cars.java @@ -0,0 +1,51 @@ +package domain; + +import utils.RandomUtils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + + +public class Cars { + private static final int START_NUMBER = 0; + private static final int FINISH_NUMBER = 10; + private final List cars; + + public Cars (List carNames) { + validateCars(carNames); + this.cars = carNames.stream() + .map(Car::new) + .collect(Collectors.toList()); + } + + public List getWinner() { + return cars.stream() + .filter(car -> car.isSamePosition(findMaxPosition())) + .collect(Collectors.toCollection(ArrayList::new)); + } + + public List getCars() { + return Collections.unmodifiableList(this.cars); + } + + public void moveCars() { + cars.forEach(Car-> Car.movePosition(RandomUtils.randomIntGenerator(START_NUMBER, FINISH_NUMBER))); + } + + private void validateCars(List carNames) { + Boolean distinctCheck = (int) carNames.stream() + .distinct() + .count() != carNames.size(); + if (distinctCheck) { + throw new IllegalArgumentException("[ERROR] 이름 중복"); + } + } + + private Car findMaxPosition() { + return cars.stream() + .max(Car::compareTo) + .orElseThrow(() -> new IllegalArgumentException("[ERROR] 차량 리스트가 비었습니다.")); + } +} diff --git a/8week/racingcar/src/main/java/domain/Number.java b/8week/racingcar/src/main/java/domain/Number.java new file mode 100644 index 00000000..4c87be0d --- /dev/null +++ b/8week/racingcar/src/main/java/domain/Number.java @@ -0,0 +1,22 @@ +package domain; + +public class Number { + private final int value; + + public Number(String value) { + validateNumber(value); + this.value = Integer.parseInt(value); + } + + private void validateNumber(String value) { + try { + Integer.parseInt(value); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("[ERROR] 시도 횟수는 숫자여야 한다."); + } + } + + public int getCount() { + return value; + } +} diff --git a/8week/racingcar/src/main/java/service/RacingGame.java b/8week/racingcar/src/main/java/service/RacingGame.java new file mode 100644 index 00000000..ba90d4ec --- /dev/null +++ b/8week/racingcar/src/main/java/service/RacingGame.java @@ -0,0 +1,22 @@ +package service; + +import domain.Cars; +import domain.Number; +import utils.RacingGameStatusView; + +public class RacingGame { + + private RacingGame() {}; + + public static void start() { + Cars cars = RacingGameController.createCar(); + Number count = RacingGameController.createCount(); + int times = count.getCount(); + while (times-- > 0) { + cars.moveCars(); + RacingGameStatusView.carStatusView(cars); + RacingGameStatusView.spacingWord(); + } + RacingGameStatusView.resultView(cars.getWinner()); + } +} diff --git a/8week/racingcar/src/main/java/service/RacingGameController.java b/8week/racingcar/src/main/java/service/RacingGameController.java new file mode 100644 index 00000000..12289d04 --- /dev/null +++ b/8week/racingcar/src/main/java/service/RacingGameController.java @@ -0,0 +1,20 @@ +package service; + +import domain.Cars; +import domain.Number; + +import utils.Input; + +public class RacingGameController { + + private RacingGameController() {}; + + public static Cars createCar() { + return Input.carNamesInput(); + } + + public static Number createCount() { + return Input.numberInput(); + } +} + diff --git a/8week/racingcar/src/main/java/utils/Input.java b/8week/racingcar/src/main/java/utils/Input.java new file mode 100644 index 00000000..5100fe31 --- /dev/null +++ b/8week/racingcar/src/main/java/utils/Input.java @@ -0,0 +1,37 @@ +package utils; + +import domain.Cars; +import domain.Number; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.Arrays; +import java.util.List; + +public class Input { + private static final String SPLIT_RULE = ","; + private static final BufferedReader BUFFERED_READER = new BufferedReader(new InputStreamReader(System.in)); + + private Input() {}; + + public static Cars carNamesInput() { + try { + System.out.println("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)"); + String buffer = BUFFERED_READER.readLine(); + return new Cars(Arrays.asList(buffer.split(SPLIT_RULE))); + } catch (IOException e) { + throw new IllegalArgumentException("[ERROR] 잘못된 입력입니다."); + } + } + + public static Number numberInput() { + try { + System.out.println("시도할 회수는 몇회인가요?"); + String buffer = BUFFERED_READER.readLine(); + return new Number(buffer); + } catch (IOException e) { + throw new IllegalArgumentException("[ERROR] 잘못된 입력입니다."); + } + } +} diff --git a/8week/racingcar/src/main/java/utils/RacingGameStatusView.java b/8week/racingcar/src/main/java/utils/RacingGameStatusView.java new file mode 100644 index 00000000..22946624 --- /dev/null +++ b/8week/racingcar/src/main/java/utils/RacingGameStatusView.java @@ -0,0 +1,48 @@ +package utils; + +import domain.Car; +import domain.Cars; + +import java.util.List; +import java.util.stream.Collectors; + +public class RacingGameStatusView { + private static final String FINAL_WINNER = "최종 우승자 : "; + private static final String COLON = " : "; + private static final String COMMA = ", "; + private static final String POSITION_VIEW = "-"; + + private RacingGameStatusView() {}; + + public static void distanceView(int position) { + while (position-- > 0){ + System.out.print(POSITION_VIEW); + } + System.out.println(); + } + + public static void carNameView(final String name){ + System.out.print(name + COLON); + } + + public static void resultView(final List cars){ + System.out.print(FINAL_WINNER); + String winner = cars.stream() + .map(Car::getName) + .collect(Collectors.joining(COMMA)); + System.out.println(winner); + } + + public static void carStatusView(Cars cars) { + cars.getCars() + .stream() + .forEach(car -> { + RacingGameStatusView.carNameView(car.getName()); + RacingGameStatusView.distanceView(car.getPosition()); + }); + } + + public static void spacingWord() { + System.out.println(); + } +} diff --git a/8week/racingcar/src/main/java/utils/RandomUtils.java b/8week/racingcar/src/main/java/utils/RandomUtils.java new file mode 100644 index 00000000..ceff946b --- /dev/null +++ b/8week/racingcar/src/main/java/utils/RandomUtils.java @@ -0,0 +1,24 @@ +package utils; + +import java.util.Random; + +public class RandomUtils { + private static final Random RANDOM = new Random(); + + private RandomUtils() { + } + + public static int randomIntGenerator(final int startNumber, final int lastNumber) { + if (startNumber > lastNumber) { + throw new IllegalArgumentException(); + } + if (startNumber < 0) { + throw new IllegalArgumentException(); + } + if (startNumber == lastNumber) { + return startNumber; + } + return startNumber + RANDOM.nextInt(lastNumber - startNumber + 1); + } + +}