[04] Collector

MIIINยท2023๋…„ 3์›” 19์ผ
0

Modern Java

๋ชฉ๋ก ๋ณด๊ธฐ
4/8
post-thumbnail

๐Ÿ”ฎ Collector

Collector ์ธํ„ฐํŽ˜์ด์Šค์˜ ๊ตฌํ˜„์€ Stream์˜ ์š”์†Œ๋ฅผ ์–ด๋–ค ์‹์œผ๋กœ ๋„์ถœํ• ์ง€ ์ง€์ •ํ•œ๋‹ค.

๊ณ ๊ธ‰ Reducing

Stream์—์„œ collect๋กœ ๊ฒฐ๊ณผ๋ฅผ ์ˆ˜์ง‘ํ•˜๋Š” ๊ณผ์ •์—์„œ Collector๋ฅผ ์ด์šฉํ•˜์—ฌ ๊ณ ์ˆ˜์ค€์˜ Reducing์„ ์‰ฝ๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋‹ค. Collectors ์œ ํ‹ธ๋ฆฌํ‹ฐ Class๋Š” ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” Collector ์ธ์Šคํ„ด์Šค๋ฅผ ์†์‰ฝ๊ฒŒ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋Š” ์ •์  Factory Method๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

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

๋ฏธ๋ฆฌ ์ •์˜๋œ Collector

Collectors์—์„œ ์ œ๊ณตํ•˜๋Š” Factory Method์˜ ๊ธฐ๋Šฅ์€ 3๊ฐ€์ง€๋กœ ๊ตฌ๋ถ„ํ•œ๋‹ค.

  1. Stream ์š”์†Œ๋ฅผ ํ•˜๋‚˜์˜ ๊ฐ’์œผ๋กœ Reducing & Summarize
  2. ์š”์†Œ Grouping
  3. ์š”์†Œ Partitioning
