[Java8] Stream API에 대해
by 무작정 개발이번에는 Java 8에 추가된 기능 중 Stream에 대해 정리할 것이다.
1. Stream이란?
Stream이란 Collection(컬렉션)과 같은 연속된 데이터를 처리하는 오퍼레이션의 모음, Stream 그 자체로는 데이터가 아닙니다.
Stream은 컨베이어 벨트와 비슷합니다.
컨베이어 벨트에 떡 조각(데이터)들을 흘려보내면서 반죽을 하고, 앙금을 쌓고(map), 불량품은 빼고(filter) 포장을 해서(collect) 내보냅니다.
(1) Stream의 특징
- 데이터를 담고 있는 자정소(Collection)이 아닙니다.
- Functional in nature
- Stream은 처리하는 데이터 소스를 변경하지 않습니다. 즉, A라는 데이터를 수정한다고 해서 원본 데이터가 수정되는 것이 아니라는 의미입니다.
public 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입니다.
public 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이 병렬적으로 오퍼레이션을 처리해 줍니다.
public 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 등
public 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
'Language > Java' 카테고리의 다른 글
[Java8] Date와 Time (1) | 2022.11.11 |
---|---|
[Java8] Optional 이란? (0) | 2022.11.11 |
[Java8] 인터페이스의 변화 - default method, static method (1) | 2022.11.10 |
[Java8] 함수형 인터페이스, 람다 표현식 (1) | 2022.11.08 |
[Java] if-else문 보다 switch문이 더 효율적인 이유 (2) | 2022.10.30 |
블로그의 정보
무작정 개발
무작정 개발