[모던 자바인 액션] chp 5. 스트림 활용 (2)

sameul__choi·2022년 3월 28일
0

[모던 자바인 액션]

목록 보기
7/11
post-thumbnail

이전 챕터에 이어서 스트림을 활용하는 방법에 대해 더욱 자세히 알아본다.

00 숫자형 스트림

이전 장 chpt 03에서 메서드로 스트림 요소의 합을 구하는 예제를 살펴봤다. 예를 들어 다음처럼 메뉴의 칼로리 합계를 계산할 수 있다.

int calories = menu.stream()
				   .map(Dish::getCalories)
                   .reduce(0, Integer::sum);

사실 위 코드엔 박싱비용이 숨어있다. 내부적으로 합계를 계산하기 전에 Integer를 기본형으로 언박싱해야한다.

다음 코드처럼 sum을 호출할 수 있다면 더 좋지 않을까 ?

int calories = menu.stream()
				   .map(Dish::getCalories)
                   .sum();

하지만 그럴 수 없다. map 메서드가 Stream<T>를 생성하기 때문이다. 스트림의 요소 형식은 Integer지만 인터페이스에는 sum 메서드가 없다. 왜 sum 메서드가 없나, menu처럼 Stream<Dish> 형식의 요소만 있다면 sum이라는 연산을 수행할 수 없기 때문이다.

그렇기 때문에 스트림 API 숫자 스트림을 효율적으로 처리하기 위하여 기본 특화 스트림을 제공한다.

  • IntStream
  • DoubleStream
  • LongStream

각각의 인터페이스는 max, sum과 같은 자주 사용하는 숫자관련 리듀싱 연산 수행 메서드를 제공한다. 또한 필요할 때 다시 객체 스트림으로 복원하는 기능도 제공한다.

특화 스트림은 오직 박싱과정의 효율성에만 관련이 있다. 스트림에 추가기능을 제공하는 것은 아니라는 사실을 기억하자.

숫자 스트림으로 맵핑

스트림을 특화 스트림으로 변환 시킬땐 mapToInt, mapToDouble, mapToLong을 가장 많이 사용한다. map과 정확히 같은 기능을 수행하지만 특화 스트트림을 반환한다.

int calories = menu.stream()
				   .mapToInt(Dish::getCalories)
                   .sum();

IntStream을 반환받아 sum 메서드를 사용할 수 있다. 스트림이 비어있다면 0을 반환하고 IntStream은 max, min, average 등 다양한 유틸리티 메서드도 지원한다.

객체 스트림으로 복원하기

특화 스트림을 만든 뒤에 원 상태로 복원 할 수 있을까? 우리가 갖고 싶은게 숫자가 아닌 Dish 같은 다른 값을 받고 싶다면 ?

그럼 스트림 인터페이스에 정의된 일반적인 연산을 사용해야한다. 다음 예제처럼 boxed 메서드를 이용하여 다시 복원하자.

IntStream intStream = menu.stream().mapToInt(Dish::getCalories);
Stream<Integer> stream = intStream.boxed();

참고로 스트림에 요소가 없는 상황과 실제 최댓값이 0인 상황을 구별하려면 OptionalInt 같이 Optional을 사용하여 최댓값 요소를 찾을 수 있다.

숫자 범위

특정 범위의 숫자를 이용해야 할 때 rangerangeClosed 메서드를 사용할 수 있다. 이는 IntStream, LongStream 두 기본형 특화 스트림에서 지원된다. range는 열린 구간을 의미하며, rangeClosed는 닫힌 구간을 의미한다.

01 스트림 만들기

스트림이 데이터 처리 질의를 표현하는 강력한 도구임을 충분히 확인했다. 우리가 배웠던 방식 외에 다양한 방식으로 스트림을 만들 수 있다. 그 방법들에 대해서 배워보자.

값으로 스트림 만들기

정적 메서드 Stream.of을 이용하여 스트림을 만들 수 있다.

Stream<String> stream = Stream.of("jiny","choi","samuel");

stream.map(String::toUpperCase).forEach(System.out::println);

//스트림 비우기
Stream<String> emptyStream = Stream.empty();

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

자바 9부터 지원되며 Stream.ofNullable 메서드를 이용하여 null이 될 수 있는 객체를 지원하는 스트림을 만들 수 있다. null이 될 수 있는 객체를 스트림으로 만들어야 할 때도 있다. 예를 들면 System.getProperty는 제공된 키에 대응하는 속성이 없으면 null을 반환한다. 그런 메소드를 스트림에 활용하려면 다음처럼 null을 명시적으로 확인했어야 했다.

하지만 Stream.ofNullable을 활용하면 다음처럼 구현이 가능하다.

Stream<String> homeValueStream = 
						Stream.of("config", "home", "uesr")
					   .flatMap(key -> Stream.ofNullable(System.getProperty(key)));

배열로 스트림 만들기

배열을 인수로 받는 정적 메서드 Arrays.stream을 이용하여 스트림을 만들 수 있다.

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

파일로 스트림 만들기

파일을 처리하는 등의 I/O 연산에 사용하는 자바의 NIO API(비블록 I/O)도 스트림 API를 활용할 수 있도록 업데이트되었다. java.nio.file.Files의 많은 정적 메서드가 스트림을 반환한다. 예를 들어 Files.lines는 주어진 파일의 행 스트림을 문자열로 반환한다.

함수로 무한 스트림 만들기

Stream.iterate와 Stream.generate를 통해 함수를 이용하여 무한 스트림을 만들 수 있다. iterate와 generate에서 만든 스트림은 요청할 때마다 주어진 함수를 이용해서 값을 만든다. 따라서 무제한으로 값을 계산할 수 있지만, 보통 무한한 값을 출력하지 않도록 limit(n) 함수를 함께 연결해서 사용한다.

Stream.iterate

public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)

Stream.generate

`public static Stream generate(Supplier s)``

02 마치며

이 장은 내용이 길었지만, 중요한 개념들을 설명한 장이었다. 컬렉션을 더 효율적으로 처리할 수 있게 되었고, 스트림을 이용하면 복잡한 데이터 처리 질의를 간결하게 표현할 수 있다.

0개의 댓글