Factory MethodReturn TypeExample
toListList<T>์ŠคํŠธ๋ฆผ์˜ ๋ชจ๋“  ์š”์†Œ๋ฅผ ๋ฆฌ์ŠคํŠธ๋กœ ์ˆ˜์ง‘
toSetSet<T>์ŠคํŠธ๋ฆผ์˜ ๋ชจ๋“  ์š”์†Œ๋ฅผ ์ง‘ํ•ฉ์œผ๋กœ ์ˆ˜์ง‘
toCollectionCollection<T>์ŠคํŠธ๋ฆผ์˜ ๋ชจ๋“  ์š”์†Œ๋ฅผ ํŠน์ • Collection์œผ๋กœ ์ˆ˜์ง‘
countingLong์ŠคํŠธ๋ฆผ์˜ ๋ชจ๋“  ์š”์†Œ์˜ ๊ฐฏ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜
summingInt/Double/LongInteger/Double/Long์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ์˜ ์†์„ฑ๊ฐ’์„ ๋”ํ•จ
averageInt/Double/LongInteger/Double/Long์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ์˜ ์†์„ฑ ํ‰๊ท ๊ฐ’์„ ๊ณ„์‚ฐ
summarizingInt/Double/LongInteger/Double/Long์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ์˜ ์ตœ๋Œ“๊ฐ’, ์ตœ์†Ÿ๊ฐ’, ํ•ฉ๊ณ„, ํ‰๊ท  ๋“ฑ์˜ ์ •๋ณด ํ†ต๊ณ„ ์ˆ˜์ง‘
joiningString์ŠคํŠธ๋ฆผ์˜ ๋ชจ๋“  ์š”์†Œ์˜ toString()์„ ํ˜ธ์ถœํ•˜์—ฌ ์—ฐ๊ฒฐ
maxByOptional<T>์ฃผ์–ด์ง„ Comparator๋ฅผ ์ด์šฉํ•ด์„œ ์ŠคํŠธ๋ฆผ์˜ ์ตœ๋Œ“๊ฐ’ ์š”์†Œ๋ฅผ Optional๋กœ ๊ฐ์‹ผ ๊ฐ’์œผ๋กœ ๋ฐ˜ํ™˜
minByOptional<T>์ฃผ์–ด์ง„ Comparator๋ฅผ ์ด์šฉํ•ด์„œ ์ŠคํŠธ๋ฆผ์˜ ์ตœ์†Ÿ๊ฐ’ ์š”์†Œ๋ฅผ Optional๋กœ ๊ฐ์‹ผ ๊ฐ’์œผ๋กœ ๋ฐ˜ํ™˜
reducing๋ˆ„์ ์ž๋ฅผ ์ดˆ๊นƒ๊ฐ’์œผ๋กœ ์„ค์ •ํ•œ ๋‹ค์Œ์— BinaryOperator๋กœ ์ŠคํŠธ๋ฆผ์˜ ๊ฐ ์š”์†Œ๋ฅผ ๋ฐ˜๋ณต์ ์œผ๋กœ ๋ˆ„์ ์ž์™€ ํ•ฉ์ณ ์ŠคํŠธ๋ฆผ์„ ํ•˜๋‚˜์˜ ๊ฐ’์œผ๋กœ Reducing
collectingAndThen๋‹ค๋ฅธ Collector๋ฅผ ๊ฐ์‹ธ๊ณ  ๊ทธ ๊ฒฐ๊ณผ์— ๋ณ€ํ™˜ ํ•จ์ˆ˜ ์ ์šฉ
groupingByMap<Key, List<T>>ํ•˜๋‚˜์˜ ์†์„ฑ๊ฐ’์„ ๊ธฐ์ค€์œผ๋กœ ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๋ฅผ ๊ทธ๋ฃนํ™”ํ•˜๋ฉฐ ๊ธฐ์ค€ ์†์„ฑ๊ฐ’์„ ๊ฒฐ๊ณผ ๋งต์˜ ํ‚ค๋กœ ์‚ฌ์šฉ
partitioningByMap<Boolean, List<T>>Predicate๋ฅผ ์ŠคํŠธ๋ฆผ์˜ ๊ฐ ์š”์†Œ์— ์ ์šฉํ•œ ๊ฒฐ๊ณผ๋กœ ์š”์†Œ ๋ถ„ํ• 

๐ŸŒธ Reducing & Summarize

1. Collectors.counting()

Stream์—์„œ ์š”์†Œ์˜ ๊ฐœ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

long howManyDishes = menu.stream().collect(Collectors.counting());

2. Collectors.maxBy(), Collectors.minBy()

Stremam์˜ ์ตœ๋Œ“๊ฐ’๊ณผ ์ตœ์†Ÿ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

Comparator<Dish> dishCaloriesComparator = Comparator.comparingInt(Dish::getCalories);

-- Collectors.maxBy()
Optional<Dish> mostCaloriesDish = menu.stream().collect(Collectors.maxBy(dishCaloriesComparator));

-- Collectors.minBy()
Optional<Dish> leastCaloriesDish = menu.stream().collect(Collectors.minBy(dishCaloriesComparator));

3. ํ•ฉ๊ณ„

  • Collectors.summingInt(ToIntFunction<T>)
  • Collectors.summingDoulbe(ToDoubleFunction<T>)
  • Collectors.summingLong(ToLongFunction<T>)
int totalCalories = menu.stream().collect(Collectors.summingInt(Dish::getCalories));

4. ํ‰๊ท 

  • Collectors.averagingInt(ToIntFunction<T>)
  • Collectors.averagingDoulbe(ToDoubleFunction<T>)
  • Collectors.averagingLong(ToLongFunction<T>)
double averageCalories = menu.stream().collect(Collectors.averagingInt(Dish::getCalories));

5. ๋ชจ๋“  ์ •๋ณด

