Post

🦊 chap6. 슀트림 으둜 데이터 μˆ˜μ§‘

6.1 μ»¬λ ‰ν„°λž€ 무엇인가?

ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°μ—μ„œλŠ” β€˜λ¬΄μ—‡β€™μ„ μ›ν•˜λŠ”μ§€ 직접 λͺ…μ‹œν•  수 μžˆμ–΄ μ–΄λ– ν•œ λ°©λ²•μœΌλ‘œ 이λ₯Ό μ–»μ„μ§€λŠ” μ‹ κ²½ μ“Έ ν•„μš”κ°€ μ—†λ‹€.

6.1.1 κ³ κΈ‰ 리듀싱 κΈ°λŠ₯을 μˆ˜ν–‰ν•˜λŠ” 컬렉터

μŠ€νŠΈλ¦Όμ— collect λ₯Ό ν˜ΈμΆœν•˜λ©΄ 슀트림의 μš”μ†Œμ— 리듀싱 연산이 μˆ˜ν–‰λœλ‹€.

λͺ…λ Ήν˜• ν”„λ‘œκ·Έλž˜λ°μ—μ„œ μš°λ¦¬κ°€ 직접 κ΅¬ν˜„ν•΄μ•Ό ν–ˆλ˜ μž‘μ—…μ΄ μžλ™μœΌλ‘œ μˆ˜ν–‰λœλ‹€.

보톡 ν•¨μˆ˜λ₯Ό μš”μ†Œλ‘œ λ³€ν™˜ν•  λ•Œ 컬렉터λ₯Ό μ μš©ν•˜λ©° μ΅œμ’… κ²°κ³Όλ₯Ό μ €μž₯ν•˜λŠ” μžλ£Œκ΅¬μ‘°μ— 값을 λˆ„μ ν•œλ‹€.

예λ₯Ό λ“€μ–΄ κ°€μž₯ 많이 μ‚¬μš©ν•˜λŠ” 직관적인 정적 λ©”μ„œλ“œλ‘œ toList λŠ” 슀트림의 λͺ¨λ“  μš”μ†Œλ₯Ό 리슀트둜 μˆ˜μ§‘ν•œλ‹€.

1
2
List<Transaction> transactions = 
		transactionStream.collect(Collectors.toList());

6.1.2 미리 μ •μ˜λœ 컬렉터

Collectors μ—μ„œ μ œκ³΅ν•˜λŠ” λ©”μ„œλ“œμ˜ κΈ°λŠ₯은 크게 μ„Έ κ°€μ§€λ‘œ ꡬ뢄할 수 μžˆλ‹€.

  • 슀트림 μš”μ†Œλ₯Ό ν•˜λ‚˜μ˜ κ°’μœΌλ‘œ λ¦¬λ“€μŠ€ν•˜κ³  μš”μ•½
  • μš”μ†Œ κ·Έλ£Ήν™”
  • μš”μ†Œ λΆ„ν• 

6.2 리듀싱과 μš”μ•½

μ»¬λ ‰ν„°λ‘œ 슀트림의 ν•­λͺ©μ„ μ»¬λ ‰μ…˜μœΌλ‘œ μž¬κ΅¬μ„±ν•  수 μžˆλ‹€.

즉 μ»¬λ ‰ν„°λ‘œ 슀트림의 λͺ¨λ“  ν•­λͺ©μ„ ν•˜λ‚˜μ˜ 결과둜 ν•©μΉ  수 μžˆλ‹€.

1
2
3
4
5
long howManyDishes = menu.stream().collect(Collectors.counting());

->

long howManyDishes = menu.stream().count();

6.2.1 μŠ€νŠΈλ¦Όκ°’μ—μ„œ μ΅œλŒ“κ°’κ³Ό μ΅œμ†Ÿκ°’ 검색

Collectors.maxBy Collectors.minBy λŠ” 슀트림의 μ΅œλŒ“κ°’κ³Ό μ΅œμ†Ÿκ°’μ„ 계산할 수 μžˆλ‹€.

1
2
3
4
5
6
Comparator<Dish> dishCaloriesComparator = 
		Comparator.comparingInt(Dish::getCalories);

Optional<Dish> mostCalorieDish = 
		menu.stream()
				.collect(maxBy(dishCaloriesComparator));

6.2.2 μš”μ•½ μ—°μ‚°

