[Java] 스트림과 병렬 처리 ③

kiteB·2022년 3월 3일
0

Java2

목록 보기
18/36
post-thumbnail

[ 스트림 파이프라인 ]

대량의 데이터를 가공해서 축소하는 것을 일반적으로 리덕션(Reduction)이라고 하며, 데이터의 합계, 평균값, 카운팅, 최댓값, 최솟값 등이 대표적인 리덕션의 결과물이다.

하지만 컬렉션의 요소를 리덕션의 결과물로 바로 집계할 수 없을 경우에는 집계하기 좋도록 필터링, 매핑, 정렬, 그룹핑 등의 중간 처리가 필요하다.


1. 중간(Intermediate) 처리와 최종(Terminal) 처리

스트림은 데이터의 필터링, 매핑, 정렬, 그룹핑 등의 중간 처리와 합계, 카운팅, 최댓값, 최솟값 등의 최종 처리를 파이프라인으로 해결한다. 파이프라인여러 개의 스트림이 연결되어 있는 구조를 말한다. 파이프라인에서 최종 처리를 제외하고는 모두 중간 처리 스트림이다.

중간 스트림이 생성될 때 요소들이 바로 중간 처리(필터링, 매핑, 정렬)되는 것이 아니라 최종 처리가 시작되기 전까지 중간 처리는 지연된다. 최종 처리가 시작되면 비로소 컬렉션의 요소가 하나씩 중간 스트림에서 처리되고 최종 처리까지 오게 된다.

Stream 인터페이스에는 필터링, 매핑, 정렬 등의 많은 중간 처리 메소드가 있는데, 이 메소드들은 중간 처리된 스트림을 리턴한다. 그리고 이 스트림에서 다시 중간 처리 메소드를 호출해서 파이프라인을 형성하게 된다.


✅ 파이프라인을 자바 코드로 표현

Stream<Member> maleFemaleStream = list.stream();
Stream<Member> maleStream = maleFemaleStream.filter(m -> m.getSex() == Member.MALE);
IntStream ageStream = maleStream.mapToInt(Member::getAge);
OptionalDouble optionalDouble = ageStream.average();
double ageAvg = optionalDouble.getAsDouble();

로컬 변수를 생략하고 연결하면 다음과 같은 형태의 파이프라인 코드만 남는다.

double ageAvg = list.stream()					//오리지널 스트림
        .filter(m -> m.getSex() == Member.MALE)	//중간 처리 스트림
        .mapToInt(Member :: getAge)			//최종 처리 스트림
        .average()
        .getAsDouble();
  • filter(m -> m.getSex() == Member.MALE)은 남자 Member 객체를 요소로 하는 새로운 스트림을 생성한다.
  • mapToInt(Member :: getAge())는 Member 객체를 age 값으로 매핑해서 age를 요소로 하는 새로운 스트림을 생성한다.
  • average() 메소드는 age 요소들의 평균을 OptionalDouble에 저장한다.
  • OptionalDouble에서 저장된 평균값을 읽으려면 getAsDouble() 메소드를 호출하면 된다.

2. 중간 처리 메소드와 최종 처리 메소드

스트림 파이프라인에서 중간 처리를 하는 메소드와 최종 처리를 하는 메소드는 다음과 같다.

  • 중간 처리 메소드
  • 최종 처리 메소드

중간 처리 메소드와 최종 처리 메소드를 쉽게 구분하는 방법은 리턴 타입을 보면 된다!

  • 리턴 타입이 스트림이라면 중간 처리 메소드이고,
  • 리턴 타입이 기본 타입이거나 OptionalXXX라면 최종 처리 메소드이다.

소속된 인터페이스에서 공통의 의미는 Stream, IntStream, LongStream, DoubleStream에서 모두 제공된다는 뜻이다.


[ 필터링 (distinct(), filter()) ]

필터링은 중간 처리 기능으로 요소를 걸러내는 역할을 한다.

필터링 메소드인 distinct()와 filter() 메소드는 모든 스트림이 가지고 있는 공통 메소드이다.

distinct() 메소드는 중복을 제거하는데, Stream의 경우 Object.equals(Object)가 true이면 동일한 객체로 판단하고 중복을 제거한다. IntStream, LongStream, DoubleStream은 동일값일 경우 중복을 제거한다.

✅ 예제 | 필터링

import java.util.*;

public class FilteringExample {
    public static void main(String[] args) {
        List<String> fruits = Arrays.asList("apple", "lemon", "strawberry", "lime", "lemon");
        
        fruits.stream()
            .distinct()									//중복 제거
            .forEach(n -> System.out.println(n));
        System.out.println();
        
        fruits.stream()
            .filter(n -> n.startsWith("l"))				//필터링
            .forEach(n -> System.out.println(n));
        System.out.println();
        
        fruits.stream()
            .distinct()									//중복 제거 후
            .filter(n -> n.startsWith("l"))				//필터링
            .forEach(n -> System.out.println(n));	
    }
}
  • 실행 결과
apple
lemon
strawberry
lime

lemon
lime
lemon

lemon
lime

[ 참고자료 ]

이것이 자바다 책

profile
🚧 https://coji.tistory.com/ 🏠

0개의 댓글