chapter 4. Introducing streams
4.1 What are Streams
- 마치 SQL 쿼리 같이 데이터를 처리
- 많은 요소를 포함하는 커다란 컬렉션을 단순히 처리
Before(Java 7)
List<Dish> lowCaloricDishes = new ArrayList<>();
for(Dish d: menu){
if(d.getCalories() < 400){
lowCaloricDishes.add(d);
}
}
Collections.sort(lowCaloricDishes, new Comparator<Dish>() {
@Override
public int compare(Dish d1, Dish d2) {
return Integer.compare(d1.getCalories(), d2.getCalories());
}
});
List<String> lowCaloricDishesName = new ArrayList<>();
for(Dish d: lowCaloricDishes){
lowCaloricDishesName.add(d.getName());
}
자바8 코드
List<String> lowCaloricDishesName =
menu.stream()
//menu.parallelStream()
.filter(d -> d.getCalories() < 400)
.sorted(Comparator.comparing(Dish::getCalories))
.map(Dish::getName)
.collect(toList());
4.2 Getting started with streams
Sequence of elements
Collections are about data
streams are about computations
- Source
- 정렬된 컬렉션으로 스트림을 생성하면 정렬이 그대로 유지된다. 즉 리스트로 스트림을 만들면 스크림의 요소는 리스트의 요소와 같은 순서를 유지한다.
- Data processing operations
- 함수형 프로그래밍 언어에서 일반적으로 지원하는 연산과 데이터베이스와 비슷한 연산을 지원
- 예) filter, map, reduce, find, match, sort 등
- 스트림연산은 순차적으로 또는 병렬로 실행될 수 있다.
In addition
- Pipelining
- 스트림 연산 끼리 연결하여 커다란 파이프라인을 구성 할 수 있음.
- 게으름(laziness), 쇼트서킷(sort-cicuiting) 같은 최적화를 얻을 수 있음.
- 데이터베이스의 SQL 쿼리 와 비슷
- Internal iteration
- 반복자를 이용해서 명시적으로 반복하는 컬렉션과 달리 스트림은 내부 반복을 지원
List<String> threeHightCaloricDishNames =
menu.stream()
.filter(d -> d.getCalories() > 300)
.map(Dish::getName)
.limit(3)
.collect(toList());
filter
- 람다를 인수로 받아 스트림에서 특정 요소를 제외
filter(d -> d.getCalories() > 300)
map
- 람다를 이용해서 한 요소를 다른 요소로 변환하거나 정보를 추출
map(Dish::getName)
limit
- 정해진 개수 이상을 추출 못하게 한계를 지정
limit(3)
collect
- 스트림을 다른 형식으로 변환
collect(toList());
4.3 Streams vs. Collections
- DVD vs. Internet stream
- 모든 값을 메모리에 저장하여 계산 vs. 필요할때만 요소를 계산
브라우저 인터넷 검색
Traversavle only once
List<String> title = Arrays.asList("java8", "In", "Action");
Stream<String> s = title.stream();
s.forEach(System.out::println);
s.forEach(System.out::println);
output :
java8
Exception in thread "main" In
Action
java.lang.IllegalStateException: stream has already been operated upon or closed
at java.util.stream.AbstractPipeline.sourceStageSpliterator(AbstractPipeline.java:274)
at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
at Solution.IllegalStateException(Solution.java:77)
at Solution.main(Solution.java:102)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
External vs. internal iteration
for-each loop
List<String> names = new ArrayList<>();
for(Dish d: menu){
names.add(d.getName());
}
iterator
List<String> names = new ArrayList<>();
Iterator<String> iterator = menu.iterator();
while(iterator.hasNext()){
Dish d = iterator.next();
names.add(d.getName());
}
stream: internal iteration
List<String> names = menu.stream()
.map(Dish::getName)
.collct(toList());
4.4 Stream operations
intermediate operations
terminal operations
Intermediate operations
- 다른 intermediate operations 와의 상호연동을 통하여 pipe line 을 형성한다.
- intermediate operations의 동작들이 하나의 terminal operations으로 모이기 때문에 terminal operations이 호출되어서 파이프라인이 시작하기전 까지의 흐름이 동작하지 않는다( lazy)
- 파이프라인이 어떻게 동작되고 있는지 확인하기 위하여 각각의 람다함수에 print 문을 삽입한다.(이러한 코딩은 흔히 사용되지 않지만 배우는 과정이기 때문에 특별한 경우이다.)
List<String> names =
menu.stream()
.filter(d -> {
System.out.println("filtering " + d.getName());
return d.getCalories() > 300;
})
.map(d -> {
System.out.println("mapping " + d.getName());
return d.getName();
})
.limit(3)
.collect(toList());
System.out.println(names);
filtering pork
mapping pork
filtering beef
mapping beef
filtering chicken
mapping chicken
[pork, beef, chicken]
- 칼로리가 300보다 큰 dish 중에 오직 3개만 추출하고 동작이 끝난다.(Short-circuitinge)
filter 와 map 의 명령이 하나의 경로로 합쳐져서 동작한다.(loop fusion)
- Terminal operations
Stream pipe 로의 결과를 처리한다.
List 나 Integer, 혹은 void 등의 스트림이 아닌 다양한 포맷으로 데이터를 변환하여 리턴한다.
menu.stream().forEach(System.out::println);
- Working with streams
Builder pattern 과 비슷
- intermediate operations
- filter
- map
- limit
- sorted
- distinct
- Terminal operations
- forEach
- count
- collect
Summary
- stream은 데이터처리동작을 지원하는 source에서 연속된 요소이다.
- Stream은 내부반복을 사용한다.
- intermediate 와 terminal operations 이 있다.
- intermediate operations 은 filter와 map 등 stream을 반환하고 다른 stream와 연동된다. 파이프라인이 설정될뿐 어떤 result 동작을 하지 않는다.
- forEach 와 같은 terminal operations 은 스트림이 아닌 결과를 리턴한다.
스트림의 요소는 요청시에 계산된다.