티스토리 뷰
스트림이란 무엇인가?
스트림은 자바 8 API에 새로 추가된 기능이다. 스트림을 이용하면 선언형으로 컬렉션 데이터를 처리할 수 있다.
일단 스트림이 데이터 컬렉션 반복을 멋지게 처리하는 기능이라고 생각하자. 스트림을 병렬로 처리할 수 도 있는데 이건 내가 잘 이해를 못하고 일하는 환경에서의 필요성을 아직은 잘 못느껴서 나중에 정리
기존 코드와 자바8 스트림을 활용한 코드를 비교
// JAVA8 이전
public static List<String> getLowCaloricDishesNamesInJava7(List<Dish> dishes) {
List<Dish> lowCaloricDishes = new ArrayList<>(); //가비지 변수 즉, 컨테이너 역할만 하는 중간변수이다. 오직 정렬 연산을 위해 필요한 변수
for (Dish d : dishes) {
if (d.getCalories() < 400) {
lowCaloricDishes.add(d);
}
}
List<String> lowCaloricDishesName = new ArrayList<>();
Collections.sort(lowCaloricDishes, new Comparator<Dish>() {
@Override
public int compare(Dish d1, Dish d2) {
return Integer.compare(d1.getCalories(), d2.getCalories());
}
});
for (Dish d : lowCaloricDishes) {
lowCaloricDishesName.add(d.getName());
}
return lowCaloricDishesName;
}
// JAVA8
public static List<String> getLowCaloricDishesNamesInJava8(List<Dish> dishes) {
return dishes.stream()
.filter(d -> d.getCalories() < 400) //400칼로리 이하 요리선택
.sorted(comparing(Dish::getCalories)) //칼로리로 정렬
.map(Dish::getName) //이름 추출
.collect(toList()); //모든 요리명을 리스트에 저장
}
자바8 이후 코드에서는 filter, sorted, map, collect 같은 여러 빌딩 블록 연산을 연결해서 복잡한 데이터 처리 파이프라인을 만들 수 있다.
여러 연산을 파이프라인으로 연결해도 여전히 가독성과 명확성이 유지된다.
스트림 API 특징
- 선언형: 더 간결하고 가독성이 좋아진다.
- 조립할 수 있음: 유연성이 좋아진다.
- 병렬화: 성능이 좋아진다.
스트림이란?
스트림이란 데이터 처리 연산을 지원하도록 소스에서 추출된 연속된 요소로 정의할 수 있다.
- 연속된 요소
- 컬렉션과 마찬가찬가지로 스트림은 특정 요소 형식으로 이루어진 연속된 값 집합의 인터페이스를 제공
- 컬렉션의 주제는 데이터고 스트림의 주제는 계산이다.
- 소스
- 스트림은 컬렉션, 배열, I/O 자원 등의 데이터 제공 소스로부터 데이털르 소비한다.
- 정렬된 컬렉션으로 스트림을 생성하면 정렬이 그대로 유지된다.
- 데이터 처리 연산
- 스트림은 함수형 프로그래밍 언어에서 일반적으로 지원하는 연산과 데이터베이스와 비슷한 연산을 지원하며, 순차적으로 또는 병렬로 실행할 수 있다.
스트림의 두 가지 중요 특징
- 파이프라이닝
- 스트림 연산은 스트림 연산끼리 연결해서 커다란 파이프라인을 구성할 수 있도록 스트림 자신을 반환한다. 그 덕에 게으름(lazyness), 쇼트서킷(short-circuiting) 같은 최적화도 얻을 수 있다.
- 내부 반복
- 반복자를 이용해서 명시적으로 반복하는 컬렉션과 달리 스트림은 내부 반복을 지원한다.
스트림은 딱 한 번만 탐색할 수 있다.
public class StreamVsCollection {
public static void main(String... args) {
List<String> names = Arrays.asList("Java8", "Lambdas", "In", "Action");
Stream<String> s = names.stream();
s.forEach(System.out::println);
// 스트림은 한 번 만 소비할 수 있으므로 아래 행의 주석을 제거하면 IllegalStateException이 발생
//s.forEach(System.out::println);
}
}
외부 반복과 내부 반복
컬렉션 인터페이스를 사용하려면 사용자가 직접 요소를 반복해야한다. ex) for-each 이를 외부 반복이라함.
스트림은 내부 반북오르 사용한다. 함수에 어떤 작업을 수행할지만 지정하면 모든 것이 알아서 처리된다.
//외부반복
List<String> names = new ArrayList<>();
for (Dish dish : menu) { //메뉴 리스트를 명시적으로 순차 반복
if (d.getCalories() < 400) {
names.add(dish.getName()); //이름을 추출해서 리스트에 추가
}
}
//내부반복
List<String> names = menu.stream()
.map(Dish::getName)
.collect(toList());
내부 반복을 이용하면 작업을 투명하게 병렬로 처리하거나 더 최적화된 다양한 순서로 처리할 수 있다.
외부 반복을 이용하는 경우에는 병렬성을 스스로 관리해야 하는 단점이 있다. 스트림 라이브러리의 내부 반복은 데이터 표현과 하드웨어를 활용한 병렬성 구현을 자동으로 선택한다.
스트림 연산
java.util.stream.Stream 인터페이스는 많은 연산을 정의하는데 크게 두 가지로 구분할 수 있다.
dishes.stream()
.filter(d -> d.getCalories() < 300) //중간연산
.map(Dish::getName) // 중간연산
.limit(3) //중간연산
.collect(toList()); //최종연산
연결할 수 있는 스트림 연산을 중간 연산(intermediate operation) 이라고 하고 스트림을 닫는 연산을 최종 연산(terminal operation)이라고 한다.
중간 연산
filter 나 sorted 같은 중간 연산은 다른 스트림을 반환하기때문에 여러 중간 연산을 연결해서 사용할 수 있다.
중간 연산의 특징은 단말 연산을 스트림 파이프라인에 실행하기 전까지는 아무 연산도 실행하지 않는다. 즉 lazy evalution을 한다.
Stream<Integer> integerStream = Stream.of(3, -2, 5, 8, -3, 10)
.filter(x -> x > 0)
.peek(x -> System.out.println("Peeking " + x))
.filter(x -> x % 2 == 0);
System.out.println("Before Collect");
List<Integer> integers = integerStream.collect(Collectors.toList());
System.out.println("After collect: "+ integers);
//스트림은 종결처리가 일어날때까지 미루고 미룬다. lazy evaluation
/**
Before Collect
Peeking 3
Peeking 5
Peeking 8
Peeking 10
After collect: [8, 10]
**/
최종 연산
최종 연산은 스트림 파이프라인에서 결과를 도출한다. 보통 최종 연산에 의해 List, Integer, void 등 스트림 이외의 결과가 반환된다.
- Total
- Today
- Yesterday
- Linux
- localtime
- k8s
- Kotlin
- maven
- 오라클
- springboot
- jQuery
- 북리뷰
- oracle
- Spring
- Github Status
- rocky
- LocalDateTime
- JavaScript
- 베리 심플
- intellij
- config-location
- Java
- window
- docker
- svn
- elasticsearch
- LocalDate
- Bash tab
- Spring Security
- mybatis config
- Mac
- mybatis
- input
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |