[모던 자바 인 액션] Chapter 4 스트림 소개

OhJuYeong·2025년 12월 18일

모던 자바 인 액션

목록 보기
3/9
post-thumbnail

4.1 스트림이란 무엇인가

스트림

  • 자바8 API에 새로 추가된 기능
  • 선언형으로 컬렉션 데이터 처리
    • 컬렉션이란?
      • 많은 수의 데이터를 그 사용 목적에 적합한 자료구조로 묶어 하나로 그룹화한 객체
      • ArrayList, LinkedList,Vector,Stack, HashSet, TreeSet, TreeMap 등
  • 데이터를 투명하게 병렬로 처리

EX) 저칼로리의 요리명을 반환 후 칼로리를 기준으로 요리를 정렬

자바 7 코드

List<Dish> lowCaloricDishes = new ArrayList<>();
for(Dish dish: menu){
	if(dish.getCalories() < 400){
		lowCaloricDishes.add(dish)
	}
}
Collections.sort(lowCaloricDishes, new Comparator<Dish>(){
	public int compare(Dish dish1, Dish dish2){
		return Integer.compare(dish1.getCalories(), dish2.getCalories());
	}});
	List<String> lowCaloricDishesName = new ArrayList<>();
	for(Dish dish: lowCaloricDishes){
		lowCaloricDishesName.add(dish.getName());
	}
  • lowCaloricDishes → 가비지 변수
List<String> lowCaloricDishesName = menu.stream()
																		.filter(d -> d.getCalories() <400)
																		.sorted(comparing(Dish::getCalories))
																		.map(Dish::getName)
																.collect(toList()); //모든 요리명을 리스트에 저장
														
List<String> lowCaloricDishesName = menu.parallelStream()
																	.filter( d -> d.getCalories() < 400)
																	.sorted(comparing(Dishes::getCalories))
																	.map(Dish::getName)
																	.collect(toList());
  • parallelStream()로 바꾸면 멀티코어 아키텍처에서 병렬로 실행가능

스트림 이득의 기능

  • 선언형으로 코드 구현 가능
    • 루프와 if 조건문 등의 제어 블록을 사용해서 어떻게 동작을 구현할지 지정할 필요 없이 저칼로리의 요리만 선택하라 같은 동작 수행을 지정
  • filter, sorted, map, collect 같은 여러 빌딩 블록 연산을 연결해서 복잡한 데이터 처리 파이프라인 만들 수 있음
  • 여러 연산을 연결해 가독성 명확성 유지

고수준 빌딩 블록

  • filer(또는 sorted, map, collect)
  • 특정 스레딩 모델에 제한되지 않고 자유롭게 상황 사용 가능

→ 데이터 처리 과정을 병렬화 하면 스레드와 락 걱정 필요 X

자바 8의 스트림 API 특징

  • 선언형
    • 더 간결하고 가독성 좋아짐
  • 조립할 수 있음
    • 유연성이 좋아짐
  • 병렬화
    • 성능이 좋아짐

4.2 스트림 시작하기

스트림이란

  • 데이터 처리 연산을 지원하도록 소스에서 추출된 연속된 요소
  • 연속된 요소
    • 특정 요소 형식으로 이루어진 연속된 값 집합의 인터페이스 제공
    • 표현 계산식이 주를 이룸
    • 켈력션의 주제는 데이터고 스트림의 주제는 계산
  • 소스
    • 스트림은 컬렉션, 배열, I/O 자원등의 데이터 제공 소스로부터 데이터를 소비함
    • 정렬된 컬렉션으로 스트림을 생성하면 그대로 유지
  • 데이터 처리 연산
    • 스트림은 함수형 프로그래밍 언어에서 일반적으로 지원하는 연산과 데이터베이스 와 비슷한 연산 지원
  • 파이프라이닝
    • 스트림 연산끼리 연결해서 커다란 파이프라인을 구성할 수 있도록 스트림 자신을 반환
  • 내부 반복
    • 반복자를 이용해서 명시적으로 반복하는 컬렉션과 달리 스트림은 내부 반복 지원

예제

List<String threeHighCaloricDishNames =
	menu.stream() //메뉴에서 스트림 얻기
			.filter(dish -> dish.getCalories() > 300)
			.map(Dish::getName)
			.limit(3)
			.collect(toList());

데이터 소스는 메뉴 → 데이터 소시 연속된 요소 를 스트림에 제공 → 데이터 처리 연산 적용 → collect를 제외한 모든 연산은 서로 파이프라인 형성 할 수 있도록 스트림 반환

  • filter
    • 람다를 인수로 받아 스트림에서 트특요소를 제외
  • map
    • 람다를 이용해서 한 요소를 다른 요솔 변환하거나 정보를 추출
  • limit
    • 정해진 개수 이상의 요소가 스트림에 저장되지 ㅁ소하게 슨트림 크기를 축소 truncate
  • collect
    • 스트림을 다른 형식으로 변환

4.3 스트림과 컬렉션

컬렉션 , 스트림 차이

  • 데이터를 언제 계산하느냐
  • 컬렉션
    • 모든 값을 메모리에 저장하는 자료구조
    • 모든 요소는 컬렉션에 추가하기 전에 계산되어야함
    • 적극적으로 생성
  • 스트림
    • 요청할때만 요소를 계산 하는 고정된 자료구조
    • 요청한 값만 스트림에서 추출한다는 것이 핵심
    • 생산자와 소비자 관계 형성
    • 게으르게 만들어지는 컬렉션

4.3.1 딱 한번만 탐색

스트림

  • 한번만 탐색 가능
  • 탐색된 스트림의 요소는 소비됨
  • 다시 탐색하려면 초기 데이터 소스에서 새로운 스트림 만들어야함

4.3.2 외부 반복, 내부 반복

외부반복

  • 컬렉션 인터페이스를 사용하려면 사용자가 직접 요소를 반복

내부반복

  • 스트림 라이브러리는 내부 반복 사용
  • 함수에 어떤 작업을 수행할지만 지정하면 모든 것이 알아서 처리
//컬렉션: for-each 루프를 이용하는 외부반복
List<String> names = new ArrayList<>();
for(Dish dish:menu){
	 names.add(dish.getName());
}
//스트림: 내부 반복
List<String> names = menu.stream()
											.map(Dish::getName)
											.collect(toList()); //파이프라인 실행함, 반복자 필요X									

컬렉션

  • 외부적으로 반복 해서 컬렉션 항목을 하나씩 가져와서 처리 해야함

내부 반복이 좋은 다른 두가지 이유

  • 투명하게 병렬로 처리하거나 더 최적화된 다양한 순서로 처리 가능

스트림 라이브러리 내부반복

  • 데이터 표현과 하드웨어를 활용한 병렬성 구현으로 자동으로 선택
  • 외부반복에서는 병렬성을 스스로 관리 해야함
List<String highCaloricDishes = menu.stream()
																.filter(dish -> dish.getCalories() > 300)
																.map(Dish::getName)
																.collect(toList());

4.4 스트림 연산

List<String threeHighCaloricDishNames =
	menu.stream() //메뉴에서 스트림 얻기
			.filter(dish -> dish.getCalories() > 300)
			.map(Dish::getName)
			.limit(3)
			.collect(toList());
  • filter,map,limit 서로 연결되어 파이프라인 형성
  • collect 파이프라인을 실행한 후 닫음

연결할 수 있는 스트림 연산

→ 중간 연산

스트림을 닫는 연산

→ 최종 연산

4.4.1 중간 연산

특징

  • 여러 중간 연산을 연결해서 질의를 만들 수 있음
  • 단말 연산을 스트림 파이프라인에 실행하기 전까지는 아무 수행하지 않음
  • 레이지
  • 중간 연산을 합친 다음에 합쳐진 중간 연산을 최종 연산으로 한 번에 처리

4.4.2 최종 연산

최종연산

  • 스트림 파이프라인에서 결과 도출
  • List, Integer, void등 스트림 이외의 결과 반환

4.4.3 스트림 이용하기

스트림 이용과정

  • 질의를 수행할 데이터 소스
  • 스트림 파이프라인을 구성할 중간 연산 연결
  • 스트림 파이프라인을 실행하고 결과를 만들 최종 연산

스트림 파이프라인의 개념 == 빌더 패턴과 비슷

profile
기록하는 개발자

0개의 댓글