algoqna

[JAVA] 스트림 본문

JAVA

[JAVA] 스트림

kkalgo 2023. 11. 24. 17:19

참고 : 모던 자바 인 액션

정의

  • 데이터 처리 연산을 지원하도록 소스에서 추출된 연속 요소

정의를 조금 더 디테일하게 살펴보자.

연속 요소

  • 컬렉션과 마찬가지로, 특정 요소 형식으로 이루어진 연속된 값 집합의 인터페이스
  • 컬렉션은 ArrayList를 사용할 지, LinkedList를 사용할지 고민한다.
    • 주로 시간과 공간의 복잡성과 관련된 요소 저장 및 접근 연산이 주
  • 스트림은 filter, sorted, map처럼 표현 계산식이 주를 이룬다
  • 컬렉션의 주제는 ‘데이터’, 스트림의 주제는 ‘계산’

소스

  • 컬렉션, 배열, I/O 자원 등의 데이터 제공 소스로부터 데이터를 소비
  • 예를 들어 리스트로 스트림을 만들면 스트림의 요소는 리스트의 요소와 같은 순서 유지

데이터 처리 연산

  • filter, map, reduce, find, match, sort 등으로 데이터를 조작한다.
  • 스트림이 제공하는 많은 메서드는 java api document를 참고하자.

파이프라이닝(Pipelining)

  • 스트림 연산끼리 연결해서 커다란 파이프 라인을 구성
    • 게으름, 쇼트서킷 같은 최적화를 얻을 수 있다.

내부 반복

  • 외부 반복이 아닌 내부에서 반복문 등이 실행
    List<String> threeHighCaloricDishNames = 
    	menu.stream()
    			.filter(dish -> dish.getCalories() > 300)
    			.map(Dish::getNAme)
    			.limit(3)
    			.collect(toList());
    /**
    1. menu로부터 stream을 만들어
    2. 300 칼로리 이상의 dish로 필터링하고
    3. 그들의 이름을 받아서
    4. 선착순 3개를
    5. 리스트로 반환하여 저장
    **/
    • 데이터 소스는 menu가 되고
    • filter, map, limit, collect가 데이터 처리 연산이다.
    • collect를 제외한 모든 연산이 파이프라인을 형성
    • 파이프라인은 소스에 적용하는 질의같은 존재라고 생각하자.

컬렉션과 스트림

  • DVD와 인터넷 스트리밍을 비교해보자.
  • DVD
    • 어떤 영화가 저장되있다 = 영화의 전체가 저장되어 있다. 이를 컬렉션으로 보자
  • 인터넷 스트리밍
    • 사용자가 시청하는 부분의 몇 프레임을 미리 내려받는다.
    • 다른 대부분의 값을 처리하지 않은 상태에서 미리 내려받은 프레임부터 재생

핵심 : 데이터를 언제 계산하느냐

  • 컬렉션은 현재 자료구조가 포함하는 모든 값을 메모리에 저장하는 자료구조.
    • 적극적 생성 = 모든 값을 계산할 때까지 기다린다.
  • 스트림은 이론적으로 요청할 때만 요소를 계산
    • 게으른 생성 : 필요할 때만 값을 계산한다.

스트림은 딱 한 번만 탐색할 수 있다

  • 한 번 탐색된 스트림의 요소는 사라진다.
  • 다시 탐색하려면, 새로운 스트림을 만들어야 한다.

외부 반복과 내부 반복

  • 외부 반복은 for문을 명시적으로 사용한 것과 같음
    • 또한 병렬성을 스스로 관리해야함
  • 내부 반복은 반복자가 필요없다.
  • 내부 반복의 장점
    • 작업을 병렬로 처리하거나, 최적화된 다양한 순서로 처리할 수 있다.

// 외부 반복

List<String> highCaloriesDishes = new ArrayList<>();
Iterator<String> iterator = menu.iterator();
while(iterator.hasNext()) {
	Dish dish = iterator.next();
	if (dish.getCalories() > 300 ) {
		highCaloricDishes.add(d.getName());
	}
}

// 내부 반복

List<String> highCaloriesDishes = 
	menu.stream()
		.filter(dish -> dish.getCalories > 300)
		.collect(toList());

스트림 연산 : 상태 없음과 상태 있음

  • map, filter 등은 입력 스트림에서 각 요소를 받아 0 또는 결과를 출력 스트림으로 보낸다.
  • 사용자가 제공한 람다나 메서드 참조가 내부적인 가변 상태를 갖지 않는다는 가정 하에
  • 이들은 내부 상태를 갖지 않는 연산(stateless operation) 이라고 한다.

  • reduce, sum, max 같은 연산은 결과를 누적할 내부 상태가 필요하다.
  • 이런 내부 상태의 크기는 한정되어 있다.
  • 어떤 요소를 출력 스트림으로 추가하려면 모든 요소가 버퍼에 추가되어 있어야 한다.
  • 이들을 내부 상태를 갖는 연산 (stateful operation)이라고 한다.

'JAVA' 카테고리의 다른 글

[Java] ThreadLocal  (0) 2023.12.28
[JAVA] 람다 메서드 참조  (0) 2023.11.23
[JAVA] 생성자(Constructor)  (0) 2023.02.18
[JAVA] 오버로딩(Overloading) / 오버라이딩(Overriding)  (0) 2023.02.18
[JAVA] pair 생성 후 정렬  (0) 2023.02.17