스트림이라는 용어는 주변에서 많이 들어봤습니다. 스트리밍 서비스가 가장 대표적인 일 겁니다. 아래 그림으로 보시면 이해가 쉬울 것 같습니다. 왼쪽 이미지는 DVD에 데이터 전체가 저장되어서 재생되는 것을 보여줍니다. 오른쪽 이미지는 인터넷을 통해서 일부의 데이터가 들어오면 그 부분만 재생하여 보여주는 것을 표현합니다.
스트림이란 정확히 무엇일까요? 스트림은 '데이터 처리 연산을 지원하도록 소스에서 추출된 연속된 요소'로 정의될 수 있습니다. '데이터 처리 연산'은 함수형 프로그래밍 언어에서 지원하는 연산과 비슷한 연산을 지원합니다. 예를 들어, filter, map, reduce, match 등을 지원합니다. '소스'는 스트림으로 변환하기 위한 데이터를 말합니다. 컬렌션, 배열, I/O 자원 등이 있습니다. '연속된 요소'는 컬렉션(Collection)과 마찬가지로 특정 요소 형식으로 이루어진 연속된 값 집합의 인터페이스를 제공합니다.
스트림은 두 가지 중요한 특징을 가지고 있습니다. '파이프라이닝(pipelining)'은 스트림 연산끼리 연결해서 커다란 파이프 라인을 구성할 수 있게 합니다. '내부 반복'은 스트림 내부에 존재하는 반복자(for 같은 것이 내부에) 덕분에 작업을 투명하게 병렬로 처리하거나 더 최적화된 다양한 순서로 처리할 수 있습니다.
내부 반복이 아닌 외부 반복이 있습니다. 개발자가 직접 반복 코드(ex. for-each)를 만들어 주는 것 입니다. 아래 예시를 보면서 외부 반복과 내부 반복을 보겠습니다. 내부 반복은 스트림 내부에 있는 반복 코드를 사용합니다. 한 눈에 봐도 스트림을 이용한 방식이 이해하기 쉽다는 것을 알 수 있습니다.
// 외부 반복
List<String> highCaloricDishes = new ArrayList<>();
Iterator<String> iterator = menu.iterator();
while(iterator.hasNext()) {
Dish dish = iterator.next();
if(dish.getCalories() > 300){
highCaloricDished.add(d.getName());
}
}
// 내부 반복
List<String> highCaloricDish = menu.stream()
.filter(d -> d.getCalories() > 300)
.map(Dish::getName)
.collect(toList());
스트림을 이용하면 선언형으로 컬렉션 데이터를 처리할 수 있습니다. 뿐만 아니라, 데이터를 투명하게 병렬로 처리할 수 있습니다. 아래 예시에서 보겠습니다. menu 데이터에서 시작해서 filter -> sorted -> map 스트림 파이프라인을 형성했습니다. 예시를 통해서 알아본 스트림의 좋은 점은 선언형이고 조립할 수 있고 병렬화할 수 있다는 것 입니다.
List<String> lowCaloriesDishesName = menu.stream()
.filter(d -> d.getCalories() < 400)
.sorted(comparing(Dish::getCalories))
.map(Dish::getName)
.collect(toList());
List<String> lowCaloriesDishesName = menu.parallelStream()
.filter(d -> d.getCalories() < 400)
.sorted(comparing(Dish::getCalories))
.map(Dish::getName)
.collect(toList());
끝.
'IT > Java' 카테고리의 다른 글
Arrays.asList() vs ArrayList() (0) | 2022.12.19 |
---|---|
팩토리 패턴 (Factory Pattern) (0) | 2022.11.07 |
기본형과 참조형 변수 (0) | 2022.09.04 |
동작 파라미터화 (0) | 2022.08.13 |
자바 8 등장 (0) | 2022.08.13 |