count, min, max, sum, average๋ฅผ ๋ชจ๋‘ ๋ฐ˜ํ™˜ํ•˜๋Š” SummaryStatistics ํด๋ž˜์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

  • Collectors.summarizingInt(ToIntFunction<T>) : IntSummaryStatistics
  • Collectors.summarizingDoulbe(ToDoubleFunction<T>) : DoubleSummaryStatistics
  • Collectors.summarizingLong(ToLongFunction<T>) : LongSummaryStatistics
IntSummaryStatistics menuStatistics = menu.stream().collect(Collectors.summarizingInt(Dish::getCalories));

6. Collectors.joining()

Stream์˜ ๊ฐ ๊ฐ์ฒด์— toString() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๋ชจ๋“  ๋ฌธ์ž์—ด์„ ํ•˜๋‚˜์˜ ๋ฌธ์ž์—ด๋กœ ์—ฐ๊ฒฐํ•˜์—ฌ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
joining ๋ฉ”์„œ๋“œ๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ StringBuilder๋ฅผ ์ด์šฉํ•˜์—ฌ ๋ฌธ์ž์—ด์„ ํ•˜๋‚˜๋กœ ๋งŒ๋“ค๋ฉฐ (๊ตฌ๋ถ„์ž, Prefix, Postfix)๋ฅผ ์ธ์ˆ˜๋กœ ์ „๋‹ฌํ•œ๋‹ค.

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

7. Collectors.reducing()

-- ํ•ฉ๊ณ„
int totalCalories =
		menu.stream()
			.collect(Collectors.reducing(0, Dish::getCalories, (s, t) -> s + t));

-- ํ•ฉ๊ณ„ - Integer ํด๋ž˜์Šค sum ์‚ฌ์šฉ
int totalCalories =
		menu.stream()
			.collect(Collectors.reducing(0, Dish::getCalories, Integer::sum));

-- ์ตœ๋Œ“๊ฐ’
Optional<Dish> mostCaloriesDish =
		menu.stream()
			.collect(Collectors.reducing((i, j) -> i.getCalories() < j.getCalories() ? j : i));

๐Ÿ‘ช Grouping

1. Collectors.grouping()

Classification Function (๋ถ„๋ฅ˜ ํ•จ์ˆ˜)๋กœ Stream ์š”์†Œ๋ฅผ ๊ทธ๋ฃนํ™” ํ•  ์ˆ˜ ์žˆ๋‹ค.

-- ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•
Map<Dish.Type, List<Dish>> dishesByType = menu.stream().collect(Collectors.groupingBy(Dish::getType));

2. Collectors.filtering()

Java SE 9์— ์ถ”๊ฐ€๋œ Collectors.filtering ๋ฉ”์„œ๋“œ๋กœ ์š”์†Œ๋“ค์˜ ์กฐ๊ฑด๋„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค.

-- Collectors.filtering ์‚ฌ์šฉ
Map<Dish.Type, List<Dish>> caloricDishesByType =
		menu.stream().collect(
				Collectors.groupingBy(
						Dish::getType,
						Collectors.filtering(dish -> dish.getCalories() > 500, Collectors.toList())
				)
		);

3. Collectors.mapping()

Collectors ํด๋ž˜์Šค๋Š” ๋งคํ•‘ ํ•จ์ˆ˜์™€ ๊ฐ ํ•ญ๋ชฉ์— ์ ์šฉํ•œ ํ•จ์ˆ˜๋ฅผ ๋ชจ์œผ๋Š” ๋ฐ ์‚ฌ์šฉํ•˜๋Š” ๋˜ ๋‹ค๋ฅธ ์ปฌ๋ ‰ํ„ฐ๋ฅผ ์ธ์ˆ˜๋กœ ๋ฐ›๋Š” mpaaing ๋ฉ”์„œ๋“œ๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

-- Collectors.mapping ์‚ฌ์šฉ
Map<Dish.Type, List<Dish>> caloricDishesByType =
		menu.stream().collect(
				Collectors.groupingBy(
						Dish::getType,
						Collectors.mapping(Dish::getName, Collectors.toList())
				)
		);

