SlideShare ist ein Scribd-Unternehmen logo
1 von 41
자바 8 학습 (1)
참고 문헌
Java 8 In Action
Functional Programming in Java 8 이희창 (daniel.hebn@gmail.com)
목차
1. 자바 8 의 특징
2. 람다 표현식
3. 스트림
4. 병렬 데이터 처리
5. 리펙토링, 디자인 패턴, 테스팅
6. 비동기 프로그래밍 4 ~ 6 은 자바 8 학습(2) 에서
1. 자바 8 의 특징
1. 고차 함수를 사용한 설계 (함수형 프로그래밍 지원)
- 람다 표현식이라는 간단한 코드 전달 기법을 통해 손쉬운
동작 파라미터화를 가능하게 함
- 즉, 함수 자체를 파라미터로 전달 가능
- 기존 익명 클래스 등을 통한 코드 블록 전달과 비교 가능
2. 스트림 API
- 스트림이란 한 번에 한 개씩 만들어지는 연속적인 데이터 항목들의 모임
- java.uti.stream 패키지에 스트림 API 가 추가됨
- 이를 통해 쉽고 명시적인 컬렉션의 이터레이션 및 병렬 처리 등을 지원함
3. 쉬운 병렬성 처리 가능
- 새로운 스트림 API 에서 병렬 처리를 통한 빠른 컬렉션 필터링 등이 가능
- 멀티 코어 활용의 극대화 가능
크게 보면
1. 자바 8 의 특징
함수형 스타일 코드는 투자 대비 높은 효율의 코드를 만든다.
1. 변수의 명시적인 변경이나 재할당의 문제를 피할 수 있다.
- 가변 변수의 사용은 버그의 원천, 동시성을 갖기 어렵게 함
- 함수형 버전에서는 코드 내에 명시적인 변경이 없음
2. 쉽게 병렬화가 가능하다.
- 스레드-세이프티 문제를 제거
3. 서술적인 코드 작성이 가능하다.
- 문법보다는 표현에 주력
4. 고차 함수를 사용한 설계
- 함수 자체를 파라미터로 전달 가능
함수형 스타일
1. 자바 8 의 특징
동작 파라미터화
동작 파라미터화란 아직은 어떻게 실행할 것인지 결정하지 않은 코드 블록을 의미
한다. 이 코드 블록은 나중에 프로그램에서 호출한다.
즉, 코드 블록의 실행은 나중으로 미뤄진다.
변화하는 요구사항에 유연하게 대응하기 위해 동작 파라미터화를 이용할 때가 많
다. 그러나 기존 자바에서 메서드는 객체만 인수로 받으므로 동작을 전달하기 위해
서는 메서드에서 인터페이스를 파라미터로 받도록 선언하고 이를 구현한 객체를
전달하거나 익명 클래스를 통해 전달하기도 한다.
그러나 이런 식의 구현은 동작의 핵심 이외의 장황한 코드가 필연적이므로 구현하
고 유지보수하는 데 어려움이 많다.
 자바8 에서는 람다 표현식이라는 간단한 코드 전달 기법을 통해 이를 해결할 수
있도록 한다.
2. 람다 표현식
정의
람다 표현식은 메서드로 전달할 수 있는 익명 함수를 단순화한 것
(기존 자바에서는 익명 클래스 등을 활용하여 코드 블록을 전달함)
1. 익명: 보통의 메서드와 달리 이름이 없으므로 익명이라 표현함
2. 함수: 메서드처럼 특정 클래스에 종속되지 않으므로 함수라고 표현함
3. 전달: 람다 표현식을 메서드 인수로 전달하거나 변수로 저장할 수 있음
4. 간결성: 익명 클래스처럼 핵심 이외의 코드를 추가로 구현할 필요가 없음
2. 람다 표현식
기본 문법
(parameters) -> expression
또는
(parameters) -> { statements; }
람다 예제
• (List<String> list) -> list.isEmpty()
• ( ) -> new Apple(10)
• (Apple a) -> System.out.println(a.getWeight())
• (String s) -> s.length()
• (int a, int b) -> a * b
• (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight())
2. 람다 표현식
함수형 인터페이스 (Functional Interface)
함수형 인터페이스는 정확히 하나의 추상 메서드를 지정하는 인터페이스이다.
람다 표현식은 함수형 인터페이스라는 문맥에서 사용 가능하다.
public static void process(Runnable r){
r.run();
}
Runnable r1 = ( ) -> System.out.println("Hello World 1"); // 람다 표현식
process(r1); // 함수형 인터페이스를 인수로 받으므로 람다 표현식 전달 가능
process(( ) -> System.out.println("Hello World 2")); // 직접 람다 표현식 전달 가능
 Runnable 인터페이스는 run( ) 이라는 abstract 메서드를 가지는 함수형 인터페
이스이다.
 process(Runnable r) 메서드는 Runnable 이라는 함수형 인터페이스를 파라미터
로 가지므로 람다식 전달이 가능하다.
2. 람다 표현식
함수형 인터페이스와 람다 표현식
1. 람다 표현식은 변수에 할당하거나
2. 함수형 인터페이스를 인수로 받는 메서드로 전달할 수 있으며
3. 함수형 인터페이스의 추상 메서드와 같은 시그니처를 갖는다는 것
을 기억하면 충분
2. 람다 표현식
람다 활용: 실행 어라운드 패턴 (execute around pattern)
(데이터베이스의 파일 처리와 같은) 자원을 처리하는 코드를 설정(setup)과
정리(cleanup) 두 과정이 둘러싸는 형태를 실행 어라운드 패턴 이라고 부른다.
public static String processFile() throws IOException {
try(BufferedReader br = new BufferedReader(new FileReader("data.txt"))) {
return br.readLine(); // 실제 필요 작업 실행 코드
}
}
 파일에서 한 행을 읽는 코드이다. 한 번에 두 줄을 읽거나 자주 사용되는 단어
등을 반환하는 등의 처리를 하려면 어떻게 해야 할까?
 기존의 설정, 정리 과정은 재사용하고 processFile 메서드만 다른 동작으로 갈아
