[Java8] Stream API에 대해
by 무작정 개발이번에는 Java 8에 추가된 기능 중 Stream에 대해 정리할 것이다.
1. Stream이란?
Stream이란 Collection(컬렉션)과 같은 연속된 데이터를 처리하는 오퍼레이션의 모음, Stream 그 자체로는 데이터가 아닙니다.

Stream은 컨베이어 벨트와 비슷합니다.
컨베이어 벨트에 떡 조각(데이터)들을 흘려보내면서 반죽을 하고, 앙금을 쌓고(map), 불량품은 빼고(filter) 포장을 해서(collect) 내보냅니다.
(1) Stream의 특징
- 데이터를 담고 있는 자정소(Collection)이 아닙니다.
- Functional in nature
- Stream은 처리하는 데이터 소스를 변경하지 않습니다. 즉, A라는 데이터를 수정한다고 해서 원본 데이터가 수정되는 것이 아니라는 의미입니다.
javapublic static void main(String[] args) { List<String> names = new ArrayList<>(); names.add("hyunwoo"); Stream<String> stringStream = names.stream().map(String::toUpperCase); names.forEach(System.out::println); } /* [실행 결과] hyunwoo */
- Stream으로 처리하는 데이터는 오직 단 1번만 처리합니다.
- 컨베이어 벨트에서 데이터들이 1번 지나간 뒤 다시 돌아오지 않습니다.
- 데이터가 무제한 일 수도 있습니다.
- 실시간으로 계속 데이터를 받아 온다면 Short Circuit 메서드를 사용해서 제한이 가능합니다.
- 중개 오퍼레이션은 근본적으로 lazy(게으름)합니다.
- 여러 중개 오퍼레이션들을 메서드 체이닝을 하더라도 그 시점에서 코드가 수행되지는 않습니다. 모든 중개 오퍼레이션의 실행 시기는 종료 오퍼레이션의 호출 시점입니다. 그렇기에 중개 오퍼레이션의 반환 타입은 또 다른 Stream입니다.
javapublic static void main(String[] args) { List<String> names = new ArrayList<>(); names.add("hyunwoo"); names.add("blog"); names.add("26age"); names.add("무작정개발"); Stream<String> stringStream = names.stream().map(s->{ System.out.println(s); return s.toUpperCase(); }); names.forEach(System.out::println); }
1. names.stream().map(s->{....});
-> stream의 중개 오퍼레이터를 사용하는 순간에는 코드가 수행되지 않습니다. 그렇게 때문에 중개 오퍼레이션 map 안에 있는 출력문이 수행되지 않고, 수행시키기 위해서는 스트림 파이프라인(Stream PipeLine)을 정의해야 합니다.
- 쉽게 병렬 처리(multi-threading)를 할 수 있습니다.
- for문(반복문)을 stream을 통해 구현하면 간결하게 작성할 수 있습니다.
- parallelStream()을 이용하면 JVM이 병렬적으로 오퍼레이션을 처리해 줍니다.
javapublic static void main(String[] args) { List<String> names = new ArrayList<>(); names.add("hyunwoo"); names.add("blog"); names.add("26age"); names.add("무작정개발"); List<String> collect = names.parallelStream().map(s->{ System.out.println(s + " " + Thread.currentThread().getName()); return s.toUpperCase(); }).collect(Collectors.toList()); collect.forEach(System.out::println); }
-> 여기서 주의할 점은 parallelStream을 사용해서 병렬 처리를 한다고 성능이 무조건 좋아지는 것은 아닙니다.
오히려 느려질 수도 있기에 대부분의 경우에는 stream을 쓰는 것이 좋고, 대용량 데이터를 다룰 때 성능 테스트 후 parrllelStream()을 사용하면 됩니다.
2. Stream API의 중개, 종료 오퍼레이션
(1) 스트림 파이프라인 (Stream PipeLine)
스트림(Stream)이라는 컨베이어 벨트에 0개 혹은 다수의 중개 오퍼레이터(intermediate operation)와 1개의 종료 오퍼레이션(terminal operation)으로 구성합니다. 이 Stream은 반드시 하나의 종료 오퍼레이션이 있어야 하고, 만약에 종료 오퍼레이션이 없다면, Stream은 존재하지만 코드가 수행되지 않습니다.
-> 스트림(Stream)의 데이터 소스는 오직 터미널 오퍼레이션(종료 오퍼레이션)을 실행할 때에만 처리합니다.
(2) 중개 오퍼레이션 (Intermediate Operation)
- Stream을 리턴합니다.
- Stateless / Stateful 오퍼레이션으로 더 상세하게 구분 가능
- 대부분 Stateless이지만 distinct나 sorted처럼 이전 소스 데이터를 참조해야 하는 오퍼레이션은 Stateful 오퍼레이션입니다.
- filter, map, limit, skip, sorted 등
(3) 종료 오퍼레이션 (Terminal Operation)
- Stream을 리턴하지 않는다.
- collect, allmatch, count, forEach, min, max 등
javapublic static void main(String[] args) { List<String> names = new ArrayList<>(); names.add("hyunwoo"); names.add("blog"); names.add("26age"); names.add("무작정개발"); List<String> collect = names.stream().map(s -> { System.out.println(s); return s.toUpperCase(); }).collect(Collectors.toList()); collect.forEach(System.out::println); }
Reference
Stream
1. Stream 소개
catsbi.oopy.io
더 자바, Java 8 - 인프런 | 강의
자바 8에 추가된 기능들은 자바가 제공하는 API는 물론이고 스프링 같은 제 3의 라이브러리 및 프레임워크에서도 널리 사용되고 있습니다. 이 시대의 자바 개발자라면 반드시 알아야 합니다. 이
www.inflearn.com
블로그의 정보
무작정 개발
무작정 개발