Collectors.summingInt λΌλŠ” μš”μ•½ νŒ©ν„°λ¦¬ λ©”μ„œλ“œλŠ” 객체λ₯Ό int 둜 λ§€ν•‘ν•˜λŠ” ν•¨μˆ˜λ₯Ό 인수둜 λ°›λŠ”λ‹€.

이 인수둜 μ „λ‹¬λœ ν•¨μˆ˜λŠ” 객체λ₯Ό int 둜 λ§€ν•‘ν•œ 컬렉터λ₯Ό λ°˜ν™˜ν•œλ‹€.

그리고 summingInt κ°€ collect λ©”μ„œλ“œλ‘œ μ „λ‹¬λ˜λ©΄ μš”μ•½ μž‘μ—…μ„ μˆ˜ν–‰ν•œλ‹€.

1
int totalCalories = menu.stream().collect(summingInt(Dish::getCalories));

Collectors.summingLong κ³Ό Collectors.summingDouble λ©”μ„œλ“œλ„ 같은 λ°©μ‹μœΌλ‘œ λ™μž‘ν•œλ‹€.

평균값 계산 λ“±μ˜ μ—°μ‚° μš”μ•½ κΈ°λŠ₯도 μ œκ³΅λœλ‹€.

1
2
double avgCalories = 
		menu.stream().collect(averagingInt(Dish::getCalories));

두 개 μ΄μƒμ˜ μš”μ•½ 연산을 ν•œ λ²ˆμ— μˆ˜ν–‰ν•΄μ•Ό ν•  λ•ŒλŠ” summarizingInt κ°€ λ°˜ν™˜ν•˜λŠ” 컬렉터λ₯Ό μ‚¬μš©ν•  수 μžˆλ‹€.

1
2
IntSummaryStatistics menuStatistics = 
		menu.stream().collect(summarizingInt(Dish::getCalories));

menuStatistics 객체λ₯Ό 좜λ ₯ν•˜λ©΄ λ‹€μŒκ³Ό 같은 정보가 ν™•μΈλœλ‹€.

1
2
IntSummaryStatistics{count = 9, sum = 4300, min = 120,
										average = 477.777778, max = 800}

6.2.3 λ¬Έμžμ—΄ μ—°κ²°

joining νŒ©ν† λ¦¬ λ©”μ„œλ“œλ₯Ό μ΄μš©ν•˜λ©΄ 각 객체에 toString λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•΄μ„œ μΆ”μΆœν•œ λͺ¨λ“  λ¬Έμžμ—΄μ„ ν•˜λ‚˜μ˜ λ¬Έμžμ—΄λ‘œ μ—°κ²°ν•΄μ„œ λ°˜ν™˜ν•œλ‹€.

1
String shortMenu = menu.stream().map(Dish::getName).collect(joining());

λ‚΄λΆ€μ μœΌλ‘œ StringBuilder λ₯Ό μ΄μš©ν•΄μ„œ λ¬Έμžμ—΄μ„ ν•˜λ‚˜λ‘œ λ§Œλ“ λ‹€.

두 μš”μ†Œ 사이에 ꡬ뢄 λ¬Έμžμ—΄μ„ 넣을 수 μžˆλ„λ‘ μ˜€λ²„λ‘œλ“œλœ joining 도 μ œκ³΅ν•œλ‹€.

1
2
String shortMenu = menu.stream().map(Dish::getName)
											.collect(joining(", "));

6.2.4 λ²”μš© 리듀싱 μš”μ•½ μ—°μ‚°

Collectors.reducing μœΌλ‘œλ„ 이듀을 μ •μ˜ν•  수 μžˆλ‹€.

νŠΉν™”λœ 컬렉터λ₯Ό μ‚¬μš©ν•œ μ΄μœ λŠ” ν”„λ‘œκ·Έλž˜λ°μ  νŽΈμ˜μ„± λ•Œλ¬Έμ΄λ‹€.

1
2
int totalCalories = menu.stream().collect(reducing(
														0, Dish::getCalories, (i, j) -> i + j));

reducing 은 μ„Έ 개의 인수λ₯Ό λ°›λŠ”λ‹€.

  1. 리듀싱 μ—°μ‚°μ˜ μ‹œμž‘κ°’ λ˜λŠ” μŠ€νŠΈλ¦Όμ— μΈμˆ˜κ°€ 없을 λ•ŒλŠ” λ°˜ν™˜κ°’
  2. λ³€ν™˜ ν•¨μˆ˜
  3. 같은 μ’…λ₯˜μ˜ 두 ν•­λͺ©μ„ ν•˜λ‚˜μ˜ κ°’μœΌλ‘œ μ—°μ‚°ν•˜λŠ” Operator
1
2
3
Optional<Dish> mostCalorieDish = 
		menu.stream().collect(reducing(
				(d1, d2) -> d1.getCalories() > d2.getCalories() ? d1 : d2));

λ§Œμ•½ reducing 이 ν•œ 개의 인수λ₯Ό 가지면 슀트림의 첫 번째 μš”μ†Œλ₯Ό μ‹œμž‘ μš”μ†Œ (1번째 인수) 둜 λ°›λŠ”κ²ƒ,

그리고 μžμ‹ μ„ κ·ΈλŒ€λ‘œ λ°˜ν™˜ν•˜λŠ” ν•­λ“± ν•¨μˆ˜λ₯Ό 두 번째 인수둜 λ°›λŠ” κ²ƒμœΌλ‘œ κ°„μ£Όν•œλ‹€.

κ·Έλž˜μ„œ λ§Œμ•½ 빈 슀트림이 λ„˜κ²¨μ§€λ©΄ μ‹œμž‘κ°’μ΄ μ„€μ •λ˜μ§€ μ•ŠλŠ” 상황이 λ²Œμ–΄μ§€κΈ° λ•Œλ¬Έμ— Optional 을 λ°˜ν™˜ν•œλ‹€.

reducing μ—μ„œ λžŒλ‹€ λŒ€μ‹  Integer ν•¨μˆ˜λ₯Ό μ¨μ„œ 더 κ°„λ‹¨ν•˜κ²Œ λ§Œλ“€ μˆ˜λ„ μžˆλ‹€.

1
2
3
4
5
int totalCalories = menu.stream().collect(reducing(0, 
																	Dish::getCalories,
																	Integer::sum));

int totalCalories = menu.stream().mapToInt(Dish::getCalories).sum();

6.3 κ·Έλ£Ήν™”

μžλ°” 8의 ν•¨μˆ˜ν˜•μ„ μ΄μš©ν•˜λ©΄ κ°€λ…μ„±μžˆλŠ” ν•œ μ€„μ˜ μ½”λ“œλ‘œ κ·Έλ£Ήν™”λ₯Ό κ΅¬ν˜„ν•  γ…… γ…œμžˆλ‹€.

1
2
Map<Dish.Type, List<Dish>> dishesByType = 
		menu.stream().collect(groupingBy(Dish::getType));

groupingBy ν•¨μˆ˜λ₯Ό κΈ°μ€€μœΌλ‘œ 슀트림이 κ·Έλ£Ήν™”λ˜λ―€λ‘œ 이λ₯Ό λΆ„λ₯˜ ν•¨μˆ˜λΌκ³  λΆ€λ₯Έλ‹€.

λ‹¨μˆœν•œ 속성 μ ‘κ·Όμž λŒ€μ‹  더 λ³΅μž‘ν•œ λΆ„λ₯˜ 기쀀이 ν•„μš”ν•œ μƒν™©μ—μ„œλŠ” λ©”μ„œλ“œ μ°Έμ‘°λ₯Ό λΆ„λ₯˜ ν•¨μˆ˜λ‘œ μ‚¬μš©ν•  수 μ—†λ‹€.

λŒ€μ‹  λžŒλ‹€ ν‘œν˜„μ‹μœΌλ‘œ 이λ₯Ό κ΅¬ν˜„ν•  수 μžˆλ‹€.

1
2
3
4
5
6
7
8
9
public enum CaloricLevel { DIET, NORMAL, FAT }

Map<CaloricLevel, List<Dish>> dishesByCaloricLevel = menu.stream().collect(
		groupingBy(dish -> {
			if (dish.getCalories() <= 400) return CaloricLevel.DIET;
			else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
			else return CaloricLevel.FAT;

		}));

6.3.1 κ·Έλ£Ήν™”λœ μš”μ†Œ μ‘°μž‘

μš”μ†Œλ₯Ό κ·Έλ£Ήν™” ν•œ λ‹€μŒμ—λŠ” 각 κ²°κ³Ό 그룹의 μš”μ†Œλ₯Ό μ‘°μž‘ν•˜λŠ” 연산이 ν•„μš”ν•˜λ‹€.

1
2
3
Menu<Dish.Type, List<Dish>> caloricDishesByType = 
		menu.stream().filter(dish -> dish.getCalories() > 500)
								.collect(groupingBy(Dish::getType));

이 μ½”λ“œμ˜ 단점은 μ—†λŠ” 결과에 λŒ€ν•΄μ„œλŠ” Key μžμ²΄κ°€ μ‚¬λΌμ§„λ‹€λŠ” 점이닀.

κ·Έλž˜μ„œ groupingBy νŒ©ν† λ¦¬ λ©”μ„œλ“œλ₯Ό μ˜€λ²„λ‘œλ“œν•΄ 이 문제λ₯Ό ν•΄κ²°ν•œλ‹€.

1
2
3
4
Menu<Dish.Type, List<Dish>> caloricDishesByType = 
		menu.stream()
				.collect(groupingBy(Dish::getType,
								filtering(dish -> dish.getCalories() > 500, toList())));

filtering λ©”μ„œλ“œλŠ” Collectors 클래슀의 또 λ‹€λ₯Έ 정적 νŒ©ν† λ¦¬ λ©”μ„œλ“œλ‘œ ν”„λ ˆλ””μΌ€μ΄νŠΈλ₯Ό 인수둜 λ°›μ•„ μž¬κ·Έλ£Ήν™” ν•œλ‹€.

κ·Έλž˜μ„œ λΉ„μ–΄μžˆλŠ” λͺ©λ‘λ„ ν•­λͺ©μœΌλ‘œ μΆ”κ°€λœλ‹€.

mapping λ©”μ„œλ“œλŠ” 그룹의 각 μš”λ¦¬λ₯Ό κ΄€λ ¨ 이름 λͺ©λ‘μœΌλ‘œ λ³€ν™˜ν•  수 μžˆλ‹€.

1
2
3
4
Menu<Dish.Type, List<String>> dishNamesByType = 
		menu.stream()
				.collect(groupingBy(Dish::getType,
								mapping(Dish::getName, toList())));

flatMapping 컬렉터λ₯Ό μ΄μš©ν•˜λ©΄ μ§‘ν•©μœΌλ‘œ κ·Έλ£Ήν™”ν•˜κ³  쀑볡을 μ œκ±°ν•  수 μžˆλ‹€.

1
2
3
4
Menu<Dish.Type, Set<String>> dishNamesByType = 
		menu.stream()
				.collect(groupingBy(Dish::getType,
								flatMapping(Dish::getName).stream(), toSet())));

6.3.2 λ‹€μˆ˜μ€€ κ·Έλ£Ήν™”

groupingBy λ₯Ό μ΄μš©ν•΄ ν•­λͺ©μ„ λ‹€μˆ˜μ€€μœΌλ‘œ κ·Έλ£Ήν™”ν•  수 μžˆλ‹€.

1
2
3
4
5
6
7
8
9
10
11
12
13
Map<Dish.Type, Map<CaloricLevel, List<Dish>>> dishesByTypeCaloricLevel = 
menu.stream().collect(
	groupingBy(Dish::getType,
			groupingBy(dish -> {
					if(dish.getCalories() <= 400)
						return CaloricLevel.DIET
					else if (dish.getCalories() <= 700)
						return CaloricLevel.NORMAL;
					else
						return CaloricLevel.FAT;
			})
	)
);

n μˆ˜μ€€ κ·Έλ£Ήν™”μ˜ κ²°κ³ΌλŠ” n μˆ˜μ€€ 트리 ꡬ쑰둜 ν‘œν˜„λ˜λŠ” n μˆ˜μ€€ 맡이 λœλ‹€.

6.3.3 μ„œλΈŒκ·Έλ£ΉμœΌλ‘œ 데이터 μˆ˜μ§‘

groupingBy 둜 λ„˜κ²¨μ£ΌλŠ” μ»¬λ ‰ν„°μ˜ ν˜•μ‹μ€ μ œν•œμ΄ μ—†λ‹€.

1
2
Map<Dish.Type, Long> typesCount = menu.stream().collect(
									groupingBy(Dish::getType, counting()));
1
2
3
4
Map<Dish.Type, Optional<Dish>> mostCaloricByType = 
		menu.stream()
				.collect(groupingBy(Dish::getType,
														maxBy(comparingInt(Dish::getCalories))));

νŒ©ν† λ¦¬ λ©”μ„œλ“œ collectingAndThen 은 μ μš©ν•  컬렉터와 λ³€ν™˜ν•¨μˆ˜λ₯Ό λ°›μ•„ λ‹€λ₯Έ 컬렉터λ₯Ό λ°˜ν™˜ν•œλ‹€.

1
2
3
4
5
6
Map<Dish.Type, Dish> mostCaloricByType = 
		menu.stream()
				.collect(groupingBy(Dish::getType,
							collectingAndThen(
									maxBy(comparingInt(Dish::getCalories)),
							Optional.get)));

6.4 λΆ„ν• 

뢄할은 λΆ„ν•  ν•¨μˆ˜λΌ λΆˆλ¦¬λŠ” ν”„λ ˆλ””μΌ€μ΄νŠΈλ₯Ό λΆ„λ₯˜ ν•¨μˆ˜λ‘œ μ‚¬μš©ν•œλ‹€.

λΆ„ν•  ν•¨μˆ˜λŠ” Boolean 을 κΈ°μ€€μœΌλ‘œ λ‚˜λ‰œ μ°Έ, 거짓 그룹으둜만 λΆ„λ¦¬λœλ‹€.

1
2
Map<Boolean, List<Dish>> partitionedMenu = 
		menu.stream().collect(partitioningBy(Dish::isVegerarian));

6.4.1 λΆ„ν• μ˜ μž₯점

λΆ„ν•  ν•¨μˆ˜κ°€ λ°˜ν™˜ν•˜λŠ” μ°Έ, 거짓 μš”μ†Œμ˜ 슀트림 리슀트λ₯Ό λͺ¨λ‘ μœ μ§€ν•œλ‹€λŠ” 것이 μž₯점이닀.

컬렉터λ₯Ό 두 번째 인수둜 전달할 수 μžˆλŠ” μ˜€λ²„λ‘œλ“œλœ 버전도 μžˆλ‹€.

1
2
3
4
Map<Boolean, Map<Dish.Type, List<Dish>>> vegetarianDishesByType = 
		menu.stream().collect(
					partitioningBy(Dish::isVegerarian,
												groupingBy(Dish::getType)));

이λ₯Ό μ΄μš©ν•˜μ—¬ 채식 μš”λ¦¬μ™€ 채식이 μ•„λ‹Œ μš”λ¦¬ 각각의 κ·Έλ£Ήμ—μ„œ κ°€μž₯ μΉΌλ‘œλ¦¬κ°€ 높은 μš”λ¦¬λ„ 찾을 수 μžˆλ‹€.

1
2
3
4
5
Map<Boolean, Map<Dish.Type, List<Dish>>> vegetarianDishesByType = 
		menu.stream().collect(
					partitioningBy(Dish::isVegerarian,
							collectingAndThen(maxBy(comparingInt(Dish::getCalories)),
																Optional.get)));

6.4.2 숫자λ₯Ό μ†Œμˆ˜μ™€ λΉ„μ†Œμˆ˜λ‘œ λΆ„ν• ν•˜κΈ°

1
2
3
4
public boolean isPrime(int candidate) {
		return IntStream.range(2, candidate)
										.nonMatch(i -> candidate % i == 0);
}
1
2
3
4
5
public Map<Boolean, List<Integer>> partitionPrimes(int n) {
		return IntStream.rangeClosed(2, n).boxed()
										.collect(
												partitioningBy(candidate -> isPrime(candidate)));
}

6.5 Collector μΈν„°νŽ˜μ΄μŠ€

Collector μΈν„°νŽ˜μ΄μŠ€λ₯Ό 직접 κ΅¬ν˜„ν•΄μ„œ 더 효율적으둜 문제λ₯Ό ν•΄κ²°ν•˜λŠ” 컬렉터λ₯Ό λ§Œλ“€ 수 μžˆλ‹€.

Collector μΈν„°νŽ˜μ΄μŠ€μ˜ μ‹œκ·Έλ‹ˆμ²˜μ™€ λ©”μ„œλ“œ μ •μ˜λŠ” λ‹€μŒκ³Ό κ°™λ‹€.

1
2
3
4
5
6
7
public interface Collector<T, A, R> {
	Supplier<A> supplier();
	BiConsumer<A, T> accumulator();
	Function<A, R> finisher();
	BinaryOperator<A> combiner();
	Set<Characteristics> characteristics();
}
  • T : μˆ˜μ§‘λ  슀트림 ν•­λͺ©μ˜ μ œλ„€λ¦­ ν˜•μ‹
  • A : λˆ„μ μž, 쀑간 κ²°κ³Όλ₯Ό λˆ„μ ν•˜λŠ” 객체의 ν˜•μ‹
  • R : μˆ˜μ§‘ μ—°μ‚° κ²°κ³Ό 객체의 ν˜•μ‹ (λŒ€κ²Œ μ»¬λ ‰μ…˜)

μš°λ¦¬κ°€ λ§Œλ“€μ–΄λ³Ό μ½œλ ‰ν„°μ˜ ν˜•μ‹μ€ 이렇닀.

1
public class ToListCollector<T> implements Collector<T, List<T>, List<T>>

6.5.1 Collector μΈν„°νŽ˜μ΄μŠ€μ˜ λ©”μ„œλ“œ μ‚΄νŽ΄λ³΄κΈ°

  • Supplier

supplier λ©”μ„œλ“œλŠ” 빈 λˆ„μ μž μΈμŠ€ν„΄μŠ€λ₯Ό λ§Œλ“œλŠ” νŒŒλΌλ―Έν„°κ°€ μ—†λŠ” ν•¨μˆ˜μ΄λ‹€.

1
2
3
public Supplier<List<T>> supplier() {
	return () -> new ArrayList<T>();
}
  • Acuumulator

accumulator λ©”μ„œλ“œλŠ” 리듀싱 연산을 μˆ˜ν–‰ν•˜λŠ” ν•¨μˆ˜λ₯Ό λ°˜ν™˜ν•œλ‹€.

1
2
3
public BiConsumar<List<T>, T> accumulator() {
	return (list, item) -> list.add(item);
}
  • Finisher

finisher λ©”μ„œλ“œλŠ” 슀트림 탐색을 끝내고 λˆ„μ μž 객체λ₯Ό μ΅œμ’… 결과둜 λ°˜ν™˜ν•˜λ©΄μ„œ λˆ„μ  과정을 끝낼 λ•Œ ν˜ΈμΆœν•  ν•¨μˆ˜λ₯Ό λ°˜ν™˜ν•΄μ•Ό ν•œλ‹€.

1
2
3
public Function<List<T>, List<T>> finisher() {
	return Function.identity();
}
  • Combiner

combiner λ©”μ„œλ“œλŠ” 리듀싱 μ—°μ‚°μ—μ„œ μ‚¬μš©ν•  ν•¨μˆ˜λ₯Ό λ°˜ν™˜ν•œλ‹€.

슀트림의 μ„œλ‘œ λ‹€λ₯Έ μ„œλΈŒνŒŒνŠΈλ₯Ό λ³‘λ ¬λ‘œ μ²˜λ¦¬ν•  λ•Œ λˆ„μ μžκ°€ 이 κ²°κ³Όλ₯Ό μ–΄λ–»κ²Œ μ²˜λ¦¬ν• μ§€ μ •μ˜ν•œλ‹€.

1
2
3
4
5
6
public BinaryOperator<List<T>> combiner() {
	return (list1, list2) -> {
		list1.addAll(list2);
		return list1;
	}
}
  • Characteristics

Characteristics λ©”μ„œλ“œλŠ” μ»¬λ ‰ν„°μ˜ 연산을 μ •μ˜ν•˜λŠ” λΆˆλ³€ 집합을 λ°˜ν™˜ν•œλ‹€.

Characteristics λŠ” μ„Έ ν•­λͺ©μ„ ν¬ν•¨ν•˜λŠ” μ—΄κ±°ν˜•μ΄λ‹€.

  • UNORDERED : 리듀싱 κ²°κ³ΌλŠ” 슀트림 μš”μ†Œ λ°©λ¬Έ μˆœμ„œλ‚˜ λˆ„μ  μˆœμ„œμ— 영ν–₯받지 μ•ŠμŒ
  • CONCURRENT : 닀쀑 μŠ€λ ˆλ“œμ—μ„œ acuumulator ν•¨μˆ˜λ₯Ό λ™μ‹œμ— ν˜ΈμΆœν•  수 있으며 병렬 리듀싱을 μˆ˜ν–‰ν•  수 μžˆλ‹€.
  • IDENTITY_FINISH : finisher λ©”μ„œλ“œκ°€ λ°˜ν™˜ν•˜λŠ” ν•¨μˆ˜λŠ” λ‹¨μˆœνžˆ identity λ₯Ό μ μš©ν•  λΏμ΄λ―€λ‘œ 이λ₯Ό μƒλž΅ν•  수 μžˆλ‹€.

이제 μš°λ¦¬κ°€ λ§Œλ“  컬렉터λ₯Ό μ˜ˆμ œμ— μ‚¬μš©ν•  수 μžˆλ‹€.

1
List<Dish> dishes = menuStream.collect(new ToListCollector<Dish>());
This post is licensed under CC BY 4.0 by the author.