끼울 수 있다면?
2. 람다 표현식
람다 활용: 실행 어라운드 패턴 (execute around pattern)
1단계: 동작 파라미터화를 기억하라
기존의 설정, 정리 과정을 재사용하고 processFile 메서드만 다른 동작을 수행하도
록 명령하려면
processFile 의 동작을 파라미터화하여 전달하면 가능하다.
람다를 이용하면 동작 전달이 가능하다.
만약 processFile 메서드가 한 번에 두 행을 읽도록 하려면
BufferedReader 를 인수로 받아서 String 을 반환하는 람다식이 필요하다.
String twoLines =
processFile( (BufferedReader br) -> br.readLine() + br.readLine() );
2. 람다 표현식
람다 활용: 실행 어라운드 패턴 (execute around pattern)
2단계: 함수형 인터페이스를 이용해서 동작 전달
함수형 인터페이스 자리에 람다를 사용할 수 있다.
따라서 BufferedReader -> String 과 IOException 을 throw 할 수 있는 시그니처와
일치하는 함수형 인터페이스를 만들어야 한다.
@FunctionalInterface
public interface BufferedReaderProcessor {
String process(BufferedReader br) throws IOException;
}
정의한 인터페이스를 다음의 processFile 메서드의 인수로 전달할 수 있다.
public static String processFile(BufferedReaderProcessor p) throws IOException{
// …
}
2. 람다 표현식
람다 활용: 실행 어라운드 패턴 (execute around pattern)
3단계: 동작 실행!
이제 BufferedReaderProcessor 에 정의된 process 메서드의 시그니처
(BufferedReader -> String) 와 일치하는 람다를 전달할 수 있다.
람다 표현식으로 함수형 인터페이스의 추상 메서드 구현을 직접 전달할 수 있으며
전달된 코드는 함수형 인터페이스의 인스턴스로 전달된 코드와 같은 방식으로 처
리한다.
public static String processFile(BufferedReaderProcessor p) throws IOException {
try(BufferedReader br = new BufferedReader(new FileReader("data.txt"))) {
return p.process(br);
}
}
2. 람다 표현식
람다 활용: 실행 어라운드 패턴 (execute around pattern)
4단계: 람다 전달
이제 람다를 이용해서 다양한 동작을 processFile 메서드로 전달할 수 있다.
// 한 행 처리
String oneLine
= processFile((BufferedReader br )-> br.readLine());
// 두 행 처리
String twoLines
= processFile((BufferedReader br )-> br.readLine() + br.readLine());
2. 람다 표현식
자바 8 에서 제공하는 함수형 인터페이스
함수형 인터페이스는 오직 하나의 추상 메서드를 가지고,
추상 메서드는 람다 표현식의 시그니처를 묘사한다.
함수형 인터페이스의 추상 메서드 시그니처를
함수 디스크립터 (function descriptor) 라고 한다.
(이를 통해 자바 컴파일러가 함수형 인터페이스에 전달된 람다식을 해석하여 생략
된 타입을 추론하기도 한다)
이미 자바 API 는 Comparable, Runnable, Callable 등의 다양한 함수형 인터페이스
를 포함하고 있다.
그러나 자바 8 부터 java.util.function 패키지로 여러 가지 새로운 함수형 인터페이
스가 제공된다.
2. 람다 표현식
Predicate
java.util.function.Predicate<T> 인터페이스는 test 라는 추상메서드를 정의하고
Boolean 을 리턴한다.
T 형식의 객체를 사용하는 boolean 표현식이 필요한 상황에서 Predicate 인터페이
스 사용이 가능하다.
public static <T> List<T> filter(List<T> list, Predicate<T> p){
List<T> results = new ArrayList<>();
for(T s : list){
if( p.test(s)) {
results.add(s);
}
}
return results;
}
final List<String> startsWithL = friends.stream().filter(name -> name.startsWith(“L"));
 filter 의 인자는 Predicate 이고 전달된 람다식 name -> name.startsWith(“L") 는
컴파일러에 의해 Predicate 인터페이스의 구현체로 변환된다.
2. 람다 표현식
Consumer
java.util.function.Consumer<T> 인터페이스는 제네릭 형식 T 객체를 받아서 void
를 반환하는 accept 라는 추상 메서드를 정의한다.
T 형식의 객체를 인수로 받아서 어떤 동작을 수행하고 싶을 때 Consumer 인터페
이스를 사용한다.
public static <T> void forEach(List<T> list, Consumer<T> c){
for(T i : list){
c.accept(i);
}
}
// 파라미터 타입 생략 가능 – 자동 추론
friends.forEach(name -> System.out.println(name));
2. 람다 표현식
Function
java.util.function.Function<T, R> 인터페이스는 제네릭 형식 T 를 인수로 받아서 제
네릭 형식 R 객체를 반환하는 apply 라는 추상메서드를 정의한다.
다음은 String 리스트를 인수로 받아 각 String 길이를 포함하는 Integer 리스트로
변환하는 map 메서드 예시이다.
public static <T, R> List<R> map(List<T> list, Function<T, R> f){
List<R> result = new ArrayList<>();
for(T s : list){
result.add( f.apply(s) );
}
return result;
}
List<Integer> length =
map(Arrays.asList("Apple", "Banana", "Orange"), (String s) -> s.length());
2. 람다 표현식
기본형 특화
자바의 모든 형식은 참조형(reference type) 아니면 기본형(pimitive type) 에 해당
한다. 자바에서는 기본형을 참조형으로 변환할 수 있는 오토박싱(autoboxing) 이라
는 기능을 제공한다.
하지만 이런 변환 과정은 박싱한 래퍼를 힙에 저장하고, 또한 기본형을 가져올 때
도 메모리를 탐색하는 과정이 필요하다.  일종의 비용 소모
다음은 String 리스트를 인수로 받아 각 String 길이를 포함하는 Integer 리스트로
변환하는 map 메서드 예시이다.
자바 8 에서는 기본형을 입출력으로 사용하는 상황에서 오토박싱 동작을 피할 수
있는 특별한 버전의 함수형 인터페이스를 제공한다.
Ex) IntPredicate vs Predicate<Integer>
IntPredicate 는 1000 이라는 값을 박싱하지 않지만, Predicate<Integer>는 Integer
객체로 박싱한다.
2. 람다 표현식
자바 8 의 대표적인 함수 인터페이스
함수형 인터페이스 함수 디스크립터 기본형 특화
Predicate<T> T -> boolean IntPredicate, LongPredicate, DoublePredicate
Consumer<T> T -> void IntConsumer, LongConsumer, DoubleConsumer
Function<T, R> T -> R IntFunction<R>, IntToDoubleFunction,
IntToLongFunction 등
Supplier<T> ( ) -> T BooleanSupplier, IntSupplier, LongSupplier 등
UnaryOperator<T> T -> T IntUnaryOperator, LongUnaryOperator 등
BinaryOperator<T> (T, T) -> T IntBinaryOperator, LongBinaryOperator 등
BiPredicate<L, R> (L, R) -> boolean
BiConsumer<T, U> (T, U) -> void ObjIntConsumer<T>, ObjLongConsumer<T> 등
BiFunction<T, U, R> (T, U) -> R ToIntBiFunction<T>, ToLongBiFunction<T> 등
2. 람다 표현식
형식 검사
람다로 함수형 인터페이스의 인스턴스를 만들 수 있다.
그러나 람다 표현식 자체에는 람다가 어떤 함수형 인터페이스를 구현하는지의 정
보는 담겨있지 않다.
이 때 람다가 사용되는 컨텍스트(메서드 파라미터, 할당되는 변수 등) 를 이용해서
람다의 형식을 추론할 수 있다. 어떤 컨텍스트에서 기대되는 람다 표현식의 형식을
대상 형식(target type)이라고 부른다.
다음의 예를 통해 형식 확인 과정을 파악한다.
List<Apple> heavierThan150g
= filter(inventory, (Apple a) -> a.getWeight() > 150);
2. 람다 표현식
형식 검사
List<Apple> heavierThan150g
= filter(inventory, (Apple a) -> a.getWeight() > 150);
1. filter 메서드의 선언을 확인
2. filter 메서드는 두 번째 파라미터로 Predicate<Apple>형식(대상 형식)을 기대함
3. Predicate<Apple>은 test라는 한 개의 추상메서드를 정의함(함수형 인터페이스)
4. test 메서드는 Apple 을 받아 boolean 을 반환함
5. filter 메서드로 전달된 인수는 이와 같은 요구사항을 만족해야함
 위 예제에서 전달된 람다식은 Apple 을 인수로 받아 boolean 을 반환하므로 유
효한 코드임
2. 람다 표현식
형식 검사
대상 형식 이라는 특징 때문에 같은 람다식이라도 호환되는 추상 메서드를 가진 다
른 함수형 인터페이스로 사용이 가능하다.
Comparator<Apple> c1
= (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
ToIntBiFunction<Apple, Apple> c2
= (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
BiFunction<Apple, Apple, Integer> c3
= (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
2. 람다 표현식
형식 추론
자바 컴파일러는 람다 표현식이 사용된 컨텍스트(대상 형식)를 이용해서 람다 표현
식과 관련된 함수형 인터페이스를 추론한다.
이를 통해 람다식의 코드를 좀 더 단순화할 수 있다.
List<Apple> greenApples
= filter(inventory, (Apple a) -> "green".equals(a.getColor()));
// 파라미터 a 의 Type 인 Apple 선언 생략
List<Apple> greenApples
= filter(inventory, a -> "green".equals(a.getColor()));
자바 컴파일러가 명시적으로 형식의 추론이 가능하다면 람다 표현식의 간략한 작
성이 가능하다.
2. 람다 표현식
메서드 레퍼런스
자바 8 에서 제공하는 메서드 레퍼런스를 이용하면 기존의 메서드 정의를 재활용
하여 람다처럼 전달할 수 있다.
메서드 레퍼런스는 특정 메서드만을 호출하는 람다의 축약형이라고 생각할 수 있
다.
inventory.sort((Apple a1, Apple a2)
-> a1.getWeight().compareTo(a2.getWeight()));
// 메서드 레퍼런스와 java.util.Comparator.comparing 을 활용
inventory.sort(comparing(Apple::getWeight));
여기서 Apple::getWeight 는 (Apple a) -> a.getWeight() 를 축약한 것이다.
3. 스트림
스트림은 자바 API 에 새로 추가된 기능으로, 스트림을 이용하면 선언적인 코
딩으로 컬렉션 데이터를 처리할 수 있다. 또한 스트림을 활용하면 멀티쓰레드
코드를 구현하지 않아도 데이터를 쉽게 병렬처리 할 수 있다.
1. 선언형 코드 구현이 가능하다. 루프와 if 조건문 등을 통해 어떻게 구현할
지 작성할 필요 없이 ‘특정 조건만 선택하라’ 등과 같은 동작의 수행을 지
정할 수 있다.
2. filter, sorted, map, collect 와 같은 여러 빌등 블록 연산을 연결해서
복잡한 데이터 처리 파이프라인을 만들 수 있다
3. 멀티코어 아키텍처를 최대한 투명하게 활용할 수 있도록 구현되어 있다.
데이터 처리 과정을 병렬화하면서 쓰레드와 락을 신경 쓸 필요가 없다.
정의 및 특징
3. 스트림
• 연속된 요소
컬렉션과 마찬가지로 스트림은 특정 요소 형식으로 이루어진 연속된 값
집합의 인터페이스를 제공
• 소스
스트림은 컬렉션, 배열, I/O 자원 등의 데이터 제공 소스로부터 데이터를
소비한다.
• 데이터 처리 연산
filter, map, reduce, find, match, sort 등으로 데이터를 조작할 수
있다.
• 파이프라이닝
스트림 연산은 스트림 연산끼리 연결해서 커다란 파이프라인을 구성할 수
있다. 이를 통해 lazy 연산 등이 가능하다.
구성 요소
3. 스트림
• 내부 반복
반복자를 이용해서 명시적으로 반복하는 컬렉션과 달리 스트림은 내부
반복을 지원한다. 내부 반복이란 라이브러리 내에서 알아서 반복을
처리하고 결과 스트림 값을 어딘가에 저장해준다는 의미이다.
List<String> threeHighCaloricDishNames =
menu.stream()
.filter(d -> d.getCalories() > 300)
.map(Dish::getName)
.limit(3)
.collect(toList());
 collect 연산을 제외한 모든 연산은 서로 파이프라인을 형성할 수 있
도록 스트림을 반환하고, collect 연산 호출 시 최종 결과를 반환한다.
이 때의 결과물은 스트림이 아니라 List 가 반환된다.
구성 요소
3. 스트림
스트림 인터페이스의 연산은 크게 중간 연산(intermediate operation) 과
최종 연산(ternimal operation) 두 가지로 구분이 가능하다.
• 중간 연산: 최종 연산이 스트림 파이프라인에서 실행하기 전까지는 아무 연
산도 수행되지 않는다(lazy)는 것이 특징이다. 중간 연산을 합친 다음 합
쳐진 중간 연산을 최종 연산으로 한번에 처리하기 때문이다.
• 최종 연산: 최종 연산에 의해 반환되는 결과는 List, Integer 등 스트림
이외의 결과가 반환된다.
 최종 연산 실행 전까지 실행을 늦추게 되면 최종 연산의 결과를 만족하는
최소한의 작업만 실행 할 수 있으므로 효율적인 처리가 가능하다.
예를 들어, limit(3) 과 같은 연산이 포함된 경우 앞의 스트림 처리 결과
를 만족시키는 3개의 데이터만 찾게 되면 그 이상 스트림 처리 작업을 할
필요가 없다.
스트림 연산
3. 스트림
// 필터링
List<Dish> vegetarianMenu = menu.stream()
.filter(Dish::isVegetarian)
.collect(toList());
// 고유 요소 필터링 - distinct() 활용
List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
numbers.stream().filter(i -> i % 2 == 0)
.distinct()
.forEach(System.out::println);
// 스트림 축소
List<Dish> dishes = menu.stream().filter(d -> d.getCalories() > 300)
.limit(3)
.collect(toList());
스트림 활용: 필터링과 슬라이싱
3. 스트림
// 매핑: 주어진 함수를 컬렉션의 각 엘리먼트에 적용하고 결과 컬렉션을 생성
List<String> dishNames = menu.stream()
.map(Dish::getName)
.collect(toList());
// 각 단어의 글자 수의 리스트 반환
List<String> words = Arrays.asList("IDINCU", "Opensurvey", "Ovey");
List<Integer> wordLengths = words.stream()
.map(String::length)
.collect(toList());
// ["Hello" , "World"] 리스트에서 고유 문자만 추출. ["H","e","l","o","W","r","d"] 얻기
// flatMap 사용: 각 배열을 스트림이 아니라 스트림의 컨텐츠로 매핑한다.
List<String> words = Arrays.asList("Hello" , "World");
List<String> uniqueCharacters = words.stream()
.map(w -> w.split(""))
.flatMap(Arrays::stream)
.distinct().collect(Collectors.toList());
스트림 활용: 매핑
3. 스트림
// anyMatch 는 boolean 을 return 하므로 termination operation
if (menu.stream().anyMatch(Dish::isVegetarian)){
System.out.println("This menu is Vegetarian friendly");
}
// allMatch, noneMatch
boolean isHealthy = menu.stream().allMatch(d -> d.getCalories() < 1000);
boolean isHealthy = menu.stream().noneMatch(d -> d.getCalories() >= 1000);
// filter 와 findAny 의 조합
Optional<Dish> dish = menu.stream()
.filter(Dish::isVegetarian)
.findAny();
// findFirst
List<Integer> someNumbers = Arrays.asList(1,2,3,4,5);
Optional<Integer> firstSquareDivisibleByThree =
someNumbers.stream()
.map(x -> x * x)
.filter(x -> x % 3 == 0).findFirst();
스트림 활용: 검색과 매칭
3. 스트림
‘메뉴의 모든 칼로리의 합계를 구하시오’, ‘메뉴에서 칼로리가 가장 높은 요리는?’
과 같이 스트림 요소를 조합해서 더 복잡한 질의를 표현하는 연산을 리듀싱 이라고
한다.
reduce 는 두 개의 인수를 갖는다.
• 초기값
• 두 요소를 조합해서 새로운 값을 만드는 BinaryOperator<T>
reduce 는 스트림이 하나의 값으로 줄어들 때까지 각 요소를 반복해서 조합한다.
int sum = numbers.stream().reduce(0, (a, b) -> a + b); // 람다식 활용
int sum = someNumbers.stream().reduce(0, Integer::sum); // 메서드 레퍼런스 활용
초기값을 받지 않도록 오버로드된 reduce 도 있다. 이 reduce 는 Optional 객체를
반환한다. (Optional 은 뒤에서 설명)
스트림 활용: 리듀싱
3. 스트림
Optional<T> 클래스(java.util.Optional) 는 값의 존재나 부재 여부를 표현하는 컨테
이너 클래스다. Null 은 쉽게 에러를 일으킬 수 있으므로 자바 8 에서 Optional<T>
이 추가되었다.
• isPresent() 는 Optional 이 값을 포함하면 true, 포함하지 않으면 false
• ifPresent(Consumer<T> block) 은 값이 있으면 주어진 블록을 실행
• T get() 은 값이 존재하면 값을 반환, 없으면 NoSuchElementException 발생
• T orElse(T other) 는 값이 있으면 값을 반환, 없으면 기본값 반환
menu.stream()
.filter(Dish::isVegetarian)
.findAny() // Optional<Dish> 반환
.ifPresent(d -> System.out.println(d.getName())); // 값이 있을 때에 출력
Optional
3. 스트림
스트림의 연산에서 중간 연산은 한 스트림을 다른 스트림으로 변환하는 연산으로
여러 연산을 연결할 수 있다. 최종 연산은 스트림의 요소를 소비해서 최종 결과를
도출한다. 이 때 최종 연산은 스트림의 파이프라인을 최적화하면서 계산 과정을 짧
게 생략하기도 한다.
앞의 예시 코드에서 스트림의 최종 연산 collect 의 사용법이 제시되었다. 그러나
예시에서 사용된 toList 말고도 다양한 요소 누적 방식이 존재한다.
다양한 요소 누적 방식은 Collector 인터페이스에 정의 되어 있다.
Collector Interface
3. 스트림
import static java.util.stream.Collectors.toList;
List<String> dishNames = menu.stream().map(Dish::getName).collect(toList());
스트림에 collect를 호출하면 스트림의 요소에 리듀싱 연산이 수행된다. 이 때
collect 메서드로 전달된 Collector 인터페이스의 구현에 따라 스트림의 각 요소를
방문하면서 작업을 처리한다.
toList() 의 경우 Collectors 유틸리티 클래스에서 제공하는 정적 팩토리 메서드이다.
toList() 의 경우 스트림의 모든 요소를 리스트로 수집한다.
Collectors 클래스에서 자주 쓰이는 정적 팩토리 메서드는 다음과 같다
• toList, toSet
• joining, counting
• summingInt, summingLong, summingDouble
• averagingInt, averagingLong, averagingDouble
• minBy, maxBy
• groupingBy, partitioningBy 등
Collector Interface
3. 스트림
Stream.collect 메서드의 인수인 Collector Interface 의 구현체로 스트림의 항목을
컬렉션으로 재구성할 수 있다. 좀 더 일반적으로 말해 컬렉터로 스트림의 모든 항
목을 하나의 결과로 합칠 수 있다.
long howManyDishes = menu.stream().collect(Collectors.counting());
// Collectors.maxBy 는 스트림의 요소를 비교하는 데 사용할
// Comparator 를 인수로 받는다.
Comparator<Dish> dishCaloriesComparator =
Comparator.comparingInt(Dish::getCalories);
Optional<Dish> mostCalorieDish =
menu.stream().collect(maxBy(dishCaloriesComparator));
// Collectors.summingInt 는 객체를 int 로 매핑하는 함수를 인수로 받는다.
int totalCalories = menu.stream().collect(summingInt(Dish::getCalories));
Collector Interface
3. 스트림
// joining 메서드는 내부적으로 StringBuilder 를 이용하여 문자열을 하나로 만든다.
// 필요시 연결된 두 요소 사이에 구분 문자열을 넣을 수 있다.
String shortMenu = menu.stream().map(Dish::getName).collect(joining());
String shortMenu = menu.stream().map(Dish::getName).collect(joining(", "));
Collectors.groupingBy 를 이용하여 그룹화가 가능하다. 이 때 groupingBy 에는 분
류 기준이 되는 속성 접근자 등을 전달한다.
Map<Dish.Type, List<Dish>> dishesByType =
menu.stream().collect(groupingBy(Dish::getType));
// 결과: {FISH=[prawns, salmon], OTHER=[french fries, rice], MEAT=[pork, beef]}
그룹화 연산의 결과로 그룹화 함수가 반환하는 키와 각 키에 대응하는 스트림의 모
든 항목 리스트를 값으로 갖는 맵이 반환된다.
Collector Interface
3. 스트림
// 다수준 그룹화 - 일반적인 분류 함수와 컬렉터를 인수로 받는다.
menu.stream().collect(groupingBy(Dish::getType), // 첫번째 수준의 분류 함수
groupingBy(dish -> { // 두번째 수준의 분류 함수
if(dish.getCalories() <= 400){
return CaloricLevel.DIET;
}else if(dish.getCalories() <= 700){
return CaloricLevel.NORMAL;
}else{
return CaloricLevel.FAT;
}
})
);
// 결과 { MEAT={DIET=[chicken], NORMAL=[beef], FAT=[pork]},
// FISH={DIET=[prawns]} ... }
Collector Interface
그 외
• Stream – parallelStream()
• ForkJoinSumCalculator
• Stream – partitioningBy
• Functional Interface – default method
• Collector – supplier, accumulator, finisher, combiner
Thank you

Weitere ähnliche Inhalte

Was ist angesagt?

java 8 람다식 소개와 의미 고찰
java 8 람다식 소개와 의미 고찰java 8 람다식 소개와 의미 고찰
java 8 람다식 소개와 의미 고찰Sungchul Park
 
씹고 뜯고 맛보고 즐기는 스트림 API
씹고 뜯고 맛보고 즐기는 스트림 API씹고 뜯고 맛보고 즐기는 스트림 API
씹고 뜯고 맛보고 즐기는 스트림 APIArawn Park
 
Java 8 api :: lambda 이용하기
Java 8 api :: lambda 이용하기Java 8 api :: lambda 이용하기
Java 8 api :: lambda 이용하기rupert kim
 
이것이 자바다 Chap.14 람다식 Lambda expression(java)(KOR)
이것이 자바다 Chap.14 람다식 Lambda expression(java)(KOR)이것이 자바다 Chap.14 람다식 Lambda expression(java)(KOR)
이것이 자바다 Chap.14 람다식 Lambda expression(java)(KOR)MIN SEOK KOO
 
자바8강의 0강. java8 overview
자바8강의 0강. java8 overview자바8강의 0강. java8 overview
자바8강의 0강. java8 overviewSejong Park
 
자바8강의 1강. lambda expression
자바8강의 1강. lambda expression자바8강의 1강. lambda expression
자바8강의 1강. lambda expressionSejong Park
 
모델링 연습 리뷰
모델링 연습 리뷰모델링 연습 리뷰
모델링 연습 리뷰beom kyun choi
 
Javascript 완벽 가이드 정리
Javascript 완벽 가이드 정리Javascript 완벽 가이드 정리
Javascript 완벽 가이드 정리ETRIBE_STG
 
자바스크립트 함수
자바스크립트 함수자바스크립트 함수
자바스크립트 함수유진 변
 
Javascript 교육자료 pdf
Javascript 교육자료 pdfJavascript 교육자료 pdf
Javascript 교육자료 pdfHyosang Hong
 
SpringCamp 2013 : About Jdk8
SpringCamp 2013 : About Jdk8SpringCamp 2013 : About Jdk8
SpringCamp 2013 : About Jdk8Sangmin Lee
 
(망작)이것이 자바다 Chap.16 스트림&병렬처리 Stream&parallel processing(java)
(망작)이것이 자바다 Chap.16 스트림&병렬처리 Stream&parallel processing(java)(망작)이것이 자바다 Chap.16 스트림&병렬처리 Stream&parallel processing(java)
(망작)이것이 자바다 Chap.16 스트림&병렬처리 Stream&parallel processing(java)MIN SEOK KOO
 
나에 첫번째 자바8 람다식 지앤선
나에 첫번째 자바8 람다식   지앤선나에 첫번째 자바8 람다식   지앤선
나에 첫번째 자바8 람다식 지앤선daewon jeong
 
이것이 자바다 Chap.11 기본 API 클래스(java)(KOR)
이것이 자바다 Chap.11 기본 API 클래스(java)(KOR)이것이 자바다 Chap.11 기본 API 클래스(java)(KOR)
이것이 자바다 Chap.11 기본 API 클래스(java)(KOR)MIN SEOK KOO
 
스위프트 성능 이해하기
스위프트 성능 이해하기스위프트 성능 이해하기
스위프트 성능 이해하기Yongha Yoo
 

Was ist angesagt? (19)

java 8 람다식 소개와 의미 고찰
java 8 람다식 소개와 의미 고찰java 8 람다식 소개와 의미 고찰
java 8 람다식 소개와 의미 고찰
 
씹고 뜯고 맛보고 즐기는 스트림 API
씹고 뜯고 맛보고 즐기는 스트림 API씹고 뜯고 맛보고 즐기는 스트림 API
씹고 뜯고 맛보고 즐기는 스트림 API
 
Java 8 api :: lambda 이용하기
Java 8 api :: lambda 이용하기Java 8 api :: lambda 이용하기
Java 8 api :: lambda 이용하기
 
JDK 변천사
JDK 변천사JDK 변천사
JDK 변천사
 
이것이 자바다 Chap.14 람다식 Lambda expression(java)(KOR)
이것이 자바다 Chap.14 람다식 Lambda expression(java)(KOR)이것이 자바다 Chap.14 람다식 Lambda expression(java)(KOR)
이것이 자바다 Chap.14 람다식 Lambda expression(java)(KOR)
 
Just java
Just javaJust java
Just java
 
자바8강의 0강. java8 overview
자바8강의 0강. java8 overview자바8강의 0강. java8 overview
자바8강의 0강. java8 overview
 
5 swift 기초함수
5 swift 기초함수5 swift 기초함수
5 swift 기초함수
 
자바8강의 1강. lambda expression
자바8강의 1강. lambda expression자바8강의 1강. lambda expression
자바8강의 1강. lambda expression
 
6 swift 고급함수
6 swift 고급함수6 swift 고급함수
6 swift 고급함수
 
모델링 연습 리뷰
모델링 연습 리뷰모델링 연습 리뷰
모델링 연습 리뷰
 
Javascript 완벽 가이드 정리
Javascript 완벽 가이드 정리Javascript 완벽 가이드 정리
Javascript 완벽 가이드 정리
 
자바스크립트 함수
자바스크립트 함수자바스크립트 함수
자바스크립트 함수
 
Javascript 교육자료 pdf
Javascript 교육자료 pdfJavascript 교육자료 pdf
Javascript 교육자료 pdf
 
SpringCamp 2013 : About Jdk8
SpringCamp 2013 : About Jdk8SpringCamp 2013 : About Jdk8
SpringCamp 2013 : About Jdk8
 
(망작)이것이 자바다 Chap.16 스트림&병렬처리 Stream&parallel processing(java)
(망작)이것이 자바다 Chap.16 스트림&병렬처리 Stream&parallel processing(java)(망작)이것이 자바다 Chap.16 스트림&병렬처리 Stream&parallel processing(java)
(망작)이것이 자바다 Chap.16 스트림&병렬처리 Stream&parallel processing(java)
 
나에 첫번째 자바8 람다식 지앤선
나에 첫번째 자바8 람다식   지앤선나에 첫번째 자바8 람다식   지앤선
나에 첫번째 자바8 람다식 지앤선
 
이것이 자바다 Chap.11 기본 API 클래스(java)(KOR)
이것이 자바다 Chap.11 기본 API 클래스(java)(KOR)이것이 자바다 Chap.11 기본 API 클래스(java)(KOR)
이것이 자바다 Chap.11 기본 API 클래스(java)(KOR)
 
스위프트 성능 이해하기
스위프트 성능 이해하기스위프트 성능 이해하기
스위프트 성능 이해하기
 

Ähnlich wie 자바 8 학습

Scala 기초 (2)
Scala 기초 (2)Scala 기초 (2)
Scala 기초 (2)명성 정
 
Scala 기초 (3)
Scala 기초 (3)Scala 기초 (3)
Scala 기초 (3)명성 정
 
Java jungsuk3 ch14_lambda_stream
Java jungsuk3 ch14_lambda_streamJava jungsuk3 ch14_lambda_stream
Java jungsuk3 ch14_lambda_stream성 남궁
 
자바 8
자바 8자바 8
자바 8신 한
 
PySpark 배우기 Ch 06. ML 패키지 소개하기
PySpark 배우기 Ch 06. ML 패키지 소개하기PySpark 배우기 Ch 06. ML 패키지 소개하기
PySpark 배우기 Ch 06. ML 패키지 소개하기찬희 이
 
[HaU] 신입 기술 면접 준비 java
[HaU] 신입 기술 면접 준비 java[HaU] 신입 기술 면접 준비 java
[HaU] 신입 기술 면접 준비 java유리 하
 
Java8 - Oracle Korea Magazine
Java8 - Oracle Korea MagazineJava8 - Oracle Korea Magazine
Java8 - Oracle Korea MagazineJay Lee
 
R 프로그램의 이해와 활용 v1.1
R 프로그램의 이해와 활용 v1.1R 프로그램의 이해와 활용 v1.1
R 프로그램의 이해와 활용 v1.1happychallenge
 
Start IoT with JavaScript - 6.함수
Start IoT with JavaScript - 6.함수Start IoT with JavaScript - 6.함수
Start IoT with JavaScript - 6.함수Park Jonggun
 
C# 개요 및 소개 [ 유니티 및 C# 스터디 / 2024-04-19 ]
C# 개요 및 소개 [ 유니티 및 C# 스터디 / 2024-04-19 ]C# 개요 및 소개 [ 유니티 및 C# 스터디 / 2024-04-19 ]
C# 개요 및 소개 [ 유니티 및 C# 스터디 / 2024-04-19 ]leusin2
 
애플이 스위프트 프로그래밍 언어를 위해 "훔친" 몇 가지 기능
애플이 스위프트 프로그래밍 언어를 위해 "훔친" 몇 가지 기능애플이 스위프트 프로그래밍 언어를 위해 "훔친" 몇 가지 기능
애플이 스위프트 프로그래밍 언어를 위해 "훔친" 몇 가지 기능ETRIBE_STG
 
2014.07.26 KSUG와 지앤선이 함께하는 테크니컬 세미나 - 나의 첫번째 자바8 람다식 (정대원)
2014.07.26 KSUG와 지앤선이 함께하는 테크니컬 세미나 - 나의 첫번째 자바8 람다식 (정대원)2014.07.26 KSUG와 지앤선이 함께하는 테크니컬 세미나 - 나의 첫번째 자바8 람다식 (정대원)
2014.07.26 KSUG와 지앤선이 함께하는 테크니컬 세미나 - 나의 첫번째 자바8 람다식 (정대원)JiandSon
 
JavaInAction 자바 8
JavaInAction 자바 8JavaInAction 자바 8
JavaInAction 자바 8NBT Inc.
 

Ähnlich wie 자바 8 학습 (20)

Java(3/4)
Java(3/4)Java(3/4)
Java(3/4)
 
Scala 기초 (2)
Scala 기초 (2)Scala 기초 (2)
Scala 기초 (2)
 
Scala 기초 (3)
Scala 기초 (3)Scala 기초 (3)
Scala 기초 (3)
 
Java jungsuk3 ch14_lambda_stream
Java jungsuk3 ch14_lambda_streamJava jungsuk3 ch14_lambda_stream
Java jungsuk3 ch14_lambda_stream
 
Java_10 람다
Java_10 람다Java_10 람다
Java_10 람다
 
Java lambda
Java lambdaJava lambda
Java lambda
 
Linq
LinqLinq
Linq
 
자바 8
자바 8자바 8
자바 8
 
PySpark 배우기 Ch 06. ML 패키지 소개하기
PySpark 배우기 Ch 06. ML 패키지 소개하기PySpark 배우기 Ch 06. ML 패키지 소개하기
PySpark 배우기 Ch 06. ML 패키지 소개하기
 
Java stream v0.1
Java stream v0.1Java stream v0.1
Java stream v0.1
 
Java stream v0.1
Java stream v0.1Java stream v0.1
Java stream v0.1
 
[HaU] 신입 기술 면접 준비 java
[HaU] 신입 기술 면접 준비 java[HaU] 신입 기술 면접 준비 java
[HaU] 신입 기술 면접 준비 java
 
Java8 - Oracle Korea Magazine
Java8 - Oracle Korea MagazineJava8 - Oracle Korea Magazine
Java8 - Oracle Korea Magazine
 
R 프로그램의 이해와 활용 v1.1
R 프로그램의 이해와 활용 v1.1R 프로그램의 이해와 활용 v1.1
R 프로그램의 이해와 활용 v1.1
 
Start IoT with JavaScript - 6.함수
Start IoT with JavaScript - 6.함수Start IoT with JavaScript - 6.함수
Start IoT with JavaScript - 6.함수
 
C# 개요 및 소개 [ 유니티 및 C# 스터디 / 2024-04-19 ]
C# 개요 및 소개 [ 유니티 및 C# 스터디 / 2024-04-19 ]C# 개요 및 소개 [ 유니티 및 C# 스터디 / 2024-04-19 ]
C# 개요 및 소개 [ 유니티 및 C# 스터디 / 2024-04-19 ]
 
애플이 스위프트 프로그래밍 언어를 위해 "훔친" 몇 가지 기능
애플이 스위프트 프로그래밍 언어를 위해 "훔친" 몇 가지 기능애플이 스위프트 프로그래밍 언어를 위해 "훔친" 몇 가지 기능
애플이 스위프트 프로그래밍 언어를 위해 "훔친" 몇 가지 기능
 
2014.07.26 KSUG와 지앤선이 함께하는 테크니컬 세미나 - 나의 첫번째 자바8 람다식 (정대원)
2014.07.26 KSUG와 지앤선이 함께하는 테크니컬 세미나 - 나의 첫번째 자바8 람다식 (정대원)2014.07.26 KSUG와 지앤선이 함께하는 테크니컬 세미나 - 나의 첫번째 자바8 람다식 (정대원)
2014.07.26 KSUG와 지앤선이 함께하는 테크니컬 세미나 - 나의 첫번째 자바8 람다식 (정대원)
 
JavaInAction 자바 8
JavaInAction 자바 8JavaInAction 자바 8
JavaInAction 자바 8
 
5.Spring IoC&DI(DI와 관련된 어노테이션)
5.Spring IoC&DI(DI와 관련된 어노테이션)5.Spring IoC&DI(DI와 관련된 어노테이션)
5.Spring IoC&DI(DI와 관련된 어노테이션)
 

자바 8 학습

  • 1. 자바 8 학습 (1) 참고 문헌 Java 8 In Action Functional Programming in Java 8 이희창 (daniel.hebn@gmail.com)
  • 2. 목차 1. 자바 8 의 특징 2. 람다 표현식 3. 스트림 4. 병렬 데이터 처리 5. 리펙토링, 디자인 패턴, 테스팅 6. 비동기 프로그래밍 4 ~ 6 은 자바 8 학습(2) 에서
  • 3. 1. 자바 8 의 특징 1. 고차 함수를 사용한 설계 (함수형 프로그래밍 지원) - 람다 표현식이라는 간단한 코드 전달 기법을 통해 손쉬운 동작 파라미터화를 가능하게 함 - 즉, 함수 자체를 파라미터로 전달 가능 - 기존 익명 클래스 등을 통한 코드 블록 전달과 비교 가능 2. 스트림 API - 스트림이란 한 번에 한 개씩 만들어지는 연속적인 데이터 항목들의 모임 - java.uti.stream 패키지에 스트림 API 가 추가됨 - 이를 통해 쉽고 명시적인 컬렉션의 이터레이션 및 병렬 처리 등을 지원함 3. 쉬운 병렬성 처리 가능 - 새로운 스트림 API 에서 병렬 처리를 통한 빠른 컬렉션 필터링 등이 가능 - 멀티 코어 활용의 극대화 가능 크게 보면
  • 4. 1. 자바 8 의 특징 함수형 스타일 코드는 투자 대비 높은 효율의 코드를 만든다. 1. 변수의 명시적인 변경이나 재할당의 문제를 피할 수 있다. - 가변 변수의 사용은 버그의 원천, 동시성을 갖기 어렵게 함 - 함수형 버전에서는 코드 내에 명시적인 변경이 없음 2. 쉽게 병렬화가 가능하다. - 스레드-세이프티 문제를 제거 3. 서술적인 코드 작성이 가능하다. - 문법보다는 표현에 주력 4. 고차 함수를 사용한 설계 - 함수 자체를 파라미터로 전달 가능 함수형 스타일
  • 5. 1. 자바 8 의 특징 동작 파라미터화 동작 파라미터화란 아직은 어떻게 실행할 것인지 결정하지 않은 코드 블록을 의미 한다. 이 코드 블록은 나중에 프로그램에서 호출한다. 즉, 코드 블록의 실행은 나중으로 미뤄진다. 변화하는 요구사항에 유연하게 대응하기 위해 동작 파라미터화를 이용할 때가 많 다. 그러나 기존 자바에서 메서드는 객체만 인수로 받으므로 동작을 전달하기 위해 서는 메서드에서 인터페이스를 파라미터로 받도록 선언하고 이를 구현한 객체를 전달하거나 익명 클래스를 통해 전달하기도 한다. 그러나 이런 식의 구현은 동작의 핵심 이외의 장황한 코드가 필연적이므로 구현하 고 유지보수하는 데 어려움이 많다.  자바8 에서는 람다 표현식이라는 간단한 코드 전달 기법을 통해 이를 해결할 수 있도록 한다.
  • 6. 2. 람다 표현식 정의 람다 표현식은 메서드로 전달할 수 있는 익명 함수를 단순화한 것 (기존 자바에서는 익명 클래스 등을 활용하여 코드 블록을 전달함) 1. 익명: 보통의 메서드와 달리 이름이 없으므로 익명이라 표현함 2. 함수: 메서드처럼 특정 클래스에 종속되지 않으므로 함수라고 표현함 3. 전달: 람다 표현식을 메서드 인수로 전달하거나 변수로 저장할 수 있음 4. 간결성: 익명 클래스처럼 핵심 이외의 코드를 추가로 구현할 필요가 없음
  • 7. 2. 람다 표현식 기본 문법 (parameters) -> expression 또는 (parameters) -> { statements; } 람다 예제 • (List<String> list) -> list.isEmpty() • ( ) -> new Apple(10) • (Apple a) -> System.out.println(a.getWeight()) • (String s) -> s.length() • (int a, int b) -> a * b • (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight())
  • 8. 2. 람다 표현식 함수형 인터페이스 (Functional Interface) 함수형 인터페이스는 정확히 하나의 추상 메서드를 지정하는 인터페이스이다. 람다 표현식은 함수형 인터페이스라는 문맥에서 사용 가능하다. public static void process(Runnable r){ r.run(); } Runnable r1 = ( ) -> System.out.println("Hello World 1"); // 람다 표현식 process(r1); // 함수형 인터페이스를 인수로 받으므로 람다 표현식 전달 가능 process(( ) -> System.out.println("Hello World 2")); // 직접 람다 표현식 전달 가능  Runnable 인터페이스는 run( ) 이라는 abstract 메서드를 가지는 함수형 인터페 이스이다.  process(Runnable r) 메서드는 Runnable 이라는 함수형 인터페이스를 파라미터 로 가지므로 람다식 전달이 가능하다.
  • 9. 2. 람다 표현식 함수형 인터페이스와 람다 표현식 1. 람다 표현식은 변수에 할당하거나 2. 함수형 인터페이스를 인수로 받는 메서드로 전달할 수 있으며 3. 함수형 인터페이스의 추상 메서드와 같은 시그니처를 갖는다는 것 을 기억하면 충분
  • 10. 2. 람다 표현식 람다 활용: 실행 어라운드 패턴 (execute around pattern) (데이터베이스의 파일 처리와 같은) 자원을 처리하는 코드를 설정(setup)과 정리(cleanup) 두 과정이 둘러싸는 형태를 실행 어라운드 패턴 이라고 부른다. public static String processFile() throws IOException { try(BufferedReader br = new BufferedReader(new FileReader("data.txt"))) { return br.readLine(); // 실제 필요 작업 실행 코드 } }  파일에서 한 행을 읽는 코드이다. 한 번에 두 줄을 읽거나 자주 사용되는 단어 등을 반환하는 등의 처리를 하려면 어떻게 해야 할까?  기존의 설정, 정리 과정은 재사용하고 processFile 메서드만 다른 동작으로 갈아 끼울 수 있다면?
  • 11. 2. 람다 표현식 람다 활용: 실행 어라운드 패턴 (execute around pattern) 1단계: 동작 파라미터화를 기억하라 기존의 설정, 정리 과정을 재사용하고 processFile 메서드만 다른 동작을 수행하도 록 명령하려면 processFile 의 동작을 파라미터화하여 전달하면 가능하다. 람다를 이용하면 동작 전달이 가능하다. 만약 processFile 메서드가 한 번에 두 행을 읽도록 하려면 BufferedReader 를 인수로 받아서 String 을 반환하는 람다식이 필요하다. String twoLines = processFile( (BufferedReader br) -> br.readLine() + br.readLine() );
  • 12. 2. 람다 표현식 람다 활용: 실행 어라운드 패턴 (execute around pattern) 2단계: 함수형 인터페이스를 이용해서 동작 전달 함수형 인터페이스 자리에 람다를 사용할 수 있다. 따라서 BufferedReader -> String 과 IOException 을 throw 할 수 있는 시그니처와 일치하는 함수형 인터페이스를 만들어야 한다. @FunctionalInterface public interface BufferedReaderProcessor { String process(BufferedReader br) throws IOException; } 정의한 인터페이스를 다음의 processFile 메서드의 인수로 전달할 수 있다. public static String processFile(BufferedReaderProcessor p) throws IOException{ // … }
  • 13. 2. 람다 표현식 람다 활용: 실행 어라운드 패턴 (execute around pattern) 3단계: 동작 실행! 이제 BufferedReaderProcessor 에 정의된 process 메서드의 시그니처 (BufferedReader -> String) 와 일치하는 람다를 전달할 수 있다. 람다 표현식으로 함수형 인터페이스의 추상 메서드 구현을 직접 전달할 수 있으며 전달된 코드는 함수형 인터페이스의 인스턴스로 전달된 코드와 같은 방식으로 처 리한다. public static String processFile(BufferedReaderProcessor p) throws IOException { try(BufferedReader br = new BufferedReader(new FileReader("data.txt"))) { return p.process(br); } }
  • 14. 2. 람다 표현식 람다 활용: 실행 어라운드 패턴 (execute around pattern) 4단계: 람다 전달 이제 람다를 이용해서 다양한 동작을 processFile 메서드로 전달할 수 있다. // 한 행 처리 String oneLine = processFile((BufferedReader br )-> br.readLine()); // 두 행 처리 String twoLines = processFile((BufferedReader br )-> br.readLine() + br.readLine());
  • 15. 2. 람다 표현식 자바 8 에서 제공하는 함수형 인터페이스 함수형 인터페이스는 오직 하나의 추상 메서드를 가지고, 추상 메서드는 람다 표현식의 시그니처를 묘사한다. 함수형 인터페이스의 추상 메서드 시그니처를 함수 디스크립터 (function descriptor) 라고 한다. (이를 통해 자바 컴파일러가 함수형 인터페이스에 전달된 람다식을 해석하여 생략 된 타입을 추론하기도 한다) 이미 자바 API 는 Comparable, Runnable, Callable 등의 다양한 함수형 인터페이스 를 포함하고 있다. 그러나 자바 8 부터 java.util.function 패키지로 여러 가지 새로운 함수형 인터페이 스가 제공된다.
  • 16. 2. 람다 표현식 Predicate java.util.function.Predicate<T> 인터페이스는 test 라는 추상메서드를 정의하고 Boolean 을 리턴한다. T 형식의 객체를 사용하는 boolean 표현식이 필요한 상황에서 Predicate 인터페이 스 사용이 가능하다. public static <T> List<T> filter(List<T> list, Predicate<T> p){ List<T> results = new ArrayList<>(); for(T s : list){ if( p.test(s)) { results.add(s); } } return results; } final List<String> startsWithL = friends.stream().filter(name -> name.startsWith(“L"));  filter 의 인자는 Predicate 이고 전달된 람다식 name -> name.startsWith(“L") 는 컴파일러에 의해 Predicate 인터페이스의 구현체로 변환된다.
  • 17. 2. 람다 표현식 Consumer java.util.function.Consumer<T> 인터페이스는 제네릭 형식 T 객체를 받아서 void 를 반환하는 accept 라는 추상 메서드를 정의한다. T 형식의 객체를 인수로 받아서 어떤 동작을 수행하고 싶을 때 Consumer 인터페 이스를 사용한다. public static <T> void forEach(List<T> list, Consumer<T> c){ for(T i : list){ c.accept(i); } } // 파라미터 타입 생략 가능 – 자동 추론 friends.forEach(name -> System.out.println(name));
  • 18. 2. 람다 표현식 Function java.util.function.Function<T, R> 인터페이스는 제네릭 형식 T 를 인수로 받아서 제 네릭 형식 R 객체를 반환하는 apply 라는 추상메서드를 정의한다. 다음은 String 리스트를 인수로 받아 각 String 길이를 포함하는 Integer 리스트로 변환하는 map 메서드 예시이다. public static <T, R> List<R> map(List<T> list, Function<T, R> f){ List<R> result = new ArrayList<>(); for(T s : list){ result.add( f.apply(s) ); } return result; } List<Integer> length = map(Arrays.asList("Apple", "Banana", "Orange"), (String s) -> s.length());
  • 19. 2. 람다 표현식 기본형 특화 자바의 모든 형식은 참조형(reference type) 아니면 기본형(pimitive type) 에 해당 한다. 자바에서는 기본형을 참조형으로 변환할 수 있는 오토박싱(autoboxing) 이라 는 기능을 제공한다. 하지만 이런 변환 과정은 박싱한 래퍼를 힙에 저장하고, 또한 기본형을 가져올 때 도 메모리를 탐색하는 과정이 필요하다.  일종의 비용 소모 다음은 String 리스트를 인수로 받아 각 String 길이를 포함하는 Integer 리스트로 변환하는 map 메서드 예시이다. 자바 8 에서는 기본형을 입출력으로 사용하는 상황에서 오토박싱 동작을 피할 수 있는 특별한 버전의 함수형 인터페이스를 제공한다. Ex) IntPredicate vs Predicate<Integer> IntPredicate 는 1000 이라는 값을 박싱하지 않지만, Predicate<Integer>는 Integer 객체로 박싱한다.
  • 20. 2. 람다 표현식 자바 8 의 대표적인 함수 인터페이스 함수형 인터페이스 함수 디스크립터 기본형 특화 Predicate<T> T -> boolean IntPredicate, LongPredicate, DoublePredicate Consumer<T> T -> void IntConsumer, LongConsumer, DoubleConsumer Function<T, R> T -> R IntFunction<R>, IntToDoubleFunction, IntToLongFunction 등 Supplier<T> ( ) -> T BooleanSupplier, IntSupplier, LongSupplier 등 UnaryOperator<T> T -> T IntUnaryOperator, LongUnaryOperator 등 BinaryOperator<T> (T, T) -> T IntBinaryOperator, LongBinaryOperator 등 BiPredicate<L, R> (L, R) -> boolean BiConsumer<T, U> (T, U) -> void ObjIntConsumer<T>, ObjLongConsumer<T> 등 BiFunction<T, U, R> (T, U) -> R ToIntBiFunction<T>, ToLongBiFunction<T> 등
  • 21. 2. 람다 표현식 형식 검사 람다로 함수형 인터페이스의 인스턴스를 만들 수 있다. 그러나 람다 표현식 자체에는 람다가 어떤 함수형 인터페이스를 구현하는지의 정 보는 담겨있지 않다. 이 때 람다가 사용되는 컨텍스트(메서드 파라미터, 할당되는 변수 등) 를 이용해서 람다의 형식을 추론할 수 있다. 어떤 컨텍스트에서 기대되는 람다 표현식의 형식을 대상 형식(target type)이라고 부른다. 다음의 예를 통해 형식 확인 과정을 파악한다. List<Apple> heavierThan150g = filter(inventory, (Apple a) -> a.getWeight() > 150);
  • 22. 2. 람다 표현식 형식 검사 List<Apple> heavierThan150g = filter(inventory, (Apple a) -> a.getWeight() > 150); 1. filter 메서드의 선언을 확인 2. filter 메서드는 두 번째 파라미터로 Predicate<Apple>형식(대상 형식)을 기대함 3. Predicate<Apple>은 test라는 한 개의 추상메서드를 정의함(함수형 인터페이스) 4. test 메서드는 Apple 을 받아 boolean 을 반환함 5. filter 메서드로 전달된 인수는 이와 같은 요구사항을 만족해야함  위 예제에서 전달된 람다식은 Apple 을 인수로 받아 boolean 을 반환하므로 유 효한 코드임
  • 23. 2. 람다 표현식 형식 검사 대상 형식 이라는 특징 때문에 같은 람다식이라도 호환되는 추상 메서드를 가진 다 른 함수형 인터페이스로 사용이 가능하다. Comparator<Apple> c1 = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()); ToIntBiFunction<Apple, Apple> c2 = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()); BiFunction<Apple, Apple, Integer> c3 = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
  • 24. 2. 람다 표현식 형식 추론 자바 컴파일러는 람다 표현식이 사용된 컨텍스트(대상 형식)를 이용해서 람다 표현 식과 관련된 함수형 인터페이스를 추론한다. 이를 통해 람다식의 코드를 좀 더 단순화할 수 있다. List<Apple> greenApples = filter(inventory, (Apple a) -> "green".equals(a.getColor())); // 파라미터 a 의 Type 인 Apple 선언 생략 List<Apple> greenApples = filter(inventory, a -> "green".equals(a.getColor())); 자바 컴파일러가 명시적으로 형식의 추론이 가능하다면 람다 표현식의 간략한 작 성이 가능하다.
  • 25. 2. 람다 표현식 메서드 레퍼런스 자바 8 에서 제공하는 메서드 레퍼런스를 이용하면 기존의 메서드 정의를 재활용 하여 람다처럼 전달할 수 있다. 메서드 레퍼런스는 특정 메서드만을 호출하는 람다의 축약형이라고 생각할 수 있 다. inventory.sort((Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight())); // 메서드 레퍼런스와 java.util.Comparator.comparing 을 활용 inventory.sort(comparing(Apple::getWeight)); 여기서 Apple::getWeight 는 (Apple a) -> a.getWeight() 를 축약한 것이다.
  • 26. 3. 스트림 스트림은 자바 API 에 새로 추가된 기능으로, 스트림을 이용하면 선언적인 코 딩으로 컬렉션 데이터를 처리할 수 있다. 또한 스트림을 활용하면 멀티쓰레드 코드를 구현하지 않아도 데이터를 쉽게 병렬처리 할 수 있다. 1. 선언형 코드 구현이 가능하다. 루프와 if 조건문 등을 통해 어떻게 구현할 지 작성할 필요 없이 ‘특정 조건만 선택하라’ 등과 같은 동작의 수행을 지 정할 수 있다. 2. filter, sorted, map, collect 와 같은 여러 빌등 블록 연산을 연결해서 복잡한 데이터 처리 파이프라인을 만들 수 있다 3. 멀티코어 아키텍처를 최대한 투명하게 활용할 수 있도록 구현되어 있다. 데이터 처리 과정을 병렬화하면서 쓰레드와 락을 신경 쓸 필요가 없다. 정의 및 특징
  • 27. 3. 스트림 • 연속된 요소 컬렉션과 마찬가지로 스트림은 특정 요소 형식으로 이루어진 연속된 값 집합의 인터페이스를 제공 • 소스 스트림은 컬렉션, 배열, I/O 자원 등의 데이터 제공 소스로부터 데이터를 소비한다. • 데이터 처리 연산 filter, map, reduce, find, match, sort 등으로 데이터를 조작할 수 있다. • 파이프라이닝 스트림 연산은 스트림 연산끼리 연결해서 커다란 파이프라인을 구성할 수 있다. 이를 통해 lazy 연산 등이 가능하다. 구성 요소
  • 28. 3. 스트림 • 내부 반복 반복자를 이용해서 명시적으로 반복하는 컬렉션과 달리 스트림은 내부 반복을 지원한다. 내부 반복이란 라이브러리 내에서 알아서 반복을 처리하고 결과 스트림 값을 어딘가에 저장해준다는 의미이다. List<String> threeHighCaloricDishNames = menu.stream() .filter(d -> d.getCalories() > 300) .map(Dish::getName) .limit(3) .collect(toList());  collect 연산을 제외한 모든 연산은 서로 파이프라인을 형성할 수 있 도록 스트림을 반환하고, collect 연산 호출 시 최종 결과를 반환한다. 이 때의 결과물은 스트림이 아니라 List 가 반환된다. 구성 요소
  • 29. 3. 스트림 스트림 인터페이스의 연산은 크게 중간 연산(intermediate operation) 과 최종 연산(ternimal operation) 두 가지로 구분이 가능하다. • 중간 연산: 최종 연산이 스트림 파이프라인에서 실행하기 전까지는 아무 연 산도 수행되지 않는다(lazy)는 것이 특징이다. 중간 연산을 합친 다음 합 쳐진 중간 연산을 최종 연산으로 한번에 처리하기 때문이다. • 최종 연산: 최종 연산에 의해 반환되는 결과는 List, Integer 등 스트림 이외의 결과가 반환된다.  최종 연산 실행 전까지 실행을 늦추게 되면 최종 연산의 결과를 만족하는 최소한의 작업만 실행 할 수 있으므로 효율적인 처리가 가능하다. 예를 들어, limit(3) 과 같은 연산이 포함된 경우 앞의 스트림 처리 결과 를 만족시키는 3개의 데이터만 찾게 되면 그 이상 스트림 처리 작업을 할 필요가 없다. 스트림 연산
  • 30. 3. 스트림 // 필터링 List<Dish> vegetarianMenu = menu.stream() .filter(Dish::isVegetarian) .collect(toList()); // 고유 요소 필터링 - distinct() 활용 List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4); numbers.stream().filter(i -> i % 2 == 0) .distinct() .forEach(System.out::println); // 스트림 축소 List<Dish> dishes = menu.stream().filter(d -> d.getCalories() > 300) .limit(3) .collect(toList()); 스트림 활용: 필터링과 슬라이싱
  • 31. 3. 스트림 // 매핑: 주어진 함수를 컬렉션의 각 엘리먼트에 적용하고 결과 컬렉션을 생성 List<String> dishNames = menu.stream() .map(Dish::getName) .collect(toList()); // 각 단어의 글자 수의 리스트 반환 List<String> words = Arrays.asList("IDINCU", "Opensurvey", "Ovey"); List<Integer> wordLengths = words.stream() .map(String::length) .collect(toList()); // ["Hello" , "World"] 리스트에서 고유 문자만 추출. ["H","e","l","o","W","r","d"] 얻기 // flatMap 사용: 각 배열을 스트림이 아니라 스트림의 컨텐츠로 매핑한다. List<String> words = Arrays.asList("Hello" , "World"); List<String> uniqueCharacters = words.stream() .map(w -> w.split("")) .flatMap(Arrays::stream) .distinct().collect(Collectors.toList()); 스트림 활용: 매핑
  • 32. 3. 스트림 // anyMatch 는 boolean 을 return 하므로 termination operation if (menu.stream().anyMatch(Dish::isVegetarian)){ System.out.println("This menu is Vegetarian friendly"); } // allMatch, noneMatch boolean isHealthy = menu.stream().allMatch(d -> d.getCalories() < 1000); boolean isHealthy = menu.stream().noneMatch(d -> d.getCalories() >= 1000); // filter 와 findAny 의 조합 Optional<Dish> dish = menu.stream() .filter(Dish::isVegetarian) .findAny(); // findFirst List<Integer> someNumbers = Arrays.asList(1,2,3,4,5); Optional<Integer> firstSquareDivisibleByThree = someNumbers.stream() .map(x -> x * x) .filter(x -> x % 3 == 0).findFirst(); 스트림 활용: 검색과 매칭
  • 33. 3. 스트림 ‘메뉴의 모든 칼로리의 합계를 구하시오’, ‘메뉴에서 칼로리가 가장 높은 요리는?’ 과 같이 스트림 요소를 조합해서 더 복잡한 질의를 표현하는 연산을 리듀싱 이라고 한다. reduce 는 두 개의 인수를 갖는다. • 초기값 • 두 요소를 조합해서 새로운 값을 만드는 BinaryOperator<T> reduce 는 스트림이 하나의 값으로 줄어들 때까지 각 요소를 반복해서 조합한다. int sum = numbers.stream().reduce(0, (a, b) -> a + b); // 람다식 활용 int sum = someNumbers.stream().reduce(0, Integer::sum); // 메서드 레퍼런스 활용 초기값을 받지 않도록 오버로드된 reduce 도 있다. 이 reduce 는 Optional 객체를 반환한다. (Optional 은 뒤에서 설명) 스트림 활용: 리듀싱
  • 34. 3. 스트림 Optional<T> 클래스(java.util.Optional) 는 값의 존재나 부재 여부를 표현하는 컨테 이너 클래스다. Null 은 쉽게 에러를 일으킬 수 있으므로 자바 8 에서 Optional<T> 이 추가되었다. • isPresent() 는 Optional 이 값을 포함하면 true, 포함하지 않으면 false • ifPresent(Consumer<T> block) 은 값이 있으면 주어진 블록을 실행 • T get() 은 값이 존재하면 값을 반환, 없으면 NoSuchElementException 발생 • T orElse(T other) 는 값이 있으면 값을 반환, 없으면 기본값 반환 menu.stream() .filter(Dish::isVegetarian) .findAny() // Optional<Dish> 반환 .ifPresent(d -> System.out.println(d.getName())); // 값이 있을 때에 출력 Optional
  • 35. 3. 스트림 스트림의 연산에서 중간 연산은 한 스트림을 다른 스트림으로 변환하는 연산으로 여러 연산을 연결할 수 있다. 최종 연산은 스트림의 요소를 소비해서 최종 결과를 도출한다. 이 때 최종 연산은 스트림의 파이프라인을 최적화하면서 계산 과정을 짧 게 생략하기도 한다. 앞의 예시 코드에서 스트림의 최종 연산 collect 의 사용법이 제시되었다. 그러나 예시에서 사용된 toList 말고도 다양한 요소 누적 방식이 존재한다. 다양한 요소 누적 방식은 Collector 인터페이스에 정의 되어 있다. Collector Interface
  • 36. 3. 스트림 import static java.util.stream.Collectors.toList; List<String> dishNames = menu.stream().map(Dish::getName).collect(toList()); 스트림에 collect를 호출하면 스트림의 요소에 리듀싱 연산이 수행된다. 이 때 collect 메서드로 전달된 Collector 인터페이스의 구현에 따라 스트림의 각 요소를 방문하면서 작업을 처리한다. toList() 의 경우 Collectors 유틸리티 클래스에서 제공하는 정적 팩토리 메서드이다. toList() 의 경우 스트림의 모든 요소를 리스트로 수집한다. Collectors 클래스에서 자주 쓰이는 정적 팩토리 메서드는 다음과 같다 • toList, toSet • joining, counting • summingInt, summingLong, summingDouble • averagingInt, averagingLong, averagingDouble • minBy, maxBy • groupingBy, partitioningBy 등 Collector Interface
  • 37. 3. 스트림 Stream.collect 메서드의 인수인 Collector Interface 의 구현체로 스트림의 항목을 컬렉션으로 재구성할 수 있다. 좀 더 일반적으로 말해 컬렉터로 스트림의 모든 항 목을 하나의 결과로 합칠 수 있다. long howManyDishes = menu.stream().collect(Collectors.counting()); // Collectors.maxBy 는 스트림의 요소를 비교하는 데 사용할 // Comparator 를 인수로 받는다. Comparator<Dish> dishCaloriesComparator = Comparator.comparingInt(Dish::getCalories); Optional<Dish> mostCalorieDish = menu.stream().collect(maxBy(dishCaloriesComparator)); // Collectors.summingInt 는 객체를 int 로 매핑하는 함수를 인수로 받는다. int totalCalories = menu.stream().collect(summingInt(Dish::getCalories)); Collector Interface
  • 38. 3. 스트림 // joining 메서드는 내부적으로 StringBuilder 를 이용하여 문자열을 하나로 만든다. // 필요시 연결된 두 요소 사이에 구분 문자열을 넣을 수 있다. String shortMenu = menu.stream().map(Dish::getName).collect(joining()); String shortMenu = menu.stream().map(Dish::getName).collect(joining(", ")); Collectors.groupingBy 를 이용하여 그룹화가 가능하다. 이 때 groupingBy 에는 분 류 기준이 되는 속성 접근자 등을 전달한다. Map<Dish.Type, List<Dish>> dishesByType = menu.stream().collect(groupingBy(Dish::getType)); // 결과: {FISH=[prawns, salmon], OTHER=[french fries, rice], MEAT=[pork, beef]} 그룹화 연산의 결과로 그룹화 함수가 반환하는 키와 각 키에 대응하는 스트림의 모 든 항목 리스트를 값으로 갖는 맵이 반환된다. Collector Interface
  • 39. 3. 스트림 // 다수준 그룹화 - 일반적인 분류 함수와 컬렉터를 인수로 받는다. menu.stream().collect(groupingBy(Dish::getType), // 첫번째 수준의 분류 함수 groupingBy(dish -> { // 두번째 수준의 분류 함수 if(dish.getCalories() <= 400){ return CaloricLevel.DIET; }else if(dish.getCalories() <= 700){ return CaloricLevel.NORMAL; }else{ return CaloricLevel.FAT; } }) ); // 결과 { MEAT={DIET=[chicken], NORMAL=[beef], FAT=[pork]}, // FISH={DIET=[prawns]} ... } Collector Interface
  • 40. 그 외 • Stream – parallelStream() • ForkJoinSumCalculator • Stream – partitioningBy • Functional Interface – default method • Collector – supplier, accumulator, finisher, combiner