프로그래밍 언어/[ Java ]

[ Java ] 15. Java에서의 Stream

kim.svadoz 2021. 6. 18. 17:46
반응형

Stream


JAVA 8 이상부터 Stream API를 지원합니다.

JAVA에서도 8버전 이상부터 람다를 사용한 함수형 프로그래밍이 가능해졌습니다.

기존에 존재하던 CollectionStream은 무슨 차이가 있을까요?

=> 바로 데이터 계산 시점 입니다.

Collection

  • 모든 값을 메모리에 저장하는 자료구조.
  • 따라서, Collection에 추가하기 전에 미리 계산이 완료되어 있어야 합니다.
  • 외부 반복을 통해 사용자가 직접 반복 작업을 거쳐 요소를 가져올 수 있습니다. (for-each)

Stream

  • 요청할 때만 요소를 계산합니다.
  • 내부 반복을 사용하므로 추출 요소만 선언해주면 알아서 반복 처리를 진행한다.
  • 스트림에 요소를 따로 추가 혹은 제거하는 작업은 불가능하다.

Collections은 핸드폰에 음악 파일을 미리 저장하여 재생하는 플레이어라면, Stream은 필요할 때 검색해서 듣느 멜론과 같은 음악 어플이라고 생각하면 됩니다!

외부반복 & 내부반복

Collection은 외부 반복, Stream은 내부반복 입니다. 그 차이를 알아봅시다.

성능 면에서는 내부 반복이 비교적으로 좋습니다.

내부 반복은 작업을 병렬 처리하면서 최적화된 순서로 처리해줍니다.

하지만, 외부반복은 명시적으로 컬렉션 항목을 하나씩 가져와서 처리해야 하기 때문에 최적화에 불리합니다.

즉, Collection에서 병렬성을 이용하려면 직접 synchronized를 통해 관리해야만 한다.

img

Stream 연산

스트림은 연산 과정이 '중간'과 '최종'으로 나누어집니다.

filter, map, limit파이프라이닝이 가능한 연산을 중간 연산
count, collect 등 스트림을 닫는 연산을 최종 연산 이라고 합니다.

둘로 나누는 이유는, 중간 연산들은 스트림을 반환해야 하는데, 모두 한꺼번에 병합하여 연산을 처리한 다음 최종 연산에서 한꺼번에 처리하게 된다.

ex) Item 중에 가격이 1000 이상인 이름을 5개 선택한다.

List<String> itmes = item.steram()
                        .filter(d -> d.getPrices() >= 1000)
                        .map(d -> d.getName())
                        .limit(5)
                        .collect(tpList());
// filter와 map은 다른 연산이지만, 한 과정으로 병합됩니다.

만약 Collection 이었다면, 우선 가격이 1000 이상인 아이템을 찾은 다음, 이름만 따로 저장한 뒤 5개를 선택해야 합니다.

연산 최적화는 물론, 가독성 면에서도 Stream이 더 좋습니다

- Stream 중간연산

중간연산은 모두 Stream을 반환합니다.

  • filter(Predicate) : Predicate를 인자로 받아 true인 요소를 포함한 스트림 반환
  • distinct() : 중복 필터링
  • limit(n) : 주어진 사이즈 이하 크기를 갖는 스트림 반환
  • skip(n) : 처음 n개의 요소를 제외한 스트림 반환
  • map(func) : 매핑 함수의 result로 구성된 스트림 반환
  • flatMap() : 스트림의 콘텐츠로 매핑합니다. map과 달리 평면화된 스트림 반환

- Stream 최종연산

  • (boolean) allMatch(Predicate) : 모든 스트림 요소가 Predicate와 일치하는지 검사
  • (boolean) anyMatch(Predicate) : 하나라도 일치하는 요소가 있는지 검사
  • (boolean) noneMatch(Predicate) : 매치되는 요소가 없는지 검사
  • (Optional) findAny() : 현재 스트림에서 임의의 요소 반환
  • (Optional) findFirst() : 스트림의 첫번째 요소 반환
  • reduce() : 모든 스트림 요소를 처리해 값을 도출. 두개의 인자를 가짐
  • collect() : 스트림을 reduce하여 list, map, 정수 형식 컬렉션을 만듦
  • (void) forEach() : 스트림의 각 요소를 소비하며 람다 적용
  • (Long) count : 스트림의 요소 개수를 반환
# Optional Class 란 ?
> 값의 존재나 여부를 표현하는 컨테이너 Class
- NULL로 인한 버그를 막을 수 있는 장점이 있다.
- isPresent() : Optional이 값을 포함할 때 TRUE를 반환

Stream 활용 예제

  1. map()

     List<String> names = Arrays.asList("Sunghyun", "Insun", "Jaemin", "Seungjae");
    
     names.stream()
         .map(name -> name.toUpperCase())
         .forEach(name -> System.out.println(name));
  2. filter()

     List<String> startWithN = names.stream()
         .filter(name -> name.startsWithN("S"))
         .collect(Collectors.toList());
  3. reduce()

     Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
     Optional<Integer> sum = numbers.reduce((x, y) -> x + y);
     sum.ifPresent(s -> System.out.println("sum: " + s));
     // 55
  4. collect()

     System.out.println(names.stream()
                       .map(String::toUpperCase)
                       .collect(Collectors.joining(", ")));
반응형