4. Collectors.flatMapping()

Java SE 9์— ์ถ”๊ฐ€๋œ Collectors.flatMapping ๋ฉ”์„œ๋“œ๋กœ ๋‘ ์ˆ˜์ค€์˜ ๋ฆฌ์ŠคํŠธ๋ฅผ ํ•œ ์ˆ˜์ค€์œผ๋กœ ํ‰๋ฉดํ™”ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ๋‹ค.

Map<Dish.Type, Set<String>> dishNamesByType =
		menu.stream()
				.collect(
						Collectors.groupingBy(
								Dish::getType,
								Collectors.flatMapping(dish -> dishTags.get(dish.getName()).stream(), Collectors.toSet())
						)
				);

5. ๋‹ค์ˆ˜์ค€ Grouping

groupingBy ๋ฉ”์„œ๋“œ๋ฅผ ์ค‘์ฒฉ ์‚ฌ์šฉํ•˜์—ฌ ๋‹ค์ˆ˜์ค€์œผ๋กœ ์ŠคํŠธ๋ฆผ์˜ ํ•ญ๋ชฉ์„ ๊ทธ๋ฃนํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค. n์ˆ˜์ค€ ๊ทธ๋ฃนํ™”์˜ ๊ฒฐ๊ณผ๋Š” n์ˆ˜์ค€ ํŠธ๋ฆฌ ๊ตฌ์กฐ๋กœ ํ‘œํ˜„๋˜๋Š” n์ˆ˜์ค€ ๋งต์ด ๋œ๋‹ค.

Map<Dish.Type, Map<CaloricLevel, List<Dish>>> dishesByTypeCaloricLevel =
		menu.stream()
				.collect(
						Collectors.groupingBy(
								Dish::getType,
								Collectors.groupingBy(dish -> {
									if (dish.getCalories() <= 400) return CaloricLevel.DIET;
									else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
									else return CaloricLevel.FAT;
								}, Collectors.toList())
						)
				);

6. ์„œ๋ธŒ ๊ทธ๋ฃน์œผ๋กœ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘

-- ํƒ€์ž…๋ณ„ ์Œ์‹ ๊ฐฏ์ˆ˜
Map<Dish.Type, Long> typesCount =
		menu.stream().collect(Collectors.groupingBy(Dish::getType, Collectors.counting()));

-- ํƒ€์ž…๋ณ„๋กœ ๊ฐ€์žฅ ๋†’์€ ์นผ๋กœ๋ฆฌ๋ฅผ ๊ฐ€์ง„ ์Œ์‹
Map<Dish.Type, Optional<Dish>> mostCaloricByType =
		menu.stream().collect(
				Collectors.groupingBy(
						Dish::getType, 
						Collectors.maxBy(Comparator.comparingInt(Dish::getCalories))
				)
		);

7. Collectors.collectingAndThen()

์ปฌ๋ ‰ํ„ฐ๊ฐ€ ๋ฐ˜ํ™˜ํ•œ ๊ฒฐ๊ณผ๋ฅผ ๋‹ค๋ฅธ ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

-- ์ปฌ๋ ‰ํ„ฐ ๊ฒฐ๊ณผ๋ฅผ ๋‹ค๋ฅธ ํ˜•์‹์— ์ ์šฉ
Map<Dish.Type, Dish> mostCaloricByType =
		menu.stream().collect(
				Collectors.groupingBy(
						Dish::getType,
						Collectors.collectingAndThen(
								Collectors.maxBy(Comparator.comparingInt(Dish::getCalories)), 
								Optional::get
						)
				)
		);

๐Ÿฅ Partitioning

Partitioning Function (๋ถ„ํ•  ํ•จ์ˆ˜)๋ผ ๋ถˆ๋ฆฌ๋Š” Predicate๋ฅผ ๋ถ„๋ฅ˜ ํ•จ์ˆ˜๋กœ ์‚ฌ์šฉํ•˜๋Š” ํŠน์ˆ˜ํ•œ ๊ทธ๋ฃนํ™” ๊ธฐ๋Šฅ์ด๋‹ค. ๋ถ„ํ•  ํ•จ์ˆ˜๋Š” Boolean์„ ๋ฐ˜ํ™˜ํ•˜๋ฏ€๋กœ ๋งต์˜ ํ‚ค ํ˜•์‹์€ Boolean์ด๋ฉฐ ๋‘ ๊ทธ๋ฃน(True or False)์œผ๋กœ ๋‚˜๋ˆ„์–ด์ง„๋‹ค.

Collectors.partitioningBy()

-- ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•
Map<Boolean, List<Dish>> partitionedMenu =
		menu.stream().collect(Collectors.partitioningBy(Dish::isVegetarian));

-- groupingBy์™€ ๊ฐ™์ด ์‚ฌ์šฉ
Map<Boolean, Map<Dish.Type, List<Dish>>> vegetarianDishesByType =
		menu.stream().collect(
				Collectors.partitioningBy(
						Dish::isVegetarian,
						Collectors.groupingBy(Dish::getType)
				)
		);

Collector Interface

Collector Interface๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

public interface Collector<T, A, R\> {
	Supplier<A\> supplier();
	BiConsumer<A, T\> accumulator();
	Function<A, R\> finisher();
	BinaryOperator<A\> combiner();
	Set<Characteristics\> characteristics();
}
  • T๋Š” ์ˆ˜์ง‘๋  ์ŠคํŠธ๋ฆผ ์š”์†Œ์˜ Generic Type์ด๋‹ค.
  • A๋Š” Accumulator(๋ˆ„์ ์ž), ์ฆ‰ ์ˆ˜์ง‘ ๊ณผ์ •์—์„œ ์ค‘๊ฐ„ ๊ฒฐ๊ณผ๋ฅผ ๋ˆ„์ ํ•˜๋Š” ๊ฐ์ฒด์˜ ํ˜•์‹์ด๋‹ค.
  • R์€ ์ˆ˜์ง‘ ์—ฐ์‚ฐ ๊ฒฐ๊ณผ ๊ฐ์ฒด์˜ ํ˜•์‹์ด๋‹ค.

1. supplier : ์ƒˆ๋กœ์šด ๊ฒฐ๊ณผ ์ปจํ…Œ์ด๋„ˆ ๋งŒ๋“ค๊ธฐ

๋นˆ ๊ฒฐ๊ณผ๋กœ ์ด๋ฃจ์–ด์ง„ Supplier๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉฐ, ์ˆ˜์ง‘ ๊ณผ์ •์—์„œ ๋นˆ ๋ˆ„์ ์ž ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“œ๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์—†๋Š” ํ•จ์ˆ˜์ด๋‹ค.

public Supplier<List<T\>> supplier() {
	return ArrayList::new;
}

2. accumulator : ๊ฒฐ๊ณผ ์ปจํ…Œ์ด๋„ˆ์— ์š”์†Œ ์ถ”๊ฐ€ํ•˜๊ธฐ

Reducing ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉฐ, ์ŠคํŠธ๋ฆผ์—์„œ n๋ฒˆ์งธ ์š”์†Œ๋ฅผ ํƒ์ƒ‰ํ•  ๋•Œ ๋‘ ์ธ์ˆ˜(๋ˆ„์ ์ž์™€ n๋ฒˆ์งธ ์š”์†Œ)๋ฅผ ํ•จ์ˆ˜์— ์ ์šฉํ•œ๋‹ค.

public BiConsumer<List<T\>, T\> accumulator() {
	return List::add;
}

3. finisher : ์ตœ์ข… ๋ณ€ํ™˜๊ฐ’์„ ๊ฒฐ๊ณผ ์ปจํ…Œ์ด๋„ˆ์— ์ ์šฉํ•˜๊ธฐ

์ŠคํŠธ๋ฆผ ํƒ์ƒ‰์„ ๋๋‚ด๊ณ  ๋ˆ„์ ์ž ๊ฐ์ฒด๋ฅผ ์ตœ์ข… ๊ฒฐ๊ณผ๋กœ ๋ณ€ํ™˜ํ•˜๋ฉด์„œ ๋ˆ„์  ๊ณผ์ •์„ ๋๋‚ผ ๋•Œ ํ˜ธ์ถœํ•  ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

public Function<List<T\>, List<T\>> finisher() {
	return Function.identity();
}

4. combiner : ๋‘ ๊ฒฐ๊ณผ ์ปจํ…Œ์ด๋„ˆ ๋ณ‘ํ•ฉ

์ŠคํŠธ๋ฆผ์˜ ์„œ๋กœ ๋‹ค๋ฅธ ์„œ๋ธŒ ํŒŒํŠธ๋ฅผ ๋ณ‘๋ ฌ๋กœ ์ฒ˜๋ฆฌํ•  ๋•Œ ๋ˆ„์ ์ž๊ฐ€ ์ด ๊ฒฐ๊ณผ๋ฅผ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ• ์ง€ ์ •์˜ํ•œ๋‹ค. ์ŠคํŠธ๋ฆผ์˜ Reducing ์ž‘์—…์„ ๋ณ‘๋ ฌ๋กœ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

public BinarayOperator<List<T\>> combiner() {
	return (list1, list2) -> {
		list1.addAll(list2);
		return list1;
	}
}

5. characteristics

Collector์˜ ์—ฐ์‚ฐ์„ ์ •์˜ํ•˜๋Š” Characteristics ํ˜•์‹์˜ ๋ถˆ๋ณ€ ์ง‘ํ•ฉ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

**Characteristics๋Š” ๋‹ค์Œ 3 ํ•ญ๋ชฉ์„ ํฌํ•จํ•˜๋Š” ์—ด๊ฑฐํ˜•์ด๋‹ค.

  • UNORDERED : Reducing ๊ฒฐ๊ณผ๋Š” ์ŠคํŠธ๋ฆผ ์š”์†Œ์˜ ๋ฐฉ๋ฌธ ์ˆœ์„œ๋‚˜ ๋ˆ„์  ์ˆœ์„œ์— ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š๋Š”๋‹ค.
  • CONCURRENT : ๋‹ค์ค‘ Thread์—์„œ accumulator ํ•จ์ˆ˜๋ฅผ ๋™์‹œ์— ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ์ŠคํŠธ๋ฆผ์˜ ๋ณ‘๋ ฌ Reducing์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • IDENTITY_FINISH : finsher ๋ฉ”์„œ๋“œ๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜๋Š” ๋‹จ์ˆœํžˆ identity๋ฅผ ์ ์šฉํ•  ๋ฟ์ด๋ฏ€๋กœ ์ด๋ฅผ ์ƒ๋žตํ•  ์ˆ˜ ์žˆ๋‹ค.
-- ์ปค์Šคํ…€ toList ๊ตฌํ˜„
public class ToListCollector<T> implements Collector<T, List<T>, List<T>> {
	@Override
	public Supplier<List<T>> supplier() {
		return ArrayList::new;
	}

	@Override
	public BiConsumer<List<T>, T> accumulator() {
		return List::add;
	}

	@Override
	public Function<List<T>, List<T>> finisher() {
		return Function.identity();
	}

	@Override
	public BinaryOperator<List<T>> combiner() {
		return (list1, list2) -> {
			list1.addAll(list2);
			return list1;
		};
	}

	@Override
	public Set<Characteristics> characteristics() {
		return EnumSet.of(Characteristics.IDENTITY_FINISH, Characteristics.CONCURRENT);
	}
}

List<Dish> dishes = menu.stream().collect(new ToListCollector<>());

profile
๋ฐฑ์—”๋“œ๊ฐœ๋ฐœ์ž

0๊ฐœ์˜ ๋Œ“๊ธ€