4-5 리듀싱
스트림의 모든 요소를 반복적으로 처리해서 값을 도출하는 연산을 리듀싱 연산이라고 한다. 함수형 프로그래밍 언어 용어로는 이 과정이 마치 종이를 작은 조각이 될 때까지 반복해서 접는 것과 비슷하다는 의미로 폴드라고 부른다.
1)요소의 합 (reduce)
int sum = numbers.stream().reduce(0, (a,b) -> a + b); //누적값 계산
reduce( 초기값 , 두 요소를 조합해서 새로운 값을 만드는 BinaryOperator<T>)
2)초기값 없음
Optional<Integer> sum = numbers.stream().reduce((a , b) -> (a + b);)
이런경우 Optional객체를 반환한다. 스트림에 아무 요소도 없는 경우 초기값이 없으므로 reduce는 합계를 반환할 수 없다. 따라서 합계없음을 가리키기 위해 Optional객체로 감싼 결과를 반환한다.
3) 최댓값 / 최솟값
최댓값, 최솟값을 찾을 때도 reduce를 활용할 수 있다.
Optional<Integer> max = numbers.stream().reduce(Integer::max);
Integer::max 대신 Integer::min을 넘겨주면 최소값을 구할 수 있다.
* reduce 메서드의 장점과 병렬화
→ reduce를 이용하면 내부 반복이 추상화되며 내부 구현에서 병렬로 reduce를 실행할 수 있게 된다.
* 스트림 연산 : 상태 없음과 상태 있음
→ map, filter등은 입력 스트림에서 각 요소를 받아 0 또는 결과를 출력 스트림으로 보낸다. 따라서 내부 상태를 갖지 않는 연산이다. reduce, sum, max 같은 연산은 연산 결과를 누적할 내부 상태가 필요하다.
→ 반면 sorted나 distinct같은 연산은 과거의 이력을 알고 있어야 한다. 따라서 데이터 스트림의 크기가 크거나 무한이라면 문제가 생길 수 있다. 이러한 연산을 내부 상태를 갖는 연산이라 한다.
4-6 숫자형 스트림
1) 기본형 특화 스트림
(1) 숫자 스트림으로 매핑
→ 스트림을 특화 스트림으로 변활할 때는 mapToInt, mapToDouble, mapToLong 세 가지 메서드를 가장 많이 사용한다.
int calories = menu.stream() //Stream<Dish>반환
.mapToInt(Dish::getCalories) //IntStream 반환
.sum(); //스트림이 비어있으면 기본값 0반환
(2) 객체 스트림으로 복원하기
→ boxed메서드를 이용해서 특화 스트림을 일반 스트림으로 변환할 수 있다.
IntStream intStream = menu.stream().mapToInt(Dish::getCalories); //스트림 -> 숫자스트림
Stream<Integer> stream = intStream.boxed(); //숫자스트림 -> 스트림
(3) 기본값 OptionalInt
→ 스트림에 요소가 없는 경우와 실제 최댓값이 0인 경우를 가리기 위해
OptionalInt maxCalories = menu.stream()
.mapToInt(Dish::getCalories)
.max();
//값이 없는 경우 기본 최대값을 명시적으로 설정
int max = maxCalories.orElse(1);
2) 숫자 범위
→ 특정 범위의 숫자를 이용해야 하는 경우
IntStream evenNumbers = IntStream.rangeClosed(1,100) //1~100의 범위
.filter(n -> n % 2 == 0); //그중 짝수
3) 숫자 스트림 활용
피타고라스 수
stream.filter(b -> Math.sqrt(a*a + b*b) % 1 == 0)
집합 생성
stream.filter(b -> Math.sqrt(a*a + b*b) % 1 == 0)
.map(b -> new int[]{a, b, (int)Math.sqrt(a*a + b*b)});
b값 생성
IntStream.rangeClosed(1,100)
.filter(b -> Math.sqrt(a*a + b*b) % 1 == 0)
.mapToObj(b -> new int[]{a, b, (int)Math.sqrt(a*a + b*b)});
a값 생성
Stream<int[]> pythagoreanTriples = IntStream.rangeClosed(1,100).boxed()
.flatMap(a -> IntStream.rangeClosed(a, 100)
.filter(b -> Math.sqrt(a*a + b*b) % 1 == 0)
.mapToObj(b -> new int[]{a,b, (int)Math.sqrt(a * a + b * b)}));
* 개선점 : 제곱근을 두 번 계산함 → 형식을 만족하는 세 수를 만든 다음에 원하는 조건에 맞는 결과만 필터링
Stream<double[]> pythagoreanTriples = IntStream.rangeClosed(1,100).boxed()
.flatMap(a -> IntStream.rangeClosed(a, 100)
.mapToObj(b -> new int[]{a,b, (int)Math.sqrt(a * a + b * b)})
.filter(t -> t[2] % 1 == 0));
4-7 스트림 만들기
1) 값으로 스트림 만들기
Stream<String> stream = Stream.of("Modern", "Java", "In", "Action");
stream.map(String::toUpperCase).forEach(System.out::println);
2) null이 될 수 있는 객체로 스트림 만들기
Stream<String> values =
Stream.of("config", "home", "user")
.flatMap(key -> Stream.ofNullable(System.getProperty(key)));
3) 배열로 스트림 만들기
int[] numbers = {2, 3, 5, 7, 11, 13};
int sum = Arrays.stream(numbers).sum();
4) 파일로 스트림 만들기
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){
;
}
4) 함수로 무한 스트림 만들기
→ 고정된 컬렉션에서 고정된 크기로 스트림을 만들었던 것과는 달리 고정되지 않은 스트림
(1) iterate메서드
→ iterate는 요청할 때마다 값을 생성할 수 있으며 끝이 없으므로 무한 스트림을 만든다. 이러한 스트림을 언바운드 스트림이라고 표현현다.
IntStream.iterate(0, n -> n + 4)
.takeWhile(n -> n <100)
.forEach(System.out.println);
(2) generate메서드
→ generate메서드는 Supplier<T> 를 인수로 받아서 새로운 값을 생성한다. / 생산된 각 값을 연속적으로 계산하지 않는다.
IntStream twos = IntStream.generate(new IntSupplier(){
public int getAsInt(){
return 2;
}
});
'Modern java in action' 카테고리의 다른 글
5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 (0) | 2021.08.22 |
---|---|
[modern java in action] stream(5) (0) | 2021.06.24 |
[modern java in action] stream(3) (0) | 2021.06.24 |
[Modern java in action] Stream(2) (0) | 2021.06.24 |
[modern java in action] Stream(1) (0) | 2021.06.24 |
댓글