[도서][모던 자바 인 액션] 스트림 활용

Junseo Kim·2021년 2월 27일
0

필터링

스트림의 요소를 선택하는 방법.

filter: Predicate(boolean을 반환하는 함수)를 인수로 받아서 Predicate와 일치하는 모든 요소를 포함하는 스트림을 반환한다.

distinct: 고유 요소로 이루어진 스트림을 반환(중복을 제거)하는 메서드(hascCode와 equals로 비교)

슬라이싱

스트림의 요소를 선택하거나 스킵하는 다양한 방법.

Predicat를 이용한 슬라이싱

takeWhile: 해당 Predicate까지 요소들을 검사하고 Predicate을 만족하지 못하는 요소를 만나면 중단 후 스트림을 반환한다. 정렬된 요소를 슬라이싱하는데 사용할 수 있다.

dropWhile: takeWhile과 정반대의 작업을 수행한다. Predicate가 처음으로 거짓이 되는 지점까지 발견한 요소를 버린다. Predicate가 거짓이 되면 그 지점에서 작업을 중단하고 남은 요소를 반환한다.

스트림 축소

limit: 주어진 값 이하의 크기를 갖는 스트림 반환.

요소 건너뛰기

skip: 처음 n개 요소를 제외한 스트림을 반환. n개 이하의 요소를 포함하는 스트림에 skip(n)을 호출 시 빈 스트림 반환.

매핑

특정 객체에서 특정 데이터를 선택하는 기능.

스트림의 각 요소에 함수 적용

map: Function을 인수로 받는다. Function은 각 요소에 적용되며 함수를 적용한 결과가 새로운 요소로 매핑된다.

스트림 평면화

flatMap: 하나의 평면화된 스트림을 반환. 스트림의 각 값을 다른 스트림으로 만든 다음 모든 스트림을 하나의 스트림으로 연결하는 기능.

검색과 매칭

특정 속성이 데이터 집합에 있는지 여부를 검색하는 데이터 처리. 쇼트서킷 기법으로 처리된다.

쇼트서킷: 모든 요소를 검사하지 않아도 결과를 알 수 있는 경우를 뜻함. 결과를 찾는 즉시 실행 종료

Predicate이 적어도 한 요소와 일치하는지 확인

anyMatch: Predicate이 주어진 스트림에서 적어도 한 요소와 일치하는지 확인. boolean을 반환하는 최종연산.

Predicate이 모든 요소와 일치하는지 검사

allMatch: 스트림의 모든 요소가 주어진 Predicate와 일치하는지 검사. boolean을 반환하는 최종연산.

noneMatch: allMatch와 반대 연산. 주어진 Predicate와 일치하는 요소가 없는지 확인. boolean을 반환하는 최종연산.

요소 검색

findAny: 현재 스트림에서 임의의 요소를 반환(Optional 객체로). 임의의 요소가 없는 경우 Optional.empty()를 반환.

첫 번째 요소 찾기

findFirst: 스트림의 첫 번째 요소 반환(Optional 객체로). 임의의 요소가 없는 경우 Optional.empty()를 반환.

finadFirst와 findAny는 언제 사용할까?
병렬 실행에서는 첫 번째 요소 찾기가 어렵기 때문에 요소의 반환 순서가 상관없다면 병렬 스트림에서는 제약이 적은 findAny를 사용한다.

리듀싱

리듀스 연산을 이용해서 스트림 요소를 조합하여 더 복잡한 질의를 표현하는 방법. 모든 스트림 요소를 처리해서 값으로 도출하는 것.

reduce: 두 개의 인수를 갖는다. 초깃값에서 시작하여 BinaryOperator를 통한 결과가 하나의 값으로 줄어들 때 까지 각 요소를 반복해서 조합한다.
1) 초깃값
2) 두 요소를 조합해서 새로운 값을 만드는 BinaryOperator< T >

초깃값을 받지 않도록 오버로드된 reduce도 존재한다. BinaryOperator만 인수로 가지며 이때는 Optional을 반환한다.(스트림에 아무 요소도 없는 경우 초깃값도 없으면 null이 되기 때문에 Optional이 리턴값이다.)

요소의 합

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
        .reduce(0, Integer::sum);

초깃값 0에서 시작하여 하나의 값이 나올때까지 반복 조합
0 + 1 = 1
1 + 2 = 3
3 + 3 = 6
6 + 4 = 10
10 + 5 = 15

최대값과 최솟값

Optional<Integer> max = numbers.stream()
        .reduce(Integer::max); // 최대값
        
Optional<Integer> min = numbers.stream()
        .reduce(Integer::min); // 최솟값

reduce 장점과 병렬화

reduce를 이용하면 내부 반복이 추상화되면서 내부 구현에서 병렬로 reduce를 실행할 수 있게 된다.

상태 없는 연산 & 상태 있는 연산

상태 없는 연산: 상태를 저장하지 않음. filter, map 등

상태 있는 연산: 연산하는데 상태가 필요하여 상태를 저장. reduce, sorted, distinct 등

숫자형 스트림

숫자 연산을 할때 Integer 타입은 기본형으로 언박싱되어 계산된다. 이 때 박싱 비용이 발생한다. 이런 비용을 줄이기 위해 스트림 API는 숫자 스트림을 효율적으로 처리하기 위한 기본형 특화 스트림을 제공한다.(IntStream, DoubleStream, LongStream)

특화스트림은 박싱 과정에서 일어나는 효율성과 관련 있으며 스트림에 추가 기능을 제공하지는 않는다.

스트림 -> 특화 스트림

스트림을 특화 스트림으로 변환할 때 mapToInt, mapToDouble, mapToLong 메서드를 사용한다. 각 메서드는 특화 스트림을 반환해준다. 특화스트림은 sum, max, min, average 등의 메서드를 지원한다.

특화 스트림 -> 스트림

boxed 메서드를 이용하여 숫자 스트림을 원상태인 일반 스트림으로 복원할 수 있다.

특화 Optional

값의 존재 여부를 가리킬 수 있는 Optional도 특화 스트림 버전을 제공해준다. OptionalInt, OptionalDouble, OptionalLong

최대값이 0인 경우와, 스트림의 요소가 없어서 0인 경우를 구분하기 위해서는 Optional의 orElse를 사용할 수 있다.

OptionalInt maxCalories = menu.stream()
    .mapToInt(Dish::getCalories)
    .max();
int max = maxCalories.orElse(1);

특정 범위 숫자

range: 첫 번째 인수로 시작값, 두 번째 인수로 종료값. 그러나 시작값과 종료값은 결과에 포함되지 않는다.

rangeClosed: 첫 번째 인수로 시작값, 두 번째 인수로 종료값. 시작값과 종료값도 결과에 포함된다.

스트림 만들기

값으로 스트림 만들기

Stream.of(): 괄호안에 넣은 값을 스트림으로 반환.

Stream<Integer> intStream = Stream.of(1, 2, 3, 4, 5);
Stream<String> stringStream = Stream.of("hello", "world");

Stream.empty(): 빈 스트림 만들기

null이 될 수 있는 객체로 스트림 만들기

Stream.ofNullable(): null이 될 수 있는 객체를 스트림으로 만들어준다. 하나의 타입만 받을 수 있다.

배열로 스트림 만들기

Arrays.stream(): 배열을 인수로 받아 스트림 생성

int[] numbers = {2, 3, 5, 7, 11, 13};
int sum = Arrays.stream(numbers)
	.sum();

파일로 스트림 만들기

Files.lines(): 파일의 각 행 요소를 반환하는 스트림 생성.

long uniqueWords = 0;
try (Stream<String> lines =
             Files.lines(Paths.get("data.txt"), Charset.defaultCharset())) {
    uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" ")))
            .distinct()
            .count();
} catch (IOException e) {

}

스트림의 소스가 I/O 자원이므로 try-catch로 감쌈
Stream 인터페이스는 AutoCloseable 인터페이스를 구현하므로 자원을 try내에서 관리할 수 있음

함수로 무한 스트림(언바운드 스트림) 만들기

고정된 컬렉션에서 고정된 크기로 스트림을 만드는 것과 달리 크기가 고정되지 않은 스트림을 만드는 것

무한 스트림의 요소는 무한적으로 계산이 반복되므로 정렬하거나 리듀스할 수 없다.

Stream.iterate(): 초깃값과 람다를 인수로 받는다. 요청할 때마다 주어진 함수를 이용해서 값을 만든다. 기존 결과에 의존해서 순차적으로 연산을 수행한다. 보통 무한한 값을 출력하지 않도록 limit와 함께 사용한다. 연속된 일련의 값을 만들 때 사용.

Stream.iterate(0, n -> n + 2)
    .limit(10)
    .forEach(System.out::println);

자바9부터는 초깃값과 Predicate와 람다식을 인수로 받을 수 있다. Predicate은 언제까지 작업을 수행할 것인지에 대한 기준이다

IntStream.iterate(0, n -> n < 100, n -> n + 4)
	.forEach(System.out::println);

Stream.generate(): 무한스트림은 만들 수 있지만 iterate와 달리 생산된 각 값을 연속적으로 계산하지 않는다. Supplier를 인수로 받아 새로운 값을 생성한다. 보통 무한한 값을 출력하지 않도록 limit와 함께 사용한다.

Stream.generate(Math::random)
    .limit(5)
    .forEach(System.out::println);

0개의 댓글