chapter5 - 스트림 활용
- 주요 키워드
- 필터링, 슬라이싱, 매핑
- 검색, 매칭 리듀싱
- 특정 범위의 숫자와 같은 숫자 스트림 사용하기
- 다중 소스로부터 스트림 만들기
- 무한스트림
5.1 필터링과 슬라이싱
5.1.1 프레디케이트로 필터링
List<Dish> vegetarinMenu = menu.stream()
.filter(Dish::isVegetarian)
.collect(toList());
5.1.2 고유 요소 필터링
List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
numbers.stream()
.filter(i -> i % 2 == 0)
.distinct() // hashCode, equals로 결정 하는 고유 요소 필터링. 중복을 제거 한다.
.forEach(System.out::println);
5.1.3 스트림 축소
List<Dish> dishesLimit3 = menu.stream()
.filter(d -> d.getCalories() > 300)
.limit(3) // 최상위 3개의 요소를 반환한다.
.collect(toList());
dishesLimit3.forEach(System.out::println);
5.1.4 요소 건너뛰기
List<Dish> dishes = menu.stream()
.filter(d->d.getCalories() > 300)
.skip(2) //개의 요서를 건너 뛰어 나머지 요소를 반환한다.
.collect(toList());
5.2 매핑
5.2.1 스트림의 각 요소에 함수 적용하기
// map
List<String> words = Arrays.asList("Hello", "World");
List<Integer> wordLengths = words.stream()
.map(String::length)
.collect(toList());
System.out.println(wordLengths);
5.2.2 스트림 평면화
List<String> words = Arrays.asList("Hello", "World");
words.stream()
.flatMap((String line) -> Arrays.stream(line.split("")))
// string을 받아서 split하면 Stream<String[]>이 되는데 이것을 faultMap()으로 평면화하여 각각의 Stream<String>으로 변환 한다.
.distinct()
.forEach(System.out::println);
5.3 검색과 매칭
- allMatch, anyMatch, noneMatch, findFirst, findAny
5.3.1 프레디케이트가 적어도 한 요소와 일치하는지 확인
if(menu.stream().anyMatch(Dish::isVegetarian)){ //메뉴중 채소가 존재 하는가?
System.out.println("OK");
}
5.3.2 프레디케이트가 모든 요소와 일치하는지 확인
if( menu.stream().allMatch(d->d.getCalories() > 1000 ){ //메뉴가 모두 1000칼로리 이하인가?
System.out.println("OK");
}
if( menu.stream().noneMatch(d->d.getCalories() > 1000 ){ //메뉴가 모두 1000칼로리 초과 하는가?
System.out.println("OK");
}
5.3.3 요소검색
Optional<Dish> dish = menu.stream()
.filter(Dish::isVegetarian)
.findAny();
- Optional 이란?
- api doc : http://docs.oracle.com/javase/8/docs/api/java/util/Optional.html
- 값의 존재나 부재 여부를 표현하는 컨테이너 클래스다. ( A container object which may or may not contain a non-null value. )
- null은 에러를 발생 시키므로 이것을 방지하기 위해 1.8에서 추가 되었다.
| 메소드 | 설명 |
|---|---|
| isPresent() | 값을 포함하면 true, 포함하지 않으면 false |
| ifPresent(Consumer<T> block ) | 값이 있으면 블록을 실행한다. Consumer의 시그니처는 T -> void이다. |
| T get () | 값이 존재하면 반환하고 값이 없으면 NoSuchElementException을 일으킨다. |
| T orElse( T other ) | 값이 있으면 값을 반환하고, 값이 없으면 기본값(other)을 반환한다. |
5.3.4 첫번째 요소 찾기
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(); //9 = 첫번째 값을 가져 온다.
5.4 리듀싱
: 폴드
5.4.1 요소의 합
- 초기값이 있는경우
List<Integer> number = Arrays.asList(4,5,3,9); int sum = number.stream().reduce(0, (a,b) -> a + b); // ( 초기값, ( 누적값(최초에는 초기값, 최후에는 리턴값), 스트림값) int sum2 = number.stream().reduce(0, Integer::sum); // ( 초기값, 메소드 레퍼런스 ) - 초기값이 없는 경우 : 스트림이 아무 요소가 없는경우 아무 값도 반환할수 없어 Optional을 반환한다.
List<Integer> number = Arrays.asList(4,5,3,9); Optional<Integer> sum = number.stream().reduce((a,b) -> a + b);
5.4.2 최댓값과 최솟값
List<Integer> number = Arrays.asList(4,5,3,9);
Optional<Integer> max = number.stream().reduce(Integer:max); //최대값
Optional<Integer> min = number.stream().reduce(Integer:min); //최소값
Optional<Integer> max = number.stream().reduce((x,y) -> x>y ? x : y ); //람다식 최대값
5.4.3 카운터
int count = menu.stream()
.map( d -> 1)
.reduce( 0, (a,b)->a+b );
long count = menu.stream().count();
| 연산 | 형식 | 반환 형식 | 인터페이스 형식 | 함수 디스크립터 |
|---|---|---|---|---|
| filter | 중간 연산 | Stream<T> | Predicate<T> | T->boolean |
| distinct | 중간 연산(상태있는 언바운드) | Stream<T> | ||
| skip | 중간 연산(상태있는 바운드) | Stream<T> | Long | |
| limit | 중간 연산(상태 있는 바운드) | Stream<T> | Long | T->R |
| map | 중간 연산 | Stream<R> | Function<T,R> | T -> Stream<R> |
| flatMap | 중간 연산 | Stream<R> | Function<T,Stream<R>> | |
| sorted | 중간 연산(상태 있는 언바운드) | Stream<T> | Comparator<T> | (T,T)->int |
| anyMatch | 최종연산 | boolean | Predicate<T> | T->boolean |
| noneMatch | 최종연산 | boolean | Predicate<T> | T->boolean |
| allMatch | 최종연산 | boolean | Predicate<T> | T->boolean |
| findAny | 최종연산 | Optional<T> | ||
| findFirst | 최종연산 | Optional<T> | ||
| forEach | 최종연산 | void | Consumer<T> | T->void |
| Collect | 최종연산 | R | Collector<T,A,R> | |
| Reduce | 최종연산(상태 있는 바운드) | Optional<T> | BinaryOperator<T> | (T,T)->T |
| count | 최종연산 | long |
- 바운드 : 내부 상태의 크기가 제한 되어 있음.
5.6 숫자형 스트림
5.6.1 기본형 특화 스트림
박싱 비용을 없애기 위해 기본형에 특화된 스트림을 제공
- IntStream, DoubleStream, LongStream
숫자 스트림으로 매핑
int calories = menu.stream() // Stream<Dish> 반환 .mapToInt(Dish::getCalories) // IntSream 반환 .sum();객체 스트림으로 복원하기
IntStream intStream = menu.stream().mapToInt(Dish::getCalories); //스트림을 숫자 스트림으로 변환 Stream<Integer> stream = intStream.boxed(); // 스트림으로 변환OptionalInt : 기본값 : OptionalInt의 max가 없는 없는 경우 값이 0이 될수 있다. 하지만 실제로 max가 0인 경우가 될수도 있다. 그렇다면 이 두개는 보이는 값은 동일하지만 의미가 다르므로 구분 할 필요가 있다.
OptionalInt maxCalories = menu.stream() .mapToInt(Dish::getCalories) .max(); int max = maxCalroies.orElse(1); // 값이 없을때 기본 최댓값을 명시적으로 설정.
5.6.2 숫자 범위
- range, rangeClosed : IntStream 과 LongStream에서 지원
- range : 종료값이 불포함
- rangeClosed : 종료값이 포함
IntStream evenNumbers = IntStream.rangeClosed(1, 100)
.filter(n -> n % 2 == 0);
System.out.println(evenNumbers.count());
5.6.3 숫자 스트림 활용 : 피타고라스 수
Stream<int[]> pythagoreamTriples =
IntStream.rangeClosed(1, 100) //a값의 범위를 구한다.
.boxed() //Stream<Integer>로 변환
.flatMap( a ->
IntStream.rangeClosed( a, 100 )//b값의 범위
.filter( b->Math.sqrt( a * a + b * b ) % 1 == 0 ) // 정수 체크
.mapToObj( b->
new int[]{ a, b, (int)Math.sqrt( a * a + b * b )}
)// 반환 형 : IntStream<int[]>
); //flatMap으로 결과 값에 대해 평면화 진행, map인경우 Stream<IntStream<int[]>>반환, 이경우 Stream<int[]>를 반환
pythagoreamTriples.limit(5)
.forEach(t->System.out.println(t[0] + ", " + t[1] + ", " + t[2] ));
pythagoreamTriples =
IntStream.rangeClosed(1, 100) //a값의 범위를 구한다.
.boxed() //Stream<Integer>로 변환
.flatMap( a ->
IntStream.rangeClosed( a, 100 )//b값의 범위
.mapToObj( b->
new double[]{ a, b, (int)Math.sqrt( a * a + b * b )}
)
.filter(t->t[2]%1 == 0)
);
pythagoreamTriples.limit(5)
.forEach(t->System.out.println(t[0] + ", " + t[1] + ", " + t[2] ));
5.7 스트림 만들기
5.7.1 값으로 스트림 만들기
Stream<String> stream = Stream.of("Java 8", "Lambdas", "In", "Action");
stream.map(String::toUpperCase).forEach(System.out::println);
Stream<String> emptyStream = Stream.empty();
5.7.2 배열로 스트림 만들기
int[] numbers = {1,2,3,4,5};
int sum = Arrays.stream(numbers).sum();
5.7.3 파일로 스트림 만들기
java.nio.file.Files의 많은 정적 메서드가 스트림을 반환한다.
long uniqueWords = 0; try ( Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset()) ){// 스트림의 자원을 자동으로 해제할 수 있는 AutoClosable이다 uniqueWords = line.flatMap(line->Arrays.stream(line.split(" "))) // 단어별로 스트림 생성 .distinct() .count(); }catch(IOException e){ //예외 처리 }5.7.4 함수로 무한 스트림 만들기
- Stream.iterate, Stream.generate 제공
- 크기가 고정되지 않고 요청 할때마다 주어진 함수를 이용해서 값을 만들어 낸다.
but, 보통 무한한 값을 출력하지 않도록 limit(n)함수를 함께 연결해서 사용한다.
iterate : 값을 요청할때마다 값을 생산할수 있으며 끝이 없으므로 무한 스트림을 만든다. (언바운드 스트림)
Steam.iterate(0, n-> n+2)
.limit(10)
.forEach(System.out::println);
- generate : 상태 저장이 없는 무한 스트림 ( 각 값을 연속적으로 구하지 않음 )
Stream.generate(Math::random)
.limit(5)
.forEach(System.out::println);
IntSupplier fib = new IntSupplier(){
private int previous = 0;
private int current = 1;
public int getAsInt(){
int oldPrevious = this.previous;
int nextValue = this.previous + this.current;
this.previous = this.current;
this.current = nextValue;
return oldPrevious;
}
};
IntStream.generate(fib).limit(10).forEach(System.out::println);