Stream 이란?
연속된 데이터를 처리하는 Operation(조작)의 모임.
컬렉션 이나 배열 등에 저장되어 있는 요소들을 하나씩 참조하며 반복적인 처리를 가능케 하는 기능이다.
Stream 을 이용한다면 불필요한 반복문이나 분기처리를 쓰지 않고도 직관적인 코드를 작성할 수 있다.
Stream 특징
- 데이터를 담고 있는 저장소(컬렉션)이 아니다.
- Functional in nature, 즉 Stream 이 처리하는 데이터 소스를 변경하지 않는다.
- 무제한일 수도 있다.
- 실시간으로 계속해서 Stream 으로 들어온 데이터를 Stream 으로 받아 처리할 수 있다.
- Short Circuit 메소드를 사용해서 제한하는 것도 가능하다.
- 중개 오퍼레이션들은 근복적으로 Lazy 하다.
- Stream API 는 크게 중계 오퍼레이션과 터미널 오퍼레이션, 두 가지로 나눌 수 있다.
- 이 둘의 뜻은 계속 이어지는 오퍼레이션, 종료시키는 오퍼레이션을 뜻한다.
- 이 둘의 가장 큰 차이는 Stream return 여부에 있다.
- 손쉽게 병렬처리 할 수 있다.
스트림 파이프 라인
- 0 또는 다수의 중개 오퍼레이션 (intermediate operation)과 한개의 종료 오퍼레이션 (terminal operation) 으로 구성한다.
- Stream 의 데이터 소스는 오직 터미널 오퍼레이션을 실행할 때에만 처리한다.
중개 오퍼레이션
- Stream 을 리턴한다.
- Stateless / Stateful 오퍼레이션으로 더 상세하게 구분할 수도 있다.
(대부분은 Stateless 지만, distinct 나 sorted 처럼 이전 소스 데이터를 참조해야하는 오퍼레이션은 Stateful 오퍼레이션이다.) - fillter, map, skip, sorted, ...
종료 오퍼레이션
- Stream 을 리턴하지 않는다.
- collect, allMatch, count, forEach, min, max, ...
parallelStream( )
- 일반 for 문과 if 문으로 구성된 코드를 stream 혹은 parallelStream 으로 변경해보자.
public class Main {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("hyokeun");
names.add("and");
names.add("jungkeung");
// 적용 전
for (String name: names) {
if (name.startsWith("h")) {
System.out.println(name.toUpperCase());
}
}
// 적용 후
names.parallelStream().filter(s -> s.startsWith("h")).map(String::toUpperCase)
.collect(Collectors.toList()).forEach(System.out::println);
}
}
- parallelStream 은 멀티 쓰레드를 사용하여 데이터를 처리하는 함수인데, 무조건 좋은것은 아니다.
- 쓰레드를 생성하고 자원 수집, 컨텍스트 스위칭 비용 등을 고려한 후 데이터가 방대하게 큰 경우에 stream 과 직접 성능 비교 한 후 사용하는것이 올바르다 .
Stream 실습
package com.study.java8to11;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class StreamStudy {
public static void main(String[] args) {
List<OnlineClass> springClasses = new ArrayList<>();
springClasses.add(new OnlineClass(1, "spring boot",true));
springClasses.add(new OnlineClass(2, "spring data jpa",true));
springClasses.add(new OnlineClass(3, "spring mvc",false));
springClasses.add(new OnlineClass(4, "spring core",false));
springClasses.add(new OnlineClass(5, "rest api dev",false));
System.out.println("spring 으로 시작하는 수업");
springClasses.stream()
.filter(s -> s.getTitle().startsWith("spring"))
.forEach(s -> System.out.println(s.getId()));
System.out.println("close 되지 않은 수업");
springClasses.stream()
.filter(Predicate.not(OnlineClass::isClosed))
.forEach(s -> System.out.println(s.getId()));
System.out.println("수업 이름만 모아서 스트림 만들기");
springClasses.stream()
.map(OnlineClass::getTitle)
.forEach(System.out::println);
List<OnlineClass> javaClasses = new ArrayList<>();
javaClasses.add(new OnlineClass(6, "The Java, Test",true));
javaClasses.add(new OnlineClass(7, "The Java, Code manipulation",true));
javaClasses.add(new OnlineClass(8, "The Java, 8 to 11",false));
List<List<OnlineClass>> hyokeunEvents = new ArrayList<>();
hyokeunEvents.add(springClasses);
hyokeunEvents.add(javaClasses);
System.out.println("두 수업 목록에 들어있는 모든 수업 아이디 출력");
hyokeunEvents.stream()
.flatMap(Collection::stream)
.forEach(s -> System.out.println(s.getId()));
System.out.println("10부터 1씩 증가하는 무제한 스트림 중에서 앞에 10개 빼고 최대 10개 까지만");
Stream.iterate(10 ,i -> i + 1)
.skip(10)
.limit(10)
.forEach(System.out::println);
System.out.println("자바 수업 중에 Test가 들어있는 수업이 있는지 확인");
boolean test = javaClasses.stream().anyMatch(oc -> oc.getTitle().contains("Test"));
System.out.println(test);
System.out.println("스프링 수업 중에 제목에 spring 이 들어간 제목만 모목서 List로 만들기");
springClasses.stream()
.map(OnlineClass::getTitle)
.filter(t -> t.contains("spring"))
.collect(Collectors.toList())
.forEach(System.out::println);
}
}
참고
'프로그래밍 > Java' 카테고리의 다른 글
Date와 Time API (0) | 2022.05.05 |
---|---|
Optional (0) | 2022.05.05 |
Lombok 동작원리 (0) | 2022.05.03 |
다이나믹 프록시 (0) | 2022.05.03 |
클래스 로더 (class loader) (0) | 2022.05.01 |