숫자 스트림
➡️ 메뉴의 칼로리 합계
int calories = menu.stream()
.map(Dish::getCalories)
.reduce(0, Integer::sum);
System.out.println(calories);// 결과 : 4300
위 코드에는 박싱 비용
이 숨어있다. 내부적으로 합계를 계산하기 전에 Integer를 기본으로 언박싱을해야 한다.
아래와 같이 직접 sum메서드를 호출할 수 있다면 더 좋지 않을까 ?
int calroies = menu.stream()
.map(Dish::getCalories)
.sum();
위 코드처럼 sum 메소드를 직접 호출할 수 없다.
이유는 map 메서드가 Stream
스트림의 요소 형식은 integer지만 인터페이스에는 sum 메서드가 없다.
- 왜 sum 메서드가 없을까?
예를 들어 menu처럼 Stream
- 다른 방법이 있을까?
스트림 API 숫자 스트림을 효율적으로 처리할 수 있도록 기본형 특화 스트림(primitive stream specialzation)
을 제공한다.
기본형 특화 스트림이란 무엇인가 ?
➡️ 자바 8에서는 세 가지 기본형 특화 스트림을 제공한다.
int
요소에 특화된IntStream
을 제공한다.double
요소에 특화된DoubleStream
을 제공한다.lon
요소에 특화된LongStream
을 제공한다.
위와같이 특화된 스트림 제공으로 스트림 API는 박싱 비용을 지출하지 않아도 된다.
➡️ 각각의 인터페이스 자주 사용하는 숫자 관련 리듀싱 연산 수행 메서드를 제공한다.
- 숫자 스트림의 합계를 계산하는 sum
- 최댓값 요소를 검색하는 max
- ..등등
➡️ 다시 객체 스트림으로 복원하는 기능도 제공한다.
**※기억하자 !
-** 특화 스트림은 오직 박싱 과정에서 일어나는 효율성과 관련 있으며 스트림에 추가 기능을 제공하지는 않는다는 사실을 기억하자
숫자 스트림으로 매핑
특화 스트림으로 변환할 때, 사용하는 메서드들
- mapToInt
- mapToDouble
- mapToLong
위의 메서드들은 map과 같은 기능을 수행하지만, Stream
int calroies = menu.stream() // <- Stream<Dish> 반환
.mapToInt(Dish::getCalories) // <- IntStream 반환
.sum();
mapToInt
메서드는 각 요리에서 모든 칼로리(Integer 형식)를 추출한 다음에 IntStream을 반환한다.
주의할 점 : IntStream은 Stream
IntStream 인터페이스에서 제공하는 sum 메소드를 이용해서 칼로리 합계를 구할 수 있다.
- 스트림이 비어있으면 기본값 0을 반환
➡️ IntStream이 지원하는 메서드들은 max, min, average ….등 유틸리티 메소드도 지원을 한다.
객체 스트림으로 복원하기
- 숫자 스트림을 만든 다음에, 원상태인 특화되지 않은 스트림으로 복원하는 방법
IntStream
은 기본형의 정수값만 만들 수 있다.
IntStream
의 map 연산은 int를 인수로 받아서 int를 반환하는 람다(intUnaryOperator)를 인수로 받는다.
하지만 정수가 아닌 Dish같은 다른 값을 반환하고 싶으면 어떻게 해야할까 ?
- 스트림 인터페이스에 정의된 일반적인 연산을 사용
- boxed() 사용 : 특화 스트림 → 일반 스트림 변환
IntStream intStream = menu.stream()
.mapToInt(Dish::getCalories); // <-- 스트림을 숫자 스트림으로 변환
Stream<Integer> stream = intStream.boxed(); // <-- 숫자 스트림을 스트림으로 변환
기본값 : OptionalInt
합계를 구할 때 0이라는 기본값이 있었기때문에 값을 구하는데 에러는 나오지 않았다.
하지만 .. IntStream
에서 최댓값을 찾을 때는 0이라는 기본값 때문에 잘못된 결과가 도출될 수 있다.
아래와 같은 상황일 때 어떻게 구별할까 ?
- 스트림에 요소가 없는 상황
- 실제 최댓값이 0인 상황
➡️Optional을 사용하자.
- Optional은 Integer, String 등의 참조 형식으로 파리미터화할 수 있다.
- OptionalInt, OptionalDouble, OptionalLong 세 가지 기본형 특화 스트림 버전도 제공
- 예제) OptionalInt를 사용해서 IntStream의 최댓값 요소 구하기
OptionalInt maxCalories = menu.stream()
.mapToInt(Dish::getCalories)
.max();
짜잔✨✨
OptionalInt를 사용해서 최댓값이 없는 상황에 사용할 기본값을 명시적으로 정의할 수 있다.
int max = maxCalories.orElse(1); //<-- 값이 없을 때 기본 최댓값을 명시적으로 설정
숫자의 범위
IntStream
과 LongStream
에서는 range와 rangeClosed라는 두 가지 정적 메소드
를 제공한다.
range()
는 시작값과 종료값을 포함하지 않는다.
- IntStream.range(시작값, 종료값)
- LongStream.range(시작값, 종료값)
rangeClosed()
는 시작값과 종료값을 포함한다.
- IntStream.rangeClosed(시작값, 종료값)
- LongStream.rangeClosed(시작값, 종료값)
예제) rangeClosed()
IntStream evenNumbers = IntStream.rangeClosed(1, 100) //<-- [1, 100]의 범위를 나타낸다.
.filter(n -> n % 2 == 0);
System.out.println(evenNumbers.count()); // <-- 1부터 100까지 에는 50짝수가 있음
결과 값 : 50
예제) range
IntStream evenNumbers = IntStream.range(1, 100) // <-- (1, 100)의 범위를 나타낸다.
.filter(n -> n % 2 == 0);
System.out.println(evenNumbers.count()); // <-- 49
결과 값 : 49
1과 100을 포함하지 않기 때문에 49개가 나온다.