나를 기록하다
article thumbnail
반응형

우아한테크코스

Stream이란?

정의

  • 모던 자바 인 액션 - 스트림은 데이터처리 연산을 지원하도록 소스에서 추출된 연속된 값 요소이다.
  • 이것이 자바다 - 스트림은 컬렉션의 요소를 하나씩 참조해 람다식으로 처리할 수 있는 반복자이다.

→ 스트림은 데이터 컬렉션 반복을 멋지게 처리하는 기능이다!


Stream을 사용하는 이유

[예시] 연봉이 1억이 넘는 직장인들의 평균 연봉 구하기

BEFORE JAVA 8

int sum = 0;
int count = 0;
for(Employee emp : emps) {
	if (emp.getSalary() > 100_000_000) {
    	sum += emp.getSalary();
        count++;
    }
}
double average = (double) sum / count;
  • 자바 8이 나오기 전에는 어떻게 구현해야 하는지 하나씩 지시해야 했다. → HOW 중심의 코드

AFTER JAVA 8

double average = emps.stream()
	.filter(emp -> emp.getSalary() > 100_000_000)
    .mapToInt(Employee::getSalary)
    .average()
    .orElse(0)
    ;
  • 자바 8과 함께 Stream이 등장 → 연산에 필요한 매개변수를 사용하지 않게 되었다.
    → 직장인들을 데리고 있는 객체에 지시를 된다 → WHAT 중심의 코드
정리
스트림을 사용 전 - HOW 중심의 외부 반복을 사용
스트림을 사용 후 - WHAT 중심의 내부 반복 사용 → 직관적인 코드를 짤 수 있게 되었다.

[예시] 우형이의 코스 요리 식사 과정

우형이는 점심에 배가 고파서 A 코스를 주문하였다.

점심에는 컬렉션 요리사가 주문을 받는다. 컬렉션 요리사는 A 코스에 들어가는 샐러드, 쌀국수, 케이크를 차례대로 만들었다. 다 만들어졌어도 아직 요리사가 음식을 주지 않았기에 우형이는 아직 음식을 먹지 못한다. 요리사는 이걸 다 만들고 나서야 이제 'A 코스 음식 모두 나왔습니다'하고 넘긴다. 이렇게 모든 값을 다 처리하고 넘겨주는 방식을 적극적 생성이라고 한다.

 

저녁에도 우형이가 A 코스를 다시 주문한다.

저녁에는 스트림 요리사가 주문을 받는다. 스트림 요리사는 요리가 완성될 때마다 우형이에게 넘겨준다. 샐러드가 와서 먹고 다 먹으니 쌀국수를 주고, 그 다음에 케이크도 주면서 완성이 된다. 이런 식으로 요청할 때마다 값을 처리하고 넘겨주는 방식을 게으른 생성이라고 한다.

정리
자료구조가 포함하는 모든 값을 메서드에 포함하는 컬렉션과 다르게,
스트림은 요청할 때만 요소를 계산하는 고정된 자료구조를 가진다.

[예시] 우형이가 밀가루 음식만 먹고 싶을 때

처음에 방문한 식당의 요리사는 컬렉션 요리사이다. 컬렉션 요리사는 밀가루 음식이 뭔지 모른다. 하지만 적극적이기 때문에 일단은 자기가 할 수 있는 모든 요리를 만든다. 우형이는 요리사의 열정을 보지 않고 요리만 본다. 처음에 밀가루 음식인 케이크를 제공한다. 하지만 다음에 나온 쌀국수는 쌀가루라 안먹는데 계속 준다...실망한다...별점 테러를 한다...

 

다음으로 방문한 식당의 요리사는 스트림 요리사이다. 스트림 요리사는 밀가루 음식이 뭔지 모르고 게으르다. 요리를 완성될 때마다 주기는 한다. 처음에 밀가루 음식인 케이크를 제공한다. 그 다음으로 컬렉션 요리사처럼 쌀국수를 제공했다. 우형이는 쌀국수를 안먹는다고 했다. 그러자 스트림 요리사는 '알겠어. 이제 그만 만들게. 안녕 ~'하고 끝을 낸다. 우형이는 화가 난다...별점 테러를 한다...

 

둘다 별점 테러를 당했지만 스트림 요리사는 요리를 덜 만들었으니까 사실상 손해를 덜 본 셈이다. → 이런 걸 보고 쇼트 서킷이라고 한다.

정리
스트림은 특정 연산자를 사용할 때 여러 개의 조건이 중첩된 상황에서 값이 결정나면 불필요한 연산을 진행하지 않고 조건문을 빠져나와 실행 속도를 높힌다.

Stream 사용 방법 및 활용

스트림 구성

  1. 컬렉션을 스트림으로 만들어주는 연산자, 스트림을 생성하는 것
  2. 데이터를 처리하는 중간 연산자
  3. 연산을 정리하고 결과를 도출하는 최종 연산자

스트림 구성

위 그림의 구성(STREAM: 스트림 생성 / FILTER, SORTED, MAP: 중간 연산 / COLLECT: 최종 연산)

각 연산자끼리는 점을 찍어서 이어주는데 이 방식이 파이프 길이 있는 것과 닮았다고 파이프 라이닝이라 부른다.

 

스트림 요리사가 나타난다. 스트림 요리사는 보다 디테일한 요리과정을 보여 주기로 한다. 게으른 요리사는 중간 연산까지는 아무것도 안하고 가만히 있다. 그런데 최종 연산이 들어오면 그제서야 요리를 하기 시작한다. 그 결과 하나씩 필터 맵 필터 맵 이런식으로 붙이는데 이런 식의 데이터 처리 플로우를 우리는 루프 퓨전이라고 한다.

 

문제 - 중간 연산자 중에는 SORTED라는 것이 있는데 이게 들어오면 루프 퓨전은 어떻게 일어날까?

보기

  1. filter → sorted → map → filter → sorted → map
  2. filter → filter → sorted → map → sorted → map
  3. filter → filter → sorted → map → map

정답

  • 2번 - filter → filter → sorted → map → sorted → map

해설

sorted는 중간 연산자 이면서 특이하게 최종 연산자처럼 이전 연산자들이 실행되도록 한다.

그래서 그 이후로는 루프 퓨전이 반복적으로 일어난다.

 

스트림 활용 예시 - flatMap

사용 시기

  • List나 Collection이 중첩되어 있을 때 사용한다.

[예시] 리스트 커플들이 모여있는 리스트 존재(List<List<couple>>)

List<List<couple>>

  • 우리는 하나의 리스트로 만들고 싶다.
  • stream()을 사용해서 Stream<List<couple>>로 만듦.

Stream<List<couple>>

  • 이때 flatMap(List::stream) 사용 → 자유롭게 풀어진 상태, Stream의 의미처럼 흐르는 강가에 존재하는 상태

flatMap(List::stream) 사용 후

  • 이때 collect(Collectors.toList())를 사용 → 이제서야 리스트 완성

collect(Collectors.toList()) 사용 후

코드

List<couple> manyCouple = couple.stream()
	.map(Net::getCouples)
    .flatMap(List::stream)
    .collect(Collectors.toList())
    ;

스트림 활용 예시 - reduce

reduce란?

  • 스트림의 요소는 소모된다. 리듀스는 각 요소를 소모해가면서 연산을 처리하는 방식

코드

reduce(10, (acc, x) -> acc + x)

앞의 10은 초기값을 의미한다.


참고자료

https://www.youtube.com/watch?v=wsvhgrCGW78

 

반응형

'Review > Techotalk' 카테고리의 다른 글

[테코톡] MVC 패턴  (1) 2023.10.14
[테코톡] DTO와 VO  (0) 2023.09.12
[테코톡] OCP와 전략패턴  (0) 2023.09.11
profile

나를 기록하다

@prao

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

profile on loading

Loading...