diff --git a/modern_java_in_action/mordern-java-in-action-practice/ch4.md b/modern_java_in_action/mordern-java-in-action-practice/ch4.md new file mode 100644 index 00000000..ef7e5d9a --- /dev/null +++ b/modern_java_in_action/mordern-java-in-action-practice/ch4.md @@ -0,0 +1,96 @@ +# Chapter 4. 스트림 소개 +## 스트림이란 무엇인가요? + + +## 스트림을 사용하면 어떤 점이 좋은가요? + + + + +------------------------------------------ + + + +## 4.1 스트림이란 무엇인가요? +- 스트림을 이용하면 선언형으로 컬렉션 데이터를 처리할 수 있다. -> 일단 스트림이 데이터 컬렉션 반복을 멋지게 처리하는 기능이라고 생각하자! +- 또한, 스트림을 이용하면 멀티스레드 코드를 구현하지 않아도 데이터를 투명하게 병렬로 처리할 수 있다. + +### 스트림의 특징에 따른 장점 +#### 1. 선언형 : 더 간결하고 가독성이 좋아진다. +#### 2. 조립할 수 있음 : 유연성이 좋아진다. +- 여러 빌딩 블록 연산(filter, sorted, map, collect 등)을 연결해서 복잡한 데이터 처리 파이프라인을 만들 수 있다. +- 여러 연산을 파이프라인으로 연결해도 여전히 가독성과 명확성이 유지된다. +#### 3. 병렬화 : 성능이 좋아진다. + +## 4.2 스트림 시작하기 +- 스트림이란 `데이터 처리 연산을 지원하도록 / 소스에서 추출된 / 연속된 요소`로 정의할 수 있다. + #### 1. 연속된 요소 + #### 2. 소스 + #### 3. 데이터 처리 연산 + +- 스트림에는 다음과 같은 2가지 중요 특징이 있다. + #### 특징 1. 파이프라이닝 + - ㄹㄹ + + #### 특징 2. 내부 반복 + + + +## 4.3 스트림과 컬렉션 + + +### 4.3.1 딱 한번만 탐색할 수 있다. + + +### 4.3.2 외부 반복과 내부 반복 +- 컬렉션 인터페이스를 사용하면, 사용자가 지겁 요소를 반복해야 하는 `외부 요소`를 사용한다. +- 반면, 스트림 라이브러리는 (반복을 알아서 처리하고 결과 스트림값을 어딘가에 저장해주는) `내부 반복`을 사용한다. +- 함수에 어떤 작업을 수행할지만 지정하면 모든 것이 알아서 처리된다. + + + +## 4.4 스트림 연산 +- `java.util.stream.Stream` 인터페이스는 많은 연산을 정의하는데, 크게 2가지로 구분할 수 있다. +- 연결할 수 있는 스트림 연산을 `중간 연산`이라고 하며, 스트림을 닫는 연산을 `최종 연산`이라고 한다. +- 즉, `filter`, `map`, `limit` 는 서로 연결되어 파이프라인을 형성하고, `collect`로 파이프라인을 실행한 다음에 닫는다. + +```java +List names = menu.stream() // 요리 리스트에서 스트림 얻기 + .filter(dish -> dish.getCalories() > 300) // 중간 연산 + .map(Dish::getName) // 중간 연산 + .limit(3) // 중간 연산 + .collect(toList()); // 스트림을 리스트로 변환 +``` + +### 4.4.1 중간 연산 +- 중간 연산은 다른 스트림을 반환한다. 따라서, 여러 중간 연산을 연결해서 질의를 만들 수 있다. +- 중간 연산의 특징은 단말 연산을 스트림 파이프라인에 실행하기 전까지는 아무 연산도 수행하지 않는다는 것이다. +- 즉 Lazy 하다는 것이다. 중간 연산을 합친 다음에 중간 연산을 최종 연산으로 한 번에 처리하기 때문이다. +- 스트림의 게으른 특성 덕분에 몇가지 최적화 효과를 얻을 수 있었다. +#### 최적화 효과 1. limit 연산 그리고 쇼트 서킷 +- 300칼로리가 넘은 요리는 여러 개지만, 오직 처음 3개만 선택되었다. +#### 최적화 효과 2. 루프 퓨전(loop fusion) +- filter와 map은 서로 다른 연산이지만 한 과정으로 병합되었다. + + +### 4.4.2 최종 연산 +- 최종 연산은 스트림 파이프라인에서 결과를 도출한다. +- 보통 최종 연산에 의해 List, Integer, void 등 스트림 이외의 결과가 반환된다. + +### 4.4.3 스트림 이용하기 +- 스트림 이용 과정은 다음과 같이 3가지로 요약할 수 있다. +1. 질의를 수행할 (컬렉션 같은) `데이터 소스` +2. 스트림 파이프라인을 구성할 `중간 연산` 연결 +3. 스트림 파이프라인을 실행하고 결과를 만들 `최종 연산` + + + + + + + + + + + + diff --git a/modern_java_in_action/mordern-java-in-action-practice/src/main/java/com/example/mordernjavainactionpractice/Dish.java b/modern_java_in_action/mordern-java-in-action-practice/src/main/java/com/example/mordernjavainactionpractice/Dish.java new file mode 100644 index 00000000..3f42faf7 --- /dev/null +++ b/modern_java_in_action/mordern-java-in-action-practice/src/main/java/com/example/mordernjavainactionpractice/Dish.java @@ -0,0 +1,53 @@ +package com.example.mordernjavainactionpractice; + +import java.util.Arrays; +import java.util.List; + +public class Dish { + + private final String name; + private final boolean vegetarian; + private final int calories; + private final Type type; + + public Dish(String name, boolean vegetarian, int calories, Type type) { + this.name = name; + this.vegetarian = vegetarian; + this.calories = calories; + this.type = type; + } + + public String getName() { + return name; + } + + public boolean isVegetarian() { + return vegetarian; + } + + public int getCalories() { + return calories; + } + + public Type getType() { + return type; + } + + public enum Type { MEAT, FISH, OTHER } + + @Override + public String toString() { + return name; + } + + public static final List menu = + Arrays.asList( new Dish("pork", false, 800, Dish.Type.MEAT), + new Dish("beef", false, 700, Dish.Type.MEAT), + new Dish("chicken", false, 400, Dish.Type.MEAT), + new Dish("french fries", true, 530, Dish.Type.OTHER), + new Dish("rice", true, 350, Dish.Type.OTHER), + new Dish("season fruit", true, 120, Dish.Type.OTHER), + new Dish("pizza", true, 550, Dish.Type.OTHER), + new Dish("prawns", false, 400, Dish.Type.FISH), + new Dish("salmon", false, 450, Dish.Type.FISH)); +} \ No newline at end of file diff --git a/modern_java_in_action/mordern-java-in-action-practice/src/main/java/com/example/mordernjavainactionpractice/ModernJavaInActionPracticeApplication.java b/modern_java_in_action/mordern-java-in-action-practice/src/main/java/com/example/mordernjavainactionpractice/ModernJavaInActionPracticeApplication.java index 882eca2f..0b5431e4 100644 --- a/modern_java_in_action/mordern-java-in-action-practice/src/main/java/com/example/mordernjavainactionpractice/ModernJavaInActionPracticeApplication.java +++ b/modern_java_in_action/mordern-java-in-action-practice/src/main/java/com/example/mordernjavainactionpractice/ModernJavaInActionPracticeApplication.java @@ -1,12 +1,24 @@ package com.example.mordernjavainactionpractice; -import org.springframework.boot.SpringApplication; +import static java.util.Comparator.comparing; +import static java.util.stream.Collectors.toList; + +import java.util.List; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ModernJavaInActionPracticeApplication { public static void main(String[] args) { - SpringApplication.run(ModernJavaInActionPracticeApplication.class, args); + getLowCaloricDishesNamesInJava8(Dish.menu).forEach(System.out::println); + } + + // Java 8 + public static List getLowCaloricDishesNamesInJava8(List dishes) { + return dishes.parallelStream() + .filter(d -> d.getCalories() < 400) // 400 이하의 요리 담기 + .sorted(comparing(Dish::getCalories)) // 칼로리 순으로 정렬 + .map(Dish::getName) // 요리 이름만 담기 + .collect(toList()); } }