Post

๐Ÿน chap6. ์ŠคํŠธ๋ฆผ ์œผ๋กœ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘

  • ํ†ตํ™”๋ณ„๋กœ ํŠธ๋žœ์žญ์…˜์„ ๊ทธ๋ฃนํ™”ํ•œ ์ฝ”๋“œ(๋ช…๋ นํ˜• ๋ฒ„์ „)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ๊ทธ๋ฃนํ™”ํ•œ ํŠธ๋žœ์žญ์…˜์„ ์ €์žฅํ•  ๋งต ์ƒ์„ฑ
Map<Currency, List<Transaction>> transactionsByCurrencies = new HashMap<>();

for (Transaction transaction: transactions) {
    Currency currency = transaction.getCurrency(); // ํŠธ๋žœ์žญ์…˜์˜ ํ†ตํ™”๋ฅผ ์ถ”์ถœ
    List<Transaction> transactionsForCurrency =
        transactionsByCurrencies.get(currency);
    if (transactionsForCurrency == null) {
        // ํ˜„์žฌ ํ†ตํ™”๋ฅผ ๊ทธ๋ฃนํ™”ํ•˜๋Š” ๋งต์— ํ•ญ๋ชฉ์ด ์—†์œผ๋ฉด ํ•ญ๋ชฉ์„ ๋งŒ๋“ ๋‹ค.
        transactionsForCurrency = new ArrayList<>();
        transactionsByCurrencies.put(currency, transactionsForCurrency);
    }
    // ๊ฐ™์€ ํ†ตํ™”๋ฅผ ๊ฐ€์ง„ ํŠธ๋žœ์žญ์…˜ ๋ฆฌ์ŠคํŠธ์— ํ˜„์žฌ ํƒ์ƒ‰ ์ค‘์ธ ํŠธ๋žœ์žญ์…˜์„ ์ถ”๊ฐ€ํ•œ๋‹ค. 
    transactionsForCurrency.add(transaction);
}

โ€˜ํ†ตํ™”๋ณ„๋กœ ํŠธ๋žœ์žญ์…˜ ๋ฆฌ์ŠคํŠธ๋ฅผ ๊ทธ๋ฃนํ™”ํ•˜์‹œ์˜คโ€™๋ผ๊ณ  ๊ฐ„๋‹จํžˆ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์ฝ”๋“œ๊ฐ€ ๋ฌด์—‡์„ ์‹คํ–‰ํ•˜๋Š”์ง€ ํ•œ ๋ˆˆ์— ํŒŒ์•…ํ•˜๊ธฐ ์–ด๋ ต๋‹ค. Stream์— toList๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋Œ€์‹  ๋” ๋ฒ”์šฉ์ ์ธ ์ปฌ๋ ‰ํ„ฐ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ collect ๋ฉ”์„œ๋“œ์— ์ „๋‹ฌํ•จ์œผ๋กœ์จ ์›ํ•˜๋Š” ์—ฐ์‚ฐ์„ ๊ฐ„๊ฒฐํ•˜๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ํ†ตํ™”๋ณ„๋กœ ํŠธ๋žœ์žญ์…˜์„ ๊ทธ๋ฃนํ™”ํ•œ ์ฝ”๋“œ(ํ•จ์ˆ˜ํ˜• ๋ฒ„์ „)
1
2
Map<Currency, List<Transaction>> transactionsByCurrencies =
    transactions.stream().collect(groupingBy(Transaction::getCurrency));

6.1 ์ปฌ๋ ‰ํ„ฐ๋ž€ ๋ฌด์—‡์ธ๊ฐ€?

์œ„ ์˜ˆ์ œ๋Š” ๋ช…๋ นํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์— ๋น„ํ•ด ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ด ์–ผ๋งˆ๋‚˜ ํŽธ๋ฆฌํ•œ์ง€ ๋ช…ํ™•ํ•˜๊ฒŒ ๋ณด์—ฌ์ค€๋‹ค.

ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ๋Š” โ€˜๋ฌด์—‡โ€™์„ ์›ํ•˜๋Š”์ง€ ์ง์ ‘ ๋ช…์‹œํ•  ์ˆ˜ ์žˆ์–ด์„œ ์–ด๋–ค ๋ฐฉ๋ฒ•์œผ๋กœ ์ด๋ฅผ ์–ป์„์ง€๋Š” ์‹ ๊ฒฝ ์“ธ ํ•„์š”๊ฐ€ ์—†๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” groupingBy๋ฅผ ์ด์šฉํ•ด์„œ โ€˜๊ฐ ํ‚ค(ํ†ตํ™”) ๋ฒ„ํ‚ท ๊ทธ๋ฆฌ๊ณ  ๊ฐ ํ‚ค ๋ฒ„ํ‚ท์— ๋Œ€์‘ํ•˜๋Š” ์š”์†Œ ๋ฆฌ์ŠคํŠธ๋ฅผ ๊ฐ’์œผ๋กœ ํฌํ•จํ•˜๋Š” ๋งต(Map)์„ ๋งŒ๋“ค๋ผโ€™๋Š” ๋™์ž‘์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.

๋ช…๋ นํ˜• ์ฝ”๋“œ์—์„œ๋Š” ๋‹ค์ค‘ ๋ฃจํ”„์™€ ์กฐ๊ฑด๋ฌธ์„ ์ถ”๊ฐ€ํ•˜๋ฉฐ ๊ฐ€๋…์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ์ด ํฌ๊ฒŒ ๋–จ์–ด์ง„๋‹ค.

6.1.1 ๊ณ ๊ธ‰ ๋ฆฌ๋“€์‹ฑ ๊ธฐ๋Šฅ์„ ํ•˜๋Š” ์ปฌ๋ ‰ํ„ฐ

ํ•จ์ˆ˜ํ˜• API์˜ ์žฅ์  โ†’ ๋†’์€ ์ˆ˜์ค€์˜ ์กฐํ•ฉ์„ฑ๊ณผ ์žฌ์‚ฌ์šฉ์„ฑ

์ปฌ๋ ‰ํ„ฐ์˜ ์ตœ๋Œ€ ๊ฐ•์ ์€ collect๋กœ ๊ฒฐ๊ณผ๋ฅผ ์ˆ˜์ง‘ํ•˜๋Š” ๊ณผ์ •์„ ๊ฐ„๋‹จํ•˜๋ฉด์„œ๋„ ์œ ์—ฐํ•œ ๋ฐฉ์‹์œผ๋กœ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ด๋‹ค.

  • ์ŠคํŠธ๋ฆผ์— collect๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ์— ๋‚ด๋ถ€์ ์œผ๋กœ ๋ฆฌ๋“€์‹ฑ ์—ฐ์‚ฐ์ด ์ˆ˜ํ–‰๋จ
  • ๋ช…๋ นํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ๋Š” ์šฐ๋ฆฌ๊ฐ€ ์ง์ ‘ ๊ตฌํ˜„ํ•ด์•ผ ํ–ˆ๋˜ ์ž‘์—…์ด ์ž๋™์œผ๋กœ ์ˆ˜ํ–‰๋จ
  • collect์—์„œ๋Š” ๋ฆฌ๋“€์‹ฑ ์—ฐ์‚ฐ์„ ์ด์šฉํ•ด์„œ ์ŠคํŠธ๋ฆผ์˜ ๊ฐ ์š”์†Œ๋ฅผ ๋ฐฉ๋ฌธํ•˜๋ฉด์„œ ์ปฌ๋ ‰ํ„ฐ๊ฐ€ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•œ๋‹ค.

Collectors ์œ ํ‹ธ๋ฆฌํ‹ฐ ํด๋ž˜์Šค๋Š” ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ์ปฌ๋ ‰ํ„ฐ ์ธ์Šคํ„ด์Šค๋ฅผ ์†์‰ฝ๊ฒŒ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋Š” ์ •์  ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ๋ฅผ ์ œ๊ณตํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” ์ง๊ด€์ ์ธ ๋ฉ”์„œ๋“œ๋กœ toList๋ฅผ ๊ผฝ์„ ์ˆ˜ ์žˆ๋‹ค. toList๋Š” ์ŠคํŠธ๋ฆผ์˜ ๋ชจ๋“  ์š”์†Œ๋ฅผ ๋ฆฌ์ŠคํŠธ๋กœ ์ˆ˜์ง‘ํ•œ๋‹ค.

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

6.1.2 ๋ฏธ๋ฆฌ ์ •์˜๋œ ์ปฌ๋ ‰ํ„ฐ

groupingBy ๊ฐ™์ด Collectors ํด๋ž˜์Šค์—์„œ ์ œ๊ณตํ•˜๋Š” ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ์˜ ๊ธฐ๋Šฅ์„ ์„ค๋ช…ํ•œ๋‹ค.

  • Collectors์—์„œ ์ œ๊ณตํ•˜๋Š” ๋ฉ”์„œ๋“œ์˜ ๊ธฐ๋Šฅ
    • ์ŠคํŠธ๋ฆผ ์š”์†Œ๋ฅผ ํ•˜๋‚˜์˜ ๊ฐ’์œผ๋กœ ๋ฆฌ๋“€์Šคํ•˜๊ณ  ์š”์•ฝ

      : ์ด์ „ ์˜ˆ์ œ์ฒ˜๋Ÿผ ํŠธ๋žœ์žญ์…˜ ๋ฆฌ์ŠคํŠธ์—์„œ ํŠธ๋žœ์žญ์…˜ ์ดํ•ฉ์„ ์ฐพ๋Š” ๋“ฑ์˜ ๋‹ค์–‘ํ•œ ๊ณ„์‚ฐ์„ ์ˆ˜ํ–‰ํ•  ๋•Œ ์œ ์šฉ

    • ์š”์†Œ ๊ทธ๋ฃนํ™”

      : ์ด์ „ ์˜ˆ์ œ๋ฅผ ๋‹ค์ˆ˜์ค€์œผ๋กœ ๊ทธ๋ฃนํ™”ํ•˜๊ฑฐ๋‚˜ ๊ฐ๊ฐ์˜ ๊ฒฐ๊ณผ ์„œ๋ธŒ๊ทธ๋ฃน์— ์ถ”๊ฐ€๋กœ ๋ฆฌ๋“€์‹ฑ ์—ฐ์‚ฐ์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Œ (๋‹ค์–‘ํ•œ ์ปฌ๋ ‰ํ„ฐ ์กฐํ•ฉ์„ ํ†ตํ•ด)

    • ์š”์†Œ ๋ถ„ํ• 

      : ํ•œ ๊ฐœ์˜ ์ธ์ˆ˜๋ฅผ ๋ฐ›์•„ boolean์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜, ์ฆ‰ ํ”„๋ ˆ๋””์ผ€์ดํŠธ๋ฅผ ๊ทธ๋ฃนํ™” ํ•จ์ˆ˜๋กœ ์‚ฌ์šฉ

6.2 ๋ฆฌ๋“€์‹ฑ๊ณผ ์š”์•ฝ

counting()

counting() ์ด๋ผ๋Š” ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ปฌ๋ ‰ํ„ฐ๋กœ ๋ฉ”๋‰ด์—์„œ ์š”๋ฆฌ ์ˆ˜๋ฅผ ๊ณ„์‚ฐ

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

๋‹ค์Œ์ฒ˜๋Ÿผ ๋ถˆํ•„์š”ํ•œ ๊ณผ์ •์„ ์ƒ๋žตํ•  ์ˆ˜ ์žˆ๋‹ค.

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

counting ์ปฌ๋ ‰ํ„ฐ๋Š” ๋‹ค๋ฅธ ์ปฌ๋ ‰ํ„ฐ์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ๋•Œ ์œ„๋ ฅ์„ ๋ฐœํœ˜ํ•œ๋‹ค.

Collectors ํด๋ž˜์Šค์˜ ์ •์  ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ๋ฅผ ๋ชจ๋‘ ์ž„ํฌํŠธ

1
import static java.util.stream.Collectors.*;

Collectors.counting()์„ ๊ฐ„๋‹จํ•˜๊ฒŒ counting()์œผ๋กœ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

6.2.1 ์ŠคํŠธ๋ฆผ๊ฐ’์—์„œ ์ตœ๋Œ“๊ฐ’๊ณผ ์ตœ์†Ÿ๊ฐ’ ๊ฒ€์ƒ‰

๋ฉ”๋‰ด์—์„œ ๊ฐ€์žฅ ์นผ๋กœ๋ฆฌ๊ฐ€ ๋†’์€ ์š”๋ฆฌ๋ฅผ ์ฐพ๋Š”๋‹ค๊ณ  ๊ฐ€์ •ํ•˜์ž.

Collectors.maxBy, Collectors.minBy ๋‘ ๊ฐœ์˜ ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•ด์„œ ์ŠคํŠธ๋ฆผ์˜ ์ตœ๋Œ“๊ฐ’๊ณผ ์ตœ์†Ÿ๊ฐ’์„ ๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ๋‹ค. ๋‘ ์ปฌ๋ ‰ํ„ฐ๋Š” ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๋ฅผ ๋น„๊ตํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  Comparator๋ฅผ ์ธ์ˆ˜๋กœ ๋ฐ›๋Š”๋‹ค.

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

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

Opitonal๋Š” ๊ฐ’์„ ํฌํ•จํ•˜๊ฑฐ๋‚˜ ๋˜๋Š” ํฌํ•จํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋Š” ์ปจํ…Œ์ด๋„ˆ์ด๋‹ค.(์ž๋ฐ” 8์ดํ›„)

๋˜ํ•œ ์ŠคํŠธ๋ฆผ์— ์žˆ๋Š” ๊ฐ์ฒด์˜ ์ˆซ์ž ํ•„๋“œ์˜ ํ•ฉ๊ณ„๋‚˜ ํ‰๊ท  ๋“ฑ์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ์—ฐ์‚ฐ์—๋„ ๋ฆฌ๋“€์‹ฑ ๊ธฐ๋Šฅ์ด ์ž์ฃผ ์‚ฌ์šฉ๋œ๋‹ค. ์ด๋Ÿฌํ•œ ์—ฐ์‚ฐ์„ ์š”์•ฝ(summarization) ์—ฐ์‚ฐ์ด๋ผ ๋ถ€๋ฅธ๋‹ค.

6.2.2 ์š”์•ฝ ์—ฐ์‚ฐ

summingInt(), summarizingInt()

Collectors ํด๋ž˜์Šค๋Š” Collectors.summingInt๋ผ๋Š” ํŠน๋ณ„ํ•œ ์š”์•ฝ ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

  • ๊ฐ์ฒด๋ฅผ int๋กœ ๋งคํ•‘ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์ธ์ˆ˜๋กœ ๋ฐ›๋Š”๋‹ค.
  • summingInt์˜ ์ธ์ˆ˜๋กœ ์ „๋‹ฌ๋œ ํ•จ์ˆ˜๋Š” ๊ฐ์ฒด๋ฅผ int๋กœ ๋งคํ•‘ํ•œ ์ปฌ๋ ‰ํ„ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  • summingInt๊ฐ€ collect ๋ฉ”์„œ๋“œ๋กœ ์ „๋‹ฌ๋˜๋ฉด ์š”์•ฝ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.
1
int totalCalories = menu.stream().collect(summingInt(Dish::getCalories));

์นผ๋กœ๋ฆฌ๋กœ ๋งคํ•‘๋œ ๊ฐ ์š”๋ฆฌ์˜ ๊ฐ’์„ ํƒ์ƒ‰ํ•˜๋ฉด์„œ ์ดˆ๊นƒ๊ฐ’(์—ฌ๊ธฐ์„œ๋Š” 0)์œผ๋กœ ์„ค์ •๋˜์–ด ์žˆ๋Š” ๋ˆ„์ ์ž์— ์นผ๋กœ๋ฆฌ๋ฅผ ๋”ํ•œ๋‹ค.

Collectors.summingLong๊ณผ Collectors.summingDouble ๋ฉ”์„œ๋“œ๋Š” ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ๋™์ž‘ํ•˜๋ฉฐ ๊ฐ๊ฐ long ๋˜๋Š” double ํ˜•์‹์˜ ๋ฐ์ดํ„ฐ๋กœ ์š”์•ฝํ•œ๋‹ค๋Š” ์ ๋งŒ ๋‹ค๋ฅด๋‹ค.

์ด์™ธ์˜ ํ‰๊ท ๊ฐ’ ๊ณ„์‚ฐ ๋“ฑ์˜ ์—ฐ์‚ฐ๋„ ์š”์•ฝ ๊ธฐ๋Šฅ์œผ๋กœ ์ œ๊ณต๋œ๋‹ค. ์ฆ‰, Collectors.averagingInt, averagingLong, averagingDouble ๋“ฑ์œผ๋กœ ๋‹ค์–‘ํ•œ ํ˜•์‹์œผ๋กœ ์ด๋ฃจ์–ด์ง„ ์ˆซ์ž ์ง‘ํ•ฉ์˜ ํ‰๊ท ์„ ๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ๋‹ค.

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

์ปฌ๋ ‰ํ„ฐ๋กœ ์ข…์ข… ๋‘ ๊ฐœ ์ด์ƒ์˜ ์—ฐ์‚ฐ์„ ํ•œ ๋ฒˆ์— ์ˆ˜ํ–‰ํ•ด์•ผ ํ•  ๋•Œ๋„ ์žˆ๋‹ค. ์ด๋Ÿฐ ์ƒํ™ฉ์—์„œ๋Š” ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ summarizingInt๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ปฌ๋ ‰ํ„ฐ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๋‹ค์Œ์€ ํ•˜๋‚˜์˜ ์š”์•ฝ ์—ฐ์‚ฐ์œผ๋กœ ๋ฉ”๋‰ด์— ์žˆ๋Š” ์š”๋ฆฌ ์ˆ˜, ์š”๋ฆฌ์˜ ์นผ๋กœ๋ฆฌ ํ•ฉ๊ณ„, ํ‰๊ท , ์ตœ๋Œ“๊ฐ’, ์ตœ์†Ÿ๊ฐ’ ๋“ฑ์„ ๊ณ„์‚ฐํ•˜๋Š” ์ฝ”๋“œ๋‹ค.

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

์œ„ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด IntSummaryStatistics ํด๋ž˜์Šค๋กœ ๋ชจ๋“  ์ •๋ณด๊ฐ€ ์ˆ˜์ง‘๋œ๋‹ค. menuStatistics ๊ฐ์ฒด๋ฅผ ์ถœ๋ ฅํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ •๋ณด๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

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

๋งˆ์ฐฌ๊ฐ€์ง€๋กœ int๋ฟ๋งŒ ์•„๋‹ˆ๋ผ long์ด๋‚˜ double์— ๋Œ€์‘ํ•˜๋Š” summarizingLong, summarizingDouble ๋ฉ”์„œ๋“œ์™€ ๊ด€๋ จ๋œ LongSummaryStatistics, DoubleSummaryStatistics ํด๋ž˜์Šค๋„ ์žˆ๋‹ค.

6.2.3 ๋ฌธ์ž์—ด ์—ฐ๊ฒฐ

joinning()

์ปฌ๋ ‰ํ„ฐ์— joining ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•˜๋ฉด ์ŠคํŠธ๋ฆผ์˜ ๊ฐ ๊ฐ์ฒด์— toString ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด์„œ ์ถ”์ถœํ•œ ๋ฌธ์ž์—ด์„ ํ•˜๋‚˜์˜ ๋ฌธ์ž์—ด๋กœ ์—ฐ๊ฒฐํ•ด์„œ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

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

joinning ๋ฉ”์„œ๋“œ๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ StringBuilder๋ฅผ ์ด์šฉํ•ด์„œ ๋ฌธ์ž์—ด์„ ํ•˜๋‚˜๋กœ ๋งŒ๋“ ๋‹ค. Dish ํด๋ž˜์Šค๊ฐ€ ์š”๋ฆฌ๋ช…์„ ๋ฐ˜ํ™˜ํ•˜๋Š” toString ๋ฉ”์„œ๋“œ๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ๋‹ค๋ฉด ๋‹ค์Œ ์ฝ”๋“œ์ฒ˜๋Ÿผ map์œผ๋กœ ๊ฐ ์š”๋ฆฌ์˜ ์ด๋ฆ„์„ ์ถ”์ถœํ•˜๋Š” ๊ณผ์ •์„ ์ƒ๋žตํ•  ์ˆ˜ ์žˆ๋‹ค.

1
String shortMenu = menu.stream().collect(joinning());

๊ฒฐ๊ณผ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

1
porkbeefchickenfrench friesriceseason fruitpizzaprawnssalmon

ํ•˜์ง€๋งŒ ๊ฒฐ๊ณผ ๋ฌธ์ž์—ด์„ ํ•ด์„ํ•  ์ˆ˜๊ฐ€ ์—†๋‹ค. ๋‘ ์š”์†Œ ์‚ฌ์ด์— ๊ตฌ๋ถ„ ๋ฌธ์ž์—ด์„ ๋„ฃ์„ ์ˆ˜ ์žˆ๋„๋ก ์˜ค๋ฒ„๋กœ๋“œ๋œ joinning ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

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

๋‹ค์Œ์€ ์œ„ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•œ ๊ฒฐ๊ณผ์ด๋‹ค.

1
pork, beef, chicken, french fries, rice, season fruit, pizza, prawns, salmon

์ง€๊ธˆ๊นŒ์ง€ ์ŠคํŠธ๋ฆผ์„ ํ•˜๋‚˜์˜ ๊ฐ’์œผ๋กœ ๋ฆฌ๋“€์Šค ํ•˜๋Š” ๋‹ค์–‘ํ•œ ์ปฌ๋ ‰ํ„ฐ๋ฅผ ํ™•์ธํ•ด๋ณด์•˜๋‹ค.

6.2.4 ๋ฒ”์šฉ ๋ฆฌ๋“€์‹ฑ ์š”์•ฝ ์—ฐ์‚ฐ

์ง€๊ธˆ๊นŒ์ง€ ์‚ดํŽด๋ณธ ๋ชจ๋“  ์ปฌ๋ ‰ํ„ฐ๋Š” reducing ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ๋กœ๋„ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ฆ‰, ๋ฒ”์šฉ Collectors.reducing์œผ๋กœ๋„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๋‹ค์Œ ์ฝ”๋“œ์ฒ˜๋Ÿผ reducing ๋ฉ”์„œ๋“œ๋กœ ๋งŒ๋“ค์–ด์ง„ ์ปฌ๋ ‰ํ„ฐ๋กœ๋„ ๋ฉ”๋‰ด์˜ ๋ชจ๋“  ์นผ๋กœ๋ฆฌ ํ•ฉ๊ณ„๋ฅผ ๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ๋‹ค.

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

reducing์€ ์ธ์ˆ˜ ์„ธ ๊ฐœ๋ฅผ ๋ฐ›๋Š”๋‹ค.

  • ์ฒซ ๋ฒˆ์งธ ์ธ์ˆ˜ : ๋ฆฌ๋“€์‹ฑ ์—ฐ์‚ฐ์˜ ์‹œ์ž‘๊ฐ’์ด๊ฑฐ๋‚˜ ์ŠคํŠธ๋ฆผ์— ์ธ์ˆ˜๊ฐ€ ์—†์„ ๋•Œ๋Š” ๋ฐ˜ํ™˜๊ฐ’ (์ˆซ์ž ํ•ฉ๊ณ„์—์„œ๋Š” ์ธ์ˆ˜๊ฐ€ ์—†์„ ๋•Œ ๋ฐ˜ํ™˜๊ฐ’์œผ๋กœ 0์ด ์ ํ•ฉํ•˜๋‹ค.)
  • ๋‘ ๋ฒˆ์งธ ์ธ์ˆ˜ : ์š”๋ฆฌ๋ฅผ ์นผ๋กœ๋ฆฌ ์ •์ˆ˜๋กœ ๋ณ€ํ™˜ํ•  ๋•Œ ์‚ฌ์šฉํ•œ ๋ณ€ํ™˜ ํ•จ์ˆ˜
  • ์„ธ ๋ฒˆ์งธ ์ธ์ˆ˜ : ๊ฐ™์€ ์ข…๋ฅ˜์˜ ๋‘ ํ•ญ๋ชฉ์„ ํ•˜๋‚˜์˜ ๊ฐ’์œผ๋กœ ๋”ํ•˜๋Š” BinaryOperator (์˜ˆ์ œ์—์„œ๋Š” ๋‘ ๊ฐœ์˜ int๊ฐ€ ์‚ฌ์šฉ๋˜์—ˆ๋‹ค.)

๋‹ค์Œ์ฒ˜๋Ÿผ ํ•œ ๊ฐœ์˜ ์ธ์ˆ˜๋ฅผ ๊ฐ€์ง„ reducing ๋ฒ„์ „์„ ์ด์šฉํ•ด์„œ ๊ฐ€์žฅ ์นผ๋กœ๋ฆฌ๊ฐ€ ๋†’์€ ์š”๋ฆฌ๋ฅผ ์ฐพ๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ๋‹ค.

1
2
3
Optional<Dish> mostCalorieDish =
    menu.stream().collect(reducing(
        (d1, d2) -> d1.getCalories() > d2.getCalories() ? d1 : d2));

ํ•œ ๊ฐœ์˜ ์ธ์ˆ˜๋ฅผ ๊ฐ–๋Š” reducing ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ๋Š” ์„ธ ๊ฐœ์˜ ์ธ์ˆ˜๋ฅผ ๊ฐ–๋Š” reducing ๋ฉ”์„œ๋“œ์—์„œ ์ŠคํŠธ๋ฆผ์˜ ์ฒซ ๋ฒˆ์งธ ์š”์†Œ๋ฅผ ์‹œ์ž‘ ์š”์†Œ, ์ฆ‰ ์ฒซ ๋ฒˆ์งธ ์ธ์ˆ˜๋กœ ๋ฐ›์œผ๋ฉฐ, ์ž์‹ ์„ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•ญ๋“ฑ ํ•จ์ˆ˜๋ฅผ ๋‘ ๋ฒˆ์งธ ์ธ์ˆ˜๋กœ ๋ฐ›๋Š” ์ƒํ™ฉ์— ํ•ด๋‹นํ•œ๋‹ค. ์ฆ‰, ํ•œ ๊ฐœ์˜ ์ธ์ˆ˜๋ฅผ ๊ฐ–๋Š” reducing ์ปฌ๋ ‰ํ„ฐ๋Š” ์‹œ์ž‘๊ฐ’์ด ์—†์œผ๋ฏ€๋กœ ๋นˆ ์ŠคํŠธ๋ฆผ์ด ๋„˜๊ฒจ์กŒ์„ ๋•Œ ์‹œ์ž‘๊ฐ’์ด ์„ค์ •๋˜์ง€ ์•Š๋Š” ์ƒํ™ฉ์ด ๋ฒŒ์–ด์ง„๋‹ค. ๊ทธ๋ž˜์„œ **ํ•œ ๊ฐœ์˜ ์ธ์ˆ˜๋ฅผ ๊ฐ–๋Š” reducing์€ Optional ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜**ํ•œ๋‹ค.

collect์™€ reduce

Stream ์ธํ„ฐํŽ˜์ด์Šค์˜ collect์™€ reduce์˜ ์ฐจ์ด์ ์€ ๋ฌด์—‡์ผ๊นŒ? ๋‘˜์€ ๊ฐ™์€ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋‹ค์Œ ์ฝ”๋“œ์—์„œ ๋ณด์—ฌ์ฃผ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ toList ์ปฌ๋ ‰ํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” collect ๋Œ€์‹  reduce ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
4
5
6
Stream<Integer> stream = Arrays.asList(1, 2, 3, 4, 5, 6).stream();
List<Integer> numbers 
    = stream.reduce(
        new ArrayList<Integer>(), 
        (List<Integer> l, Integer e) -> {l.add(e); return l;},
        (List<Integer> l1, List<Integer> l2) -> {l1.addAll(l2); return l1;});

์œ„ ์ฝ”๋“œ์—๋Š” ์˜๋ฏธ๋ก ์ ์ธ ๋ฌธ์ œ์™€ ์‹ค์šฉ์„ฑ ๋ฌธ์ œ ๋“ฑ ๋‘ ๊ฐ€์ง€ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

  • ์˜๋ฏธ๋ก ์  ๋ฌธ์ œ

    collect ๋ฉ”์„œ๋“œ๋Š” ๋„์ถœํ•˜๋ ค๋Š” ๊ฒฐ๊ณผ๋ฅผ ๋ˆ„์ ํ•˜๋Š” ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๋ฐ”๊พธ๋„๋ก ์„ค๊ณ„๋œ ๋ฉ”์„œ๋“œ์ธ ๋ฐ˜๋ฉด reduce๋Š” ๋‘ ๊ฐ’์„ ํ•˜๋‚˜๋กœ ๋„์ถœํ•˜๋Š” ๋ถˆ๋ณ€ํ˜• ์—ฐ์‚ฐ์ด๋ผ๋Š” ์ 

    ์˜ˆ์ œ์—์„œ reduce ๋ฉ”์„œ๋“œ๋Š” ๋ˆ„์ ์ž๋กœ ์‚ฌ์šฉ๋œ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ณ€ํ™˜์‹œํ‚ค๋ฏ€๋กœ reduce๋ฅผ ์ž˜๋ชป ํ™œ์šฉํ•œ ์˜ˆ์— ํ•ด๋‹นํ•œ๋‹ค.

  • ์‹ค์šฉ์„ฑ ๋ฌธ์ œ

    ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ๊ฐ™์€ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ์ฒด๋ฅผ ๊ณ ์น˜๋ฉด ๋ฆฌ์ŠคํŠธ ์ž์ฒด๊ฐ€ ๋ง๊ฐ€์ ธ๋ฒ„๋ฆฌ๋ฏ€๋กœ ๋ฆฌ๋“€์‹ฑ ์—ฐ์‚ฐ์„ ๋ณ‘๋ ฌ๋กœ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์—†๋‹ค๋Š” ์ 

    ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋งค๋ฒˆ ์ƒˆ๋กœ์šด ๋ฆฌ์ŠคํŠธ๋ฅผ ํ• ๋‹นํ•ด์•ผ ํ•œ๋‹ค. โ†’ ๊ฐ์ฒด ํ• ๋‹น ๋•Œ๋ฌธ์— ์„ฑ๋Šฅ ์ €ํ•˜

    ๊ฐ€๋ณ€ ์ปจํ…Œ์ด๋„ˆ ๊ด€๋ จ ์ž‘์—…์ด๋ฉด์„œ ๋ณ‘๋ ฌ์„ฑ์„ ํ™•๋ณดํ•˜๋ ค๋ฉด collect ๋ฉ”์„œ๋“œ๋กœ ๋ฆฌ๋“€์‹ฑ ์—ฐ์‚ฐ์„ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์ด ๋ฐ”๋žŒ์งํ•˜๋‹ค

์ปฌ๋ ‰์…˜ ํ”„๋ ˆ์ž„์›Œํฌ ์œ ์—ฐ์„ฑ : ๊ฐ™์€ ์—ฐ์‚ฐ๋„ ๋‹ค์–‘ํ•œ ๋ฐฉ์‹์œผ๋กœ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

reducing ์ปฌ๋ ‰ํ„ฐ๋ฅผ ์‚ฌ์šฉํ•œ ์ด์ „ ์˜ˆ์ œ์—์„œ ๋žŒ๋‹ค ํ‘œํ˜„์‹ ๋Œ€์‹  Integer ํด๋ž˜์Šค์˜ sum ๋ฉ”์„œ๋“œ ์ฐธ์กฐ๋ฅผ ์ด์šฉํ•˜๋ฉด ์ฝ”๋“œ๋ฅผ ์ข€ ๋” ๋‹จ์ˆœํ™” ํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
4
5
6
7
8
int totalCalories = 
    menu.stream().collect(reducing(0, Dish::getCalories, (i, j) -> i + j));

// sum ๋ฉ”์„œ๋“œ ์ฐธ์กฐ
int totalCalories = 
    menu.stream().collect(reducing(0, // ์ดˆ๊นƒ๊ฐ’
                            Dish::getCalories, // ๋ณ€ํ™˜ ํ•จ์ˆ˜
                            Integer::sum));    // ํ•ฉ๊ณ„ ํ•จ์ˆ˜

๋ฆฌ๋“€์‹ฑ ์—ฐ์‚ฐ๊ณผ์ •์€ ๋ˆ„์ ์ž๋ฅผ ์ดˆ๊นƒ๊ฐ’์œผ๋กœ ์ดˆ๊ธฐํ™”ํ•˜๊ณ , ํ•ฉ๊ณ„ ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด์„œ ๊ฐ ์š”์†Œ์— ๋ณ€ํ™˜ ํ•จ์ˆ˜๋ฅผ ์ ์šฉํ•œ ๊ฒฐ๊ณผ ์ˆซ์ž๋ฅผ ๋ฐ˜๋ณต์ ์œผ๋กœ ์กฐํ•ฉํ•œ๋‹ค.

counting ์ปฌ๋ ‰ํ„ฐ๋„ ์„ธ ๊ฐœ์˜ ์ธ์ˆ˜๋ฅผ ๊ฐ–๋Š” reducing ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•ด์„œ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋‹ค์Œ ์ฝ”๋“œ์ฒ˜๋Ÿผ ์ŠคํŠธ๋ฆผ์˜ Long ๊ฐ์ฒด ํ˜•์‹์˜ ์š”์†Œ๋ฅผ 1๋กœ ๋ณ€ํ™˜ํ•œ ๋‹ค์Œ์— ๋ชจ๋‘ ๋”ํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
public static <T> Collector<T, ?, Long> counting() {
    return reducing(0L, e -> 1L, Long::sum);
}

์ œ๋„ค๋ฆญ ์™€์ผ๋“œ์นด๋“œ โ€˜?โ€™ ์‚ฌ์šฉ๋ฒ•

์œ„ ์˜ˆ์ œ์—์„œ counting ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ปฌ๋ ‰ํ„ฐ ์‹œ๊ทธ๋‹ˆ์ฒ˜์˜ ๋‘ ๋ฒˆ์งธ ์ œ๋„ค๋ฆญ ํ˜•์‹์œผ๋กœ ์™€์ผ๋“œ์นด๋“œ ?์ด ์‚ฌ์šฉ๋˜์—ˆ๋‹ค. ?๋Š” ์ปฌ๋ ‰ํ„ฐ์˜ ๋ˆ„์ ์ž ํ˜•์‹์ด ์•Œ๋ ค์ง€์ง€ ์•Š์•˜์Œ(๋ˆ„์ ์ž์˜ ํ˜•์‹์ด ์ž์œ ๋กœ์›€)์„ ์˜๋ฏธํ•œ๋‹ค.

1
2
int totalCalories =
    menu.stream().map(Dish::getCalories).reduce(Integer::sum).get

์ปฌ๋ ‰ํ„ฐ๋ฅผ ์ด์šฉํ•˜์ง€ ์•Š๊ณ  ๊ฐ™์€ ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•œ ๊ฒƒ์ด๋‹ค.

ํ•œ ๊ฐœ์˜ ์ธ์ˆ˜๋ฅผ ๊ฐ–๋Š” reduce๋ฅผ ์ŠคํŠธ๋ฆผ์— ์ ์šฉํ•œ ๋‹ค๋ฅธ ์˜ˆ์ œ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ reduce(Integer::sum)๋„ ๋นˆ ์ŠคํŠธ๋ฆผ๊ณผ ๊ด€๋ จํ•œ ๋„ ๋ฌธ์ œ๋ฅผ ํ”ผํ•  ์ˆ˜ ์žˆ๋„๋ก int๊ฐ€ ์•„๋‹Œ Optional<Integer>๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  get์œผ๋กœ Optional ๊ฐ์ฒด์˜ ๊ฐ’์„ ์ถ”์ถœํ–ˆ๋‹ค. (์š”๋ฆฌ ์ŠคํŠธ๋ฆผ์ด ๋น„์–ด์žˆ์ง€ ์•Š๋‹ค๋Š” ์‚ฌ์‹ค์„ ์•Œ๊ณ  ์žˆ์œผ๋ฏ€๋กœ)

์ผ๋ฐ˜์ ์œผ๋กœ๋Š” ๊ธฐ๋ณธ๊ฐ’์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๋Š” orElse, orElseGet ๋“ฑ์„ ์ด์šฉํ•ด์„œ Optional์˜ ๊ฐ’์„ ์–ป์–ด์˜ค๋Š” ๊ฒƒ์ด ์ข‹๋‹ค. ๋งˆ์ง€๋ง‰์œผ๋กœ ์ŠคํŠธ๋ฆ ์„ IntStream์œผ๋กœ ๋งคํ•‘ํ•œ ๋‹ค์Œ์— sum ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ๋„ ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.

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

์ž์‹ ์˜ ์ƒํ™ฉ์— ๋งž๋Š” ์ตœ์ ์˜ ํ•ด๋ฒ• ์„ ํƒ

์ง€๊ธˆ๊นŒ์ง€ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ๋Š” ํ•˜๋‚˜์˜ ์—ฐ์‚ฐ์„ ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Œ์„ ๋ณด์—ฌ์ค€๋‹ค. ๋˜ํ•œ ์ŠคํŠธ๋ฆผ ์ธํ„ฐํŽ˜์ด์Šค์—์„œ ์ง์ ‘ ์ œ๊ณตํ•˜๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•˜๋Š” ๊ฒƒ์— ๋น„ํ•ด ์ปฌ๋ ‰ํ„ฐ๋ฅผ ์ด์šฉํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ๋” ๋ณต์žกํ•˜๋‹ค๋Š” ์‚ฌ์‹ค๋„ ๋ณด์—ฌ์ค€๋‹ค. ์ฝ”๋“œ๊ฐ€ ์ข€ ๋” ๋ณต์žกํ•œ ๋Œ€์‹  ์žฌ์‚ฌ์šฉ์„ฑ๊ณผ ์ปค์Šคํ„ฐ๋งˆ์ด์ฆˆ ๊ฐ€๋Šฅ์„ฑ์„ ์ œ๊ณตํ•˜๋Š” ๋†’์€ ์ˆ˜์ค€์˜ ์ถ”์ƒํ™”์™€ ์ผ๋ฐ˜ํ™”๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.

๊ฐ€๋…์„ฑ๊ณผ ์„ฑ๋Šฅ ๋‘ ๋งˆ๋ฆฌ ํ† ๋ผ๋ฅผ ์žก๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋‹ค์–‘ํ•œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์„ ํ™•์ธํ•œ ํ›„ ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์œผ๋กœ ๋ฌธ์ œ์— ํŠนํ™”๋œ ํ•ด๊ฒฐ์ฑ…์„ ๊ณ ๋ฅด๋Š” ๊ฒƒ์ด ๋ฐ”๋žŒ์งํ•˜๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด ๋ฉ”๋‰ด ์ „์ฒด์˜ ์นผ๋กœ๋ฆฌ๋ฅผ ๊ณ„์‚ฐํ•˜๋Š” ์˜ˆ์ œ์—์„œ๋Š” (IntStream์„ ์‚ฌ์šฉํ•œ)๊ฐ€์žฅ ๋งˆ์ง€๋ง‰์— ํ™•์ธํ•œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์ด ๊ฐ€์žฅ ๊ฐ€๋…์„ฑ์ด ์ข‹๊ณ  ๊ฐ„๊ฒฐํ•˜๋‹ค. ๋˜ํ•œ IntStream ๋•๋ถ„์— ์ž๋™ ์–ธ๋ฐ•์‹ฑ ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•˜๊ฑฐ๋‚˜ Integer๋ฅผ int๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ณผ์ •์„ ํ”ผํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์„ฑ๋Šฅ๊นŒ์ง€ ์ข‹๋‹ค.

  • Quiz. ๋ฆฌ๋“€์‹ฑ์œผ๋กœ ๋ฌธ์ž์—ด ์—ฐ๊ฒฐํ•˜๊ธฐ

    ์•„๋ž˜ joining ์ปฌ๋ ‰ํ„ฐ๋ฅผ 6.2.3์ ˆ์—์„œ ์‚ฌ์šฉํ•œ reducing ์ปฌ๋ ‰ํ„ฐ๋กœ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋ฐ”๊พผ ์ฝ”๋“œ๋ฅผ ๋ชจ๋‘ ์„ ํƒํ•˜์‹œ์˜ค.

    1
    2
    3
    4
    5
    6
    7
    8
    
      String shortMenu = menu.stream().map(Dish::getName).collect(joining());
        
      1. String shortMenu = menu.stream().map(Dish::getName)
              .collect(reducing((s1, s2) -> s1 + s2)).get();
      2. String shortMenu = menu.stream()
              .collect(reducing((d1, d2) -> d1.getName() + d2.getName())).get();
      3. String shortMenu = menu.stream()
              .collect(reducing("", Dish::getName, (s1, s2) -> s1 + s2));
    
    • ์ •๋‹ต

      1๋ฒˆ๊ณผ 3๋ฒˆ์ด ์ •๋‹ต์ด๋ฉฐ, 2๋ฒˆ์€ ์ปดํŒŒ์ผ๋˜์ง€ ์•Š๋Š” ์ฝ”๋“œ๋‹ค.

      1. ์›๋ž˜์˜ joining ์ปฌ๋ ‰ํ„ฐ์ฒ˜๋Ÿผ ๊ฐ ์š”๋ฆฌ๋ฅผ ์š”๋ฆฌ๋ช…์œผ๋กœ ๋ณ€ํ™˜ํ•œ ๋‹ค์Œ์— ๋ฌธ์ž์—ด์„ ๋ˆ„์ ์ž๋กœ ์‚ฌ์šฉํ•ด์„œ ๋ฌธ์ž์—ด ์ŠคํŠธ๋ฆผ์„ ๋ฆฌ๋“€์Šคํ•˜๋ฉด์„œ ์š”๋ฆฌ๋ช…์„ ํ•˜๋‚˜์”ฉ ์—ฐ๊ฒฐํ•œ๋‹ค.
      2. reducing์€ BinaryOperator, ์ฆ‰ BiFunction<T, T, T>๋ฅผ ์ธ์ˆ˜๋กœ ๋ฐ›๋Š”๋‹ค. ์ฆ‰, reducing์€ ๋‘ ์ธ์ˆ˜๋ฅผ ๋ฐ›์•„ **๊ฐ™์€ ํ˜•์‹์„ ๋ฐ˜ํ™˜**ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์ธ์ˆ˜๋กœ ๋ฐ›๋Š”๋‹ค. ํ•˜์ง€๋งŒ 2๋ฒˆ ๋žŒ๋‹ค ํ‘œํ˜„์‹์€ ๋‘ ๊ฐœ์˜ ์š”๋ฆฌ๋ฅผ ์ธ์ˆ˜๋กœ ๋ฐ›์•„ ๋ฌธ์ž์—ด์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
      3. ๋นˆ ๋ฌธ์ž์—ด์„ ํฌํ•จํ•˜๋Š” ๋ˆ„์ ์ž๋ฅผ ์ด์šฉํ•ด์„œ ๋ฆฌ๋“€์‹ฑ ๊ณผ์ •์„ ์‹œ์ž‘ํ•˜๋ฉฐ, ์ŠคํŠธ๋ฆผ์˜ ์š”๋ฆฌ๋ฅผ ๋ฐฉ๋ฌธํ•˜๋ฉด์„œ ๊ฐ ์š”๋ฆฌ๋ฅผ ์š”๋ฆฌ๋ช…์œผ๋กœ ๋ณ€ํ™˜ํ•œ ๋‹ค์Œ์— ๋ˆ„์ ์ž๋กœ ์ถ”๊ฐ€ํ•œ๋‹ค. ์„ธ ๊ฐœ์˜ ์ธ์ˆ˜๋ฅผ ๊ฐ–๋Š” reducing์€ ๋ˆ„์ ์ž ์ดˆ๊นƒ๊ฐ’์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ Optional์„ ๋ฐ˜ํ™˜ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.

      1๋ฒˆ๊ณผ 3๋ฒˆ์ด ์˜ฌ๋ฐ”๋ฅธ ์ฝ”๋“œ์ด๊ธด ํ•˜์ง€๋งŒ ๋‹จ์ง€ ๋ฒ”์šฉ reducing์œผ๋กœ joining์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Œ์„ ๋ณด์—ฌ์ฃผ๋Š” ์˜ˆ์ œ์ผ ๋ฟ์ด๋‹ค. ์‹ค๋ฌด์—์„œ๋Š” joining์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€๋…์„ฑ๊ณผ ์„ฑ๋Šฅ์— ์ข‹๋‹ค.

6.3 ๊ทธ๋ฃนํ™”

๋ฐ์ดํ„ฐ ์ง‘ํ•ฉ์„ ํ•˜๋‚˜ ์ด์ƒ์˜ ํŠน์„ฑ์œผ๋กœ ๋ถ„๋ฅ˜ํ•ด์„œ ๊ทธ๋ฃนํ™”ํ•˜๋Š” ์—ฐ์‚ฐ

์ž๋ฐ” 8์˜ ํ•จ์ˆ˜ํ˜•์„ ์ด์šฉํ•˜๋ฉด ๊ฐ€๋…์„ฑ ์žˆ๋Š” ํ•œ ์ค„์˜ ์ฝ”๋“œ๋กœ ๊ทธ๋ฃนํ™”๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ฉ”๋‰ด ๊ทธ๋ฃนํ™” ์˜ˆ์ œ(๊ณ ๊ธฐ๋ฅผ ํฌํ•จํ•˜๋Š” ๊ทธ๋ฃน, ์ƒ์„ ์„ ํฌํ•จํ•˜๋Š” ๊ทธ๋ฃน, ๋‚˜๋จธ์ง€ ๊ทธ๋ฃน)

๋‹ค์Œ์ฒ˜๋Ÿผ ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ Collectors.groupingBy๋ฅผ ์ด์šฉํ•ด์„œ ์‰ฝ๊ฒŒ ๋ฉ”๋‰ด๋ฅผ ๊ทธ๋ฃนํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค.

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

๋‹ค์Œ์€ Map์— ํฌํ•จ๋œ ๊ฒฐ๊ณผ๋‹ค.

1
2
{FISH=[prawns, salmon], OTHER=[french fries, rice, season fruit, pizza],
 MEAT=[pork, beef, chicken]}

์ŠคํŠธ๋ฆผ์˜ ๊ฐ ์š”๋ฆฌ์—์„œ Dish.Type๊ณผ ์ผ์น˜ํ•˜๋Š” ๋ชจ๋“  ์š”๋ฆฌ๋ฅผ ์ถ”์ถœํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ groupingBy ๋ฉ”์„œ๋“œ๋กœ ์ „๋‹ฌํ–ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ŠคํŠธ๋ฆผ์ด ๊ทธ๋ฃนํ™”๋˜๋ฏ€๋กœ ์ด๋ฅผ ๋ถ„๋ฅ˜ ํ•จ์ˆ˜๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.

์˜ˆ์ œ) 400์นผ๋กœ๋ฆฌ ์ดํ•˜๋ฅผ โ€˜dietโ€™๋กœ, 400~700 ์นผ๋กœ๋ฆฌ๋ฅผ โ€˜normalโ€™๋กœ, 700 ์นผ๋กœ๋ฆฌ ์ดˆ๊ณผ๋ฅผ โ€˜fatโ€™ ์š”๋ฆฌ๋กœ ๋ถ„๋ฅ˜

Dish ํด๋ž˜์Šค์—๋Š” ์ด๋Ÿฌํ•œ ์—ฐ์‚ฐ์— ํ•„์š”ํ•œ ๋ฉ”์„œ๋“œ๊ฐ€ ์—†์œผ๋ฏ€๋กœ ๋ฉ”์„œ๋“œ ์ฐธ์กฐ ๋Œ€์‹  ๋žŒ๋‹ค ํ‘œํ˜„์‹์œผ๋กœ ํ•„์š”ํ•œ ๋กœ์ง์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
4
5
6
7
8
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 ๊ทธ๋ฃนํ™”๋œ ์š”์†Œ ์กฐ์ž‘

์š”์†Œ๋ฅผ ๊ทธ๋ฃนํ™” ํ•œ ๋‹ค์Œ์—๋Š” ๊ฐ ๊ฒฐ๊ณผ ๊ทธ๋ฃน์˜ ์š”์†Œ๋ฅผ ์กฐ์ž‘ํ•˜๋Š” ์—ฐ์‚ฐ์ด ํ•„์š”ํ•˜๋‹ค.

ex) 500 ์นผ๋กœ๋ฆฌ๊ฐ€ ๋„˜๋Š” ์š”๋ฆฌ๋งŒ ํ•„ํ„ฐ๋งํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜์ž. ๋‹ค์Œ ์ฝ”๋“œ์ฒ˜๋Ÿผ ๊ทธ๋ฃนํ™” ํ•˜๊ธฐ ์ „์— ํ”„๋ ˆ๋””์ผ€์ดํŠธ๋กœ ํ•„ํ„ฐ๋ฅผ ์ ์šฉํ•ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค ์ƒ๊ฐํ•  ๊ฒƒ์ด๋‹ค.

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

์œ„ ์ฝ”๋“œ๋กœ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ๋‹จ์ ๋„ ์กด์žฌํ•œ๋‹ค. ๋ฉ”๋‰ด ์š”๋ฆฌ๋Š” ๋‹ค์Œ์ฒ˜๋Ÿผ ๋งต ํ˜•ํƒœ๋กœ ๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ ์ฝ”๋“œ์— ์œ„ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๋งต์— ์ฝ”๋“œ๋ฅผ ์ ์šฉํ•ด์•ผ ํ•œ๋‹ค.

1
{OTHER=[french fries, pizza], MEAT=[pork, beef]}

โ†’ ํ•„ํ„ฐ ํ”„๋ ˆ๋””์ผ€์ดํŠธ๋ฅผ ๋งŒ์กฑํ•˜๋Š” FISH ์ข…๋ฅ˜ ์š”๋ฆฌ๋Š” ์—†์œผ๋ฏ€๋กœ ๊ฒฐ๊ณผ ๋งต์—์„œ ํ•ด๋‹น ํ‚ค ์ž์ฒด๊ฐ€ ์‚ฌ๋ผ์ง„๋‹ค!

Collectors ํด๋ž˜์Šค์˜ groupingBy ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ์˜ ๋‘ ๋ฒˆ์งธ ์ธ์ˆ˜๋ฅผ ๊ฐ€์ง€๊ณ  ์ด๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค. ๋‘ ๋ฒˆ์งธ Collector ์•ˆ์œผ๋กœ ํ•„ํ„ฐ ํ”„๋ ˆ๋””์ผ€์ดํŠธ๋ฅผ ์ด๋™์‹œํ‚จ๋‹ค.

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

์ด๋ ‡๊ฒŒ ๋˜๋ฉด ๋ชฉ๋ก์ด ๋น„์–ด์žˆ๋Š” FISH๋„ ํ•ญ๋ชฉ์œผ๋กœ ์ถ”๊ฐ€๋œ๋‹ค.

1
{OTHER=[french fries, pizza], MEAT=[pork, beef], FISH=[]}

ex) ๊ทธ๋ฃน์˜ ๊ฐ ์š”๋ฆฌ๋ฅผ ๊ด€๋ จ ์ด๋ฆ„ ๋ชฉ๋ก์œผ๋กœ ๋ณ€ํ™˜

๋งคํ•‘ ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด ์š”์†Œ๋ฅผ ๋ณ€ํ™˜ํ•ด์ค€๋‹ค. filtering ์ปฌ๋ ‰ํ„ฐ์ฒ˜๋Ÿผ mapping ๋ฉ”์„œ๋“œ๋ฅผ groupingBy์˜ ์ธ์ˆ˜๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

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

์ด์ „ ์˜ˆ์ œ์™€ ๋‹ฌ๋ฆฌ ๊ฒฐ๊ณผ ๋งต์˜ ๊ฐ ๊ทธ๋ฃน์€ ์š”๋ฆฌ๊ฐ€ ์•„๋‹ˆ๋ผ ๋ฌธ์ž์—ด ๋ฆฌ์ŠคํŠธ์ด๋‹ค.

groupingBy์™€ ์—ฐ๊ณ„ํ•ด ์„ธ ๋ฒˆ์งธ ์ปฌ๋ ‰ํ„ฐ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ผ๋ฐ˜ ๋งต์ด ์•„๋‹Œ flatMap ๋ณ€ํ™˜์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
4
5
6
7
8
9
10
Map<String, List<String>> dishTags = new HashMap<>();
dishTags.put("pork", asList("greasy", "salty"));
dishTags.put("beef", asList("salty", "roasted"));
dishTags.put("chicken", asList("fried", "crisp"));
dishTags.put("french fries", asList("greasy", "fries"));
dishTags.put("rice", asList("light", "natural"));
dishTags.put("season fruit", asList("fresh", "natural"));
dishTags.put("pizza", asList("tasty", "salty"));
dishTags.put("prawns", asList("tasty", "roasted"));
dishTags.put("salmon", asList("delicious", "fresh"));

์œ„ ํƒœ๊ทธ ๋ชฉ๋ก์„ ๊ฐ€์ง„ ๊ฐ ์š”๋ฆฌ๋กœ ๊ตฌ์„ฑ๋œ ๋งต์—์„œ flatMapping ์ปฌ๋ ‰ํ„ฐ๋ฅผ ์ด์šฉํ•˜๋ฉด ๊ฐ ํ˜•์‹์˜ ์š”๋ฆฌ์˜ ํƒœ๊ทธ๋ฅผ ๊ฐ„ํŽธํ•˜๊ฒŒ ์ถ”์ถœํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
4
Map<Dish.Type, Set<String>> dishNamesByType = 
    menu.stream()
        .collect(grouping(Dish::getType,
                        flatMapping(dish -> dishTags.get(dish.getName()).stream(), toSet())));

๊ฐ ์š”๋ฆฌ์—์„œ ํƒœ๊ทธ ๋ฆฌ์ŠคํŠธ๋ฅผ ์–ป์–ด์•ผ ํ•œ๋‹ค. ๋‘ ์ˆ˜์ค€์˜ ๋ฆฌ์ŠคํŠธ๋ฅผ ํ‰๋ฉดํ™”ํ•˜๋ ค๋ฉด flatMap์„ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•œ๋‹ค.

1
2
{MEAT=[salty, greasy, roasted, fried, crisp], FISH=[roasted, tasty, fresh, delicious],
 OTHER=[salty, greasy, natural, light, tasty, fresh, fried]}

6.3.2 ๋‹ค์ˆ˜์ค€ ๊ทธ๋ฃนํ™”

๋‘ ์ธ์ˆ˜๋ฅผ ๋ฐ›๋Š” ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ Collectors.groupingBy๋ฅผ ์ด์šฉํ•ด์„œ ํ•ญ๋ชฉ์„ ๋‹ค์ˆ˜์ค€์œผ๋กœ ๊ทธ๋ฃนํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค.

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

1
2
3
4
5
6
7
8
9
10
Map<Dish.Type, Map<CaloricLevel, List<Dish>>> dishesByTypeCaloricLevel =
menu.stream().collect(
    groupingBy(Dish::getType,  // ์ฒซ ๋ฒˆ์งธ ์ˆ˜์ค€์˜ ๋ถ„๋ฅ˜ ํ•จ์ˆ˜
        grouping(dish -> {     // ๋‘ ๋ฒˆ์งธ ์ˆ˜์ค€์˜ ๋ถ„๋ฅ˜ ํ•จ์ˆ˜
            if (dish.getCalories() <= 400) return CaloricLevel.DIET;
            else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
            else return CaloricLevel.FAT;
        })
    )
);

๊ทธ๋ฃนํ™”์˜ ๊ฒฐ๊ณผ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋‘ ์ˆ˜์ค€์˜ ๋งต์ด ๋งŒ๋“ค์–ด์ง„๋‹ค.

1
2
3
{MEAT={DIET=[chicken], NORMAL=[beef], FAT=[pork]}, 
 FISH={DIET=[prawns], NORMAL=[salmon]},
 OTHER={DIET=[rice, seasonal fruit], NORMAL=[french fries, pizza]}}
  • ์ฒซ ๋ฒˆ์งธ ์ˆ˜์ค€์˜ ๋ถ„๋ฅ˜ ํ•จ์ˆ˜

    ๋ถ„๋ฅ˜ ํ‚ค ๊ฐ’ : fish, meat, other

  • ๋‘ ๋ฒˆ์งธ ์ˆ˜์ค€์˜ ๋ถ„๋ฅ˜ ํ•จ์ˆ˜

    ๋ถ„๋ฅ˜ ํ‚ค ๊ฐ’ : normal, diet, fat

  • ์ตœ์ข…

    • ์ฒซ ๋ฒˆ์งธ ํ‚ค์™€ ๋‘ ๋ฒˆ์งธ ํ‚ค์˜ ๊ธฐ์ค€์— ๋ถ€ํ•ฉํ•˜๋Š” ์š”์†Œ ๋ฆฌ์ŠคํŠธ ๊ฐ’ : salmon, pizza ๋“ฑ

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

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

groupingBy ์ปฌ๋ ‰ํ„ฐ์— ๋‘ ๋ฒˆ์งธ ์ธ์ˆ˜๋กœ counting ์ปฌ๋ ‰ํ„ฐ๋ฅผ ์ „๋‹ฌํ•ด์„œ ๋ฉ”๋‰ด์—์„œ ์š”๋ฆฌ์˜ ์ˆ˜๋ฅผ ์ข…๋ฅ˜๋ณ„๋กœ ๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
Map<Dish.Type, Long> typesCount = 
    menu.stream().collect(groupingBy(Dish::getType, counring()));

๋‹ค์Œ์€ ๊ฒฐ๊ณผ ๋งต์ด๋‹ค.

1
{MEAT=3, FISH=2, OTHER=4}

๋ถ„๋ฅ˜ ํ•จ์ˆ˜ ํ•œ ๊ฐœ์˜ ์ธ์ˆ˜๋ฅผ ๊ฐ–๋Š” grouping(f)๋Š” ์‚ฌ์‹ค groupingBy(f, toList())์˜ ์ถ•์•ฝํ˜•์ด๋‹ค.

์š”๋ฆฌ์˜ ์ข…๋ฅ˜๋ฅผ ๋ถ„๋ฅ˜ํ•˜๋Š” ์ปฌ๋ ‰ํ„ฐ๋กœ ๋ฉ”๋‰ด์—์„œ ๊ฐ€์žฅ ๋†’์€ ์นผ๋กœ๋ฆฌ๋ฅผ ๊ฐ€์ง„ ์š”๋ฆฌ๋ฅผ ์ฐพ๋Š” ํ”„๋กœ๊ทธ๋žจ๋„ ๋‹ค์‹œ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

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

โ†’ ์š”๋ฆฌ์˜ ์ข…๋ฅ˜(key), Oprional(value)๋ฅผ ๊ฐ’์œผ๋กœ ๊ฐ–๋Š” ๋งต ๋ฐ˜ํ™˜

1
{FISH=Optional[salmon], OTHER=Oprional[pizza], MEAT=Optional[pork]}

์ปฌ๋ ‰ํ„ฐ ๊ฒฐ๊ณผ๋ฅผ ๋‹ค๋ฅธ ํ˜•์‹์— ์ ์šฉํ•˜๊ธฐ

์ด์ „ ๊ทธ๋ฃนํ™” ์—ฐ์‚ฌ์—์„œ ๋งต์˜ ๋ชจ๋“  ๊ฐ’์„ Optional๋กœ ๊ฐ์Œ€ ํ•„์š”๊ฐ€ ์—†์œผ๋ฏ€๋กœ Optional์„ ์‚ญ์ œํ•  ์ˆ˜ ์žˆ๋‹ค.

Collectors.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)));           // ๋ณ€ํ™˜ ํ•จ์ˆ˜

Optional::get์œผ๋กœ ๋ฐ˜ํ™˜๋œ Optional์— ํฌํ•จ๋œ ๊ฐ’์„ ์ถ”์ถœํ•œ๋‹ค. ๋ฆฌ๋“€์‹ฑ ์ปฌ๋ ‰ํ„ฐ๋Š” ์ ˆ๋Œ€ Optional.empty()๋ฅผ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ์•ˆ์ „ํ•œ ์ฝ”๋“œ๋‹ค. ๋‹ค์Œ์€ ๋งต์˜ ๊ฒฐ๊ณผ๋‹ค.

1
{FISH=salmon, OTHER=pizza, MEAT=pork}

  • ์ค‘์ฒฉ ์ปฌ๋ ‰ํ„ฐ์˜ ๋™์ž‘(๊ฐ€์žฅ ์™ธ๋ถ€ ๊ณ„์ธต์—์„œ ์•ˆ์ชฝ์œผ๋กœ ์ž‘์—… ์ˆ˜ํ–‰)
    • ์ปฌ๋ ‰ํ„ฐ๋Š” ์ ์„ ์œผ๋กœ ํ‘œ์‹œ๋˜์–ด ์žˆ์œผ๋ฉฐ groupingBy๋Š” ๊ฐ€์žฅ ๋ฐ”๊นฅ์ชฝ์— ์œ„์น˜ํ•˜๋ฉด์„œ ์š”๋ฆฌ์˜ ์ข…๋ฅ˜์— ๋”ฐ๋ผ ๋ฉ”๋‰ด ์ŠคํŠธ๋ฆผ์„ ์„ธ ๊ฐœ์˜ ์„œ๋ธŒ์ŠคํŠธ๋ฆผ์œผ๋กœ ๊ทธ๋ฃนํ™”ํ•œ๋‹ค.
    • groupingBy ์ปฌ๋ ‰ํ„ฐ๋Š” collectingAndThen ์ปฌ๋ ‰ํ„ฐ๋ฅผ ๊ฐ์‹ผ๋‹ค. ๋”ฐ๋ผ์„œ ๋‘ ๋ฒˆ์งธ ์ปฌ๋ ‰ํ„ฐ๋Š” ๊ทธ๋ฃนํ™”๋œ ์„ธ ๊ฐœ์˜ ์„œ๋ธŒ์ŠคํŠธ๋ฆผ์— ์ ์šฉ๋œ๋‹ค.
    • collectingAndThen ์ปฌ๋ ‰ํ„ฐ๋Š” ์„ธ ๋ฒˆ์งธ ์ปฌ๋ ‰ํ„ฐ maxBy๋ฅผ ๊ฐ์‹ผ๋‹ค.
    • ๋ฆฌ๋“€์‹ฑ ์ปฌ๋ ‰ํ„ฐ๊ฐ€ ์„œ๋ธŒ์ŠคํŠธ๋ฆผ์— ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•œ ๊ฒฐ๊ณผ์— collectingAndThen์˜ Optional::get ๋ณ€ํ™˜ ํ•จ์ˆ˜๊ฐ€ ์ ์šฉ๋œ๋‹ค.
    • groupingBy ์ปฌ๋ ‰ํ„ฐ๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋งต์˜ ๋ถ„๋ฅ˜ ํ‚ค์— ๋Œ€์‘ํ•˜๋Š” ์„ธ ๊ฐ’์ด ๊ฐ๊ฐ์˜ ์š”๋ฆฌ ํ˜•์‹์—์„œ ๊ฐ€์žฅ ๋†’์€ ์นผ๋กœ๋ฆฌ๋‹ค.

groupingBy์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋Š” ๋‹ค๋ฅธ ์ปฌ๋ ‰ํ„ฐ ์˜ˆ์ œ

์ผ๋ฐ˜์ ์œผ๋กœ ์ŠคํŠธ๋ฆผ์—์„œ ๊ฐ™์€ ๊ทธ๋ฃน์œผ๋กœ ๋ถ„๋ฅ˜๋œ ๋ชจ๋“  ์š”์†Œ์— ๋ฆฌ๋“€์‹ฑ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ๋•Œ๋Š” ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ groupingBy์— ๋‘ ๋ฒˆ์งธ ์ธ์ˆ˜๋กœ ์ „๋‹ฌํ•œ ์ปฌ๋ ‰ํ„ฐ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

ex) ๋ฉ”๋‰ด์— ์ž‡๋Š” ๋ชจ๋“  ์š”๋ฆฌ์˜ ์นผ๋กœ๋ฆฌ ํ•ฉ๊ณ„๋ฅผ ๊ตฌํ•˜๋ ค๊ณ  ๋งŒ๋“  ์ปฌ๋ ‰ํ„ฐ ์žฌ์‚ฌ์šฉ

1
2
3
Map<Dish.Type, Integer> totalCaloriesByType =
    menu.stream().collect(groupingBy(Dish::getType, 
            summingInt(Dish::getCalories)));

์ด์™ธ์—๋„ mapping ๋ฉ”์„œ๋“œ๋กœ ๋งŒ๋“ค์–ด์ง„ ์ปฌ๋ ‰ํ„ฐ๋„ groupingBy์™€ ์ž์ฃผ ์‚ฌ์šฉ๋œ๋‹ค.

mapping ๋ฉ”์„œ๋“œ

  • ์ธ์ˆ˜: ์ŠคํŠธ๋ฆผ์˜ ์ธ์ˆ˜๋ฅผ ๋ณ€ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜, ๋ณ€ํ™˜ ํ•จ์ˆ˜์˜ ๊ฒฐ๊ณผ ๊ฐ์ฒด๋ฅผ ๋ˆ„์ ํ•˜๋Š” ์ปฌ๋ ‰ํ„ฐ
  • ์ž…๋ ฅ ์š”์†Œ๋ฅผ ๋ˆ„์ ํ•˜๊ธฐ ์ „์— ๋งคํ•‘ ํ•จ์ˆ˜๋ฅผ ์ ์šฉํ•ด์„œ ๋‹ค์–‘ํ•œ ํ˜•์‹์˜ ๊ฐ์ฒด๋ฅผ ์ฃผ์–ด์ง„ ํ˜•์‹์˜ ์ปฌ๋ ‰ํ„ฐ์— ๋งž๊ฒŒ ๋ณ€ํ™˜

ex) ๊ฐ ์š”๋ฆฌ ํ˜•์‹์— ์กด์žฌํ•˜๋Š” ๋ชจ๋“  CaloricLevel ๊ฐ’์„ ์•Œ๊ณ  ์‹ถ์„ ๋•Œ, groupingBy์™€ mapping ์ปฌ๋ ‰ํ„ฐ๋ฅผ ํ•ฉ์ณ ๊ธฐ๋Šฅ ๊ตฌํ˜„ ๊ฐ€๋Šฅ

1
2
3
4
5
6
7
Map<Dish.Type, Set<CaloricLevel>> caloricLevelsByType =
    menu.stream().collect(
        groupingBy(Dish::getType, mapping(dish -> {
            if (dish.getCalories() <= 400) return CaloricLevel.DIET;
            else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
            else return CaloricLevel.FAT; },
        toSet())));

mapping ๋ฉ”์„œ๋“œ์— ์ „๋‹ฌํ•œ ๋ณ€ํ™˜ ํ•จ์ˆ˜๋Š” Dish๋ฅผ CaloricLevel๋กœ ๋งคํ•‘ํ•œ๋‹ค. CaloricLevel ๊ฒฐ๊ณผ ์ŠคํŠธ๋ฆผ์€ toSet์ปฌ๋ ‰ํ„ฐ๋กœ ์ „๋‹ฌ๋˜๋ฉด์„œ ๋ฆฌ์ŠคํŠธ๊ฐ€ ์•„๋‹Œ ์ง‘ํ•ฉ์œผ๋กœ ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๊ฐ€ ๋ˆ„์ ๋œ๋‹ค(์ค‘๋ณต๊ฐ’ ์ €์žฅx)

1
{OTHER=[DIET, NORMAL], MEAT=[DIET, NORMAL, FAT], FISH=[DIET, NORMAL]}

Set์˜ ํ˜•์‹์ด ์ •ํ•ด์ ธ ์žˆ์ง€ ์•Š๋‹ค. ์ด๋•Œ toCollection์„ ์ด์šฉํ•˜๋ฉด ์›ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ฒฐ๊ณผ๋ฅผ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด ๋‹ค์Œ์ฒ˜๋Ÿผ ๋ฉ”์„œ๋“œ ์ฐธ์กฐ HashSet::new๋ฅผ toCollection์— ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
4
5
6
7
Map<Dish.Type, Set<CaloricLevel>> caloricLevelsByType = 
    menu.stream().collect(
        groupingBy(Dish::getType, mapping(dish -> {
            if (dish.getCalories() <= 400) return CaloricLevel.DIET;
            else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
            else return CaloricLevel.FAT; }
        toCollection(HashSet::new))));

6.4 ๋ถ„ํ• 

: ๋ถ„ํ•  ํ•จ์ˆ˜๋ผ ๋ถˆ๋ฆฌ๋Š” ํ”„๋ ˆ๋””์ผ€์ดํŠธ๋ฅผ ๋ถ„๋ฅ˜ ํ•จ์ˆ˜๋กœ ์‚ฌ์šฉํ•˜๋Š” ํŠน์ˆ˜ํ•œ ๊ทธ๋ฃนํ™” ๊ธฐ๋Šฅ

๋ถ„ํ•  ํ•จ์ˆ˜๋Š” Boolean์„ ๋ฐ˜ํ™˜ํ•˜๋ฏ€๋กœ ๋งต์˜ ํ‚ค ํ˜•์‹์€ Boolean์ด๋‹ค. ๊ฒฐ๊ณผ์ ์œผ๋กœ ๊ทธ๋ฃนํ™” ๋งต์€ ์ตœ๋Œ€ (์ฐธ ์•„๋‹ˆ๋ฉด ๊ฑฐ์ง“์˜ ๊ฐ’์„ ๊ฐ–๋Š”) ๋‘ ๊ฐœ์˜ ๊ทธ๋ฃน์œผ๋กœ ๋ถ„๋ฅ˜๋œ๋‹ค.

ex) ๋ชจ๋“  ์š”๋ฆฌ๋ฅผ ์ฑ„์‹ ์š”๋ฆฌ์™€ ์ฑ„์‹์ด ์•„๋‹Œ ์š”๋ฆฌ๋กœ ๋ถ„๋ฅ˜

1
2
Map<Boolean, List<Dish>> partitionedMenu =
    menu.stream().collect(partitioningBy(Dish::isVegetarian)); // ๋ถ„ํ•  ํ•จ์ˆ˜

์œ„ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋งต์ด ๋ฐ˜ํ™˜๋œ๋‹ค.

1
2
{false=[pork, beef, chicken, prawns, salmon],
 true=[french fries, rice, season fruit, pizza]}

์ด์ œ ์ฐธ๊ฐ’์˜ ํ‚ค๋กœ ๋งต์—์„œ ๋ชจ๋“  ์ฑ„์‹ ์š”๋ฆฌ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.

1
List<Dish> vegetarianDishes = partitionedMenu.get(true);

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

1
2
List<Dish> vegetarianDishes = 
    menu.stream().filter(Dish::isVegetarian).collect(toList());

6.4.1 ๋ถ„ํ• ์˜ ์žฅ์ 

๋ถ„ํ•  ํ•จ์ˆ˜๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ฐธ, ๊ฑฐ์ง“์˜ ๋‘ ๊ฐ€์ง€ ์š”์†Œ์˜ ์ŠคํŠธ๋ฆผ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ชจ๋‘ ์œ ์ง€ํ•œ๋‹ค๋Š” ๊ฒƒ์ด ๋ถ„ํ• ์˜ ์žฅ์ ์ด๋‹ค.

1
2
3
4
Map<Boolean, Map<Dish.Type, List<Dish>>> vegetarianDishesByType =
    menu.stream()
        .collect(partitioningBy(Dish::isVegetarian,           // ๋ถ„ํ•  ํ•จ์ˆ˜
                                groupingBy(Dish::getType)));  // ๋‘ ๋ฒˆ์งธ ์ปฌ๋ ‰ํ„ฐ

์ปฌ๋ ‰ํ„ฐ๋ฅผ ๋‘ ๋ฒˆ์งธ ์ธ์ˆ˜๋กœ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋Š” ์˜ค๋ฒ„๋กœ๋“œ๋œ ๋ฒ„์ „์˜ partitioningBy ๋ฉ”์„œ๋“œ์ด๋‹ค.

1
2
{false=[FISH=[prawns, salmon], MEAT=[pork, beef, chicken]},
 true={OTHER=[french fries, rice, season fruit, pizza]}}

๊ฒฐ๊ณผ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ์ฑ„์‹ ์š”๋ฆฌ์˜ ์ŠคํŠธ๋ฆผ๊ณผ ์ฑ„์‹์ด ์•„๋‹Œ ์š”๋ฆฌ์˜ ์ŠคํŠธ๋ฆผ์„ ๊ฐ๊ฐ ์š”๋ฆฌ ์ข…๋ฅ˜๋กœ ๊ทธ๋ฃนํ™”ํ•ด์„œ ๋‘ ์ˆ˜์ค€์˜ ๋งต์ด ๋ฐ˜ํ™˜๋˜์—ˆ๋‹ค.

ex) ์ฑ„์‹ ์š”๋ฆฌ์™€ ์ฑ„์‹์ด ์•„๋‹Œ ์š”๋ฆฌ ๊ฐ๊ฐ์˜ ๊ทธ๋ฃน์—์„œ ๊ฐ€์žฅ ์นผ๋กœ๋ฆฌ๊ฐ€ ๋†’์€ ์š”๋ฆฌ ์ฐพ๊ธฐ

1
2
3
4
5
Map<Boolean, Dish> mostCaloricPartitionedByVegetarian =
    menu.stream().collect(
            partitioningBy(Dish::isVegetarian, 
                            collectAndThen(maxBy(comparingInt(Dish::getCalories)),
															 Optional::get)));

๋‹ค์Œ์€ ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰ ๊ฒฐ๊ณผ๋‹ค.

1
{false=pork, true=pizza}
  • Quiz. partitioningBy ์‚ฌ์šฉ

    groupingBy ์ปฌ๋ ‰ํ„ฐ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ partitioningBy ์ปฌ๋ ‰ํ„ฐ๋„ ๋‹ค๋ฅธ ์ปฌ๋ ‰ํ„ฐ์™€ ์กฐํ•ฉํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ํŠนํžˆ ๋‘ ๊ฐœ์˜ partitioningBy ์ปฌ๋ ‰ํ„ฐ๋ฅผ ์ด์šฉํ•ด์„œ ๋‹ค์ˆ˜์ค€ ๋ถ„ํ• ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋‹ค์Œ ์ฝ”๋“œ์˜ ๋‹ค์ˆ˜์ค€ ๋ถ„ํ•  ๊ฒฐ๊ณผ๋ฅผ ์˜ˆ์ธกํ•ด๋ณด์ž.

    1
    2
    3
    4
    5
    6
    
      1. menu.stream().collect(partitioningBy(Dish::isVegetarian,
                                              partitioningBy(d -> d.getCalories() > 500)));
      2. menu.stream().collect(partitioningBy(Dish::isVegetarian,
                                              partitioningBy(Dish::getType)));
      3. menu.stream().collect(partitioningBy(Dish::isVegetarian,
                                              counting()));
    
    • ์ •๋‹ต
      1. ์œ ํšจํ•œ ๋‹ค์ˆ˜์ค€ ๋ถ„ํ•  ์ฝ”๋“œ๋ฉฐ, ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋‘ ์ˆ˜์ค€์˜ ๋งต์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

        1
        2
        
         {false={false=[chicken, prawns, salmon], true=[pork, beef]},
          true={false=[rice, season fruit], true=[french fries, pizza]}}
        
      2. partitioningBy๋Š” ๋ถˆ๋ฆฌ์–ธ์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜, ์ฆ‰ ํ”„๋ ˆ๋””์ผ€์ดํŠธ๋ฅผ ์š”๊ตฌํ•˜๋ฏ€๋กœ ์ปดํŒŒ์ผ๋˜์ง€ ์•Š๋Š” ์ฝ”๋“œ๋‹ค. Dish::getType์€ ํ”„๋ ˆ๋””์ผ€์ดํŠธ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” ๋ฉ”์„œ๋“œ ์ฐธ์กฐ๋‹ค.
      3. ๋ถ„ํ• ๋œ ๊ฐ ํ•ญ๋ชฉ์˜ ๊ฐœ์ˆ˜๋ฅผ ๊ณ„์‹ผํ•˜๋Š” ์ฝ”๋“œ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋งต์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

        1
        
         {false=5, true=4}
        

6.4.2 ์ˆซ์ž๋ฅผ ์†Œ์ˆ˜์™€ ๋น„์†Œ์ˆ˜๋กœ ๋ถ„ํ• ํ•˜๊ธฐ

์ •์ˆ˜ n์„ ์ธ์ˆ˜๋กœ ๋ฐ›์•„์„œ 2์—์„œ n๊นŒ์ง€์˜ ์ž์—ฐ์ˆ˜๋ฅผ ์†Œ์ˆ˜์™€ ๋น„์†Œ์ˆ˜๋กœ ๋‚˜๋ˆ„๋Š” ํ”„๋กœ๊ทธ๋žจ์„ ๊ตฌํ˜„ํ•˜์ž. ๋จผ์ € ์ฃผ์–ด์ง„ ์ˆ˜๊ฐ€ ์†Œ์ˆ˜์ธ์ง€ ์•„๋‹Œ์ง€ ํŒ๋‹จํ•˜๋Š” ํ”„๋ ˆ๋””์ผ€์ดํŠธ๋ฅผ ๊ตฌํ˜„ํ•˜๋ฉด ํŽธ๋ฆฌํ•  ๊ฒƒ์ด๋‹ค.

1
2
3
4
public boolean **isPrime**(int candidate) {
    return IntStream.range(2, candidate) // 2๋ถ€ํ„ฐ candidate ๋ฏธ๋งŒ ์‚ฌ์ด์˜ ์ž์—ฐ์ˆ˜ ์ƒ์„ฑ
                    .noneMatch(i -> candidate % i == 0); // ์ŠคํŠธ๋ฆผ์˜ ๋ชจ๋“  ์ •์ˆ˜๋กœ candidate๋ฅผ ๋‚˜๋ˆŒ ์ˆ˜ ์—†์œผ๋ฉด ์ฐธ์„ ๋ฐ˜ํ™˜
}

๋‹ค์Œ์ฒ˜๋Ÿผ ์†Œ์ˆ˜์˜ ๋Œ€์ƒ์„ ์ฃผ์–ด์ง„ ์ˆ˜์˜ ์ œ๊ณฑ๊ทผ ์ดํ•˜์˜ ์ˆ˜๋กœ ์ œํ•œํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
4
5
public boolean **isPrime**(int candidate) {
    int candidateRoot = (int) Math.sqrt((double)candidate);
    return IntStream.rangeClosed(2, candidateRoot)
                    .noneMatch(i -> candidate % i == 0);
}

์ด์ œ n๊ฐœ์˜ ์ˆซ์ž๋ฅผ ํฌํ•จํ•˜๋Š” ์ŠคํŠธ๋ฆผ์„ ๋งŒ๋“  ๋‹ค์Œ์— ์šฐ๋ฆฌ๊ฐ€ ๊ตฌํ˜„ํ•œ isPrime ๋ฉ”์„œ๋“œ๋ฅผ ํ”„๋ ˆ๋””์ผ€์ดํŠธ๋กœ ์ด์šฉํ•˜๊ณ  partitioningBy ์ปฌ๋ ‰ํ„ฐ๋กœ ๋ฆฌ๋“€์Šคํ•ด์„œ ์ˆซ์ž๋ฅผ ์†Œ์ˆ˜์™€ ๋น„์†Œ์ˆ˜๋กœ ๋ถ„๋ฅ˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
4
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์€ ์ˆ˜์ง‘ ์—ฐ์‚ฐ ๊ฒฐ๊ณผ ๊ฐ์ฒด์˜ ํ˜•์‹(ํ•ญ์ƒ ๊ทธ๋Ÿฐ ๊ฒƒ์€ ์•„๋‹ˆ์ง€๋งŒ ๋Œ€๊ฐœ ์ปฌ๋ ‰์…˜ ํ˜•์‹)์ด๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด Stream์˜ ๋ชจ๋“  ์š”์†Œ๋ฅผ List๋กœ ์ˆ˜์ง‘ํ•˜๋Š” ToListCollector๋ผ๋Š” ํด๋ž˜์Šค๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

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

๋ˆ„์  ๊ณผ์ •์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๊ฐ์ฒด๊ฐ€ ์ˆ˜์ง‘ ๊ณผ์ •์˜ ์ตœ์ข… ๊ฒฐ๊ณผ๋กœ ์‚ฌ์šฉ๋œ๋‹ค.

6.5.1 Collector ์ธํ„ฐํŽ˜์ด์Šค์˜ ๋ฉ”์„œ๋“œ ์‚ดํŽด๋ณด๊ธฐ

Colllector ์ธํ„ฐํŽ˜์ด์Šค์— ์ •์˜๋œ ๋‹ค์„ฏ ๊ฐœ์˜ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ดํŽด๋ณด์ž.

๋จผ์ € ์‚ดํŽด๋ณผ ๋„ค ๊ฐœ์˜ ๋ฉ”์„œ๋“œ๋Š” collect ๋ฉ”์„œ๋“œ์—์„œ ์‹คํ–‰ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฐ˜๋ฉด, ๋‹ค์„ฏ ๋ฒˆ์งธ ๋ฉ”์„œ๋“œ characteristics๋Š” collect ๋ฉ”์„œ๋“œ๊ฐ€ ์–ด๋–ค ์ตœ์ ํ™”(ex. ๋ณ‘๋ ฌํ™”)๋ฅผ ์ด์šฉํ•ด์„œ ๋ฆฌ๋“€์‹ฑ ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•  ๊ฒƒ์ธ์ง€ ๊ฒฐ์ •ํ•˜๋„๋ก ๋•๋Š” ํžŒํŠธ ํŠน์„ฑ ์ง‘ํ•ฉ์„ ์ œ๊ณตํ•œ๋‹ค.

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

supplier ๋ฉ”์„œ๋“œ๋Š” ๋นˆ ๊ฒฐ๊ณผ๋กœ ์ด๋ฃจ์–ด์ง„ Supplier๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•œ๋‹ค.

์ฆ‰, supplier๋Š” ์ˆ˜์ง‘ ๊ณผ์ •์—์„œ ๋นˆ ๋ˆ„์ ์ž ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“œ๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์—†๋Š” ํ•จ์ˆ˜๋‹ค.

ToListCollector์ฒ˜๋Ÿผ ๋ˆ„์ ์ž๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ปฌ๋ ‰ํ„ฐ์—์„œ๋Š” ๋นˆ ๋ˆ„์ ์ž๊ฐ€ ๋น„์–ด์žˆ๋Š” ์ŠคํŠธ๋ฆผ์˜ ์ˆ˜์ง‘ ๊ณผ์ •์˜ ๊ฒฐ๊ณผ๊ฐ€ ๋  ์ˆ˜ ์žˆ๋‹ค. ToListCollector์—์„œ supplier๋Š” ๋‹ค์Œ์ฒ˜๋Ÿผ ๋นˆ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

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

์ƒ์„ฑ์ž ์ฐธ์กฐ๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ๋‹ค.

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

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

accumulator ๋ฉ”์„œ๋“œ๋Š” ๋ฆฌ๋“€์‹ฑ ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์ŠคํŠธ๋ฆผ์—์„œ n๋ฒˆ์งธ ์š”์†Œ๋ฅผ ํƒ์ƒ‰ํ•  ๋•Œ ๋‘ ์ธ์ˆ˜, ์ฆ‰ ๋ˆ„์ ์ž(์ŠคํŠธ๋ฆผ์˜ ์ฒซ n-1๊ฐœ ํ•ญ๋ชฉ์„ ์ˆ˜์ง‘ํ•œ ์ƒํƒœ)์™€ n๋ฒˆ์งธ ์š”์†Œ๋ฅผ ํ•จ์ˆ˜์— ์ ์šฉํ•œ๋‹ค. ํ•จ์ˆ˜์˜ ๋ฐ˜ํ™˜๊ฐ’์€ void, ์ฆ‰ ์š”์†Œ๋ฅผ ํƒ์ƒ‰ํ•˜๋ฉด์„œ ์ ์šฉํ•˜๋Š” ํ•จ์ˆ˜์— ์˜ํ•ด ๋ˆ„์ ์ž ๋‚ด๋ถ€์ƒํƒœ๊ฐ€ ๋ฐ”๋€Œ๋ฏ€๋กœ ๋ˆ„์ ์ž๊ฐ€ ์–ด๋–ค ๊ฐ’์ผ์ง€ ๋‹จ์ •ํ•  ์ˆ˜ ์—†๋‹ค.

ToListCollector์—์„œ accumulator๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜๋Š” ์ด๋ฏธ ํƒ์ƒ‰ํ•œ ํ•ญ๋ชฉ์„ ํฌํ•จํ•˜๋Š” ๋ฆฌ์ŠคํŠธ์— ํ˜„์žฌ ํ•ญ๋ชฉ์„ ์ถ”๊ฐ€ํ•˜๋Š” ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.

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

๋‹ค์Œ์ฒ˜๋Ÿผ ๋ฉ”์„œ๋“œ ์ฐธ์กฐ๋ฅผ ์ด์šฉํ•˜๋ฉด ์ฝ”๋“œ๊ฐ€ ๋” ๊ฐ„๊ฒฐํ•ด์ง„๋‹ค.

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

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

finisher ๋ฉ”์„œ๋“œ๋Š” ์ŠคํŠธ๋ฆผ ํƒ์ƒ‰์„ ๋๋‚ด๊ณ  ๋ˆ„์ ์ž ๊ฐ์ฒด๋ฅผ ์ตœ์ข… ๊ฒฐ๊ณผ๋กœ ๋ณ€ํ™˜ํ•˜๋ฉด์„œ ๋ˆ„์  ๊ณผ์ •์„ ๋๋‚ผ ๋•Œ ํ˜ธ์ถœํ•  ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•œ๋‹ค. ๋•Œ๋กœ๋Š” ToListCollector์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ˆ„์ ์ž ๊ฐ์ฒด๊ฐ€ ์ด๋ฏธ ์ตœ์ข… ๊ฒฐ๊ณผ์ธ ์ƒํ™ฉ๋„ ์žˆ๋‹ค. ์ด๋Ÿฐ ๋•Œ๋Š” ๋ณ€ํ™˜ ๊ณผ์ •์ด ํ•„์š”ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ finisher ๋ฉ”์„œ๋“œ๋Š” ํ•ญ๋“ฑ ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

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

์ง€๊ธˆ๊นŒ์ง€ ์‚ดํŽด๋ณธ ์„ธ ๊ฐ€์ง€ ๋ฉ”์„œ๋“œ๋กœ๋„ ์ˆœ์ฐจ์  ์ŠคํŠธ๋ฆผ ๋ฆฌ๋“€์‹ฑ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค. ์‹ค์ œ๋กœ๋Š” collect๊ฐ€ ๋™์ž‘ํ•˜๊ธฐ ์ „์— ๋‹ค๋ฅธ ์ค‘๊ฐ„ ์—ฐ์‚ฐ๊ณผ ํŒŒ์ดํ”„๋ผ์ธ์„ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๊ฒŒ์œผ๋ฅธ ํŠน์„ฑ ๊ทธ๋ฆฌ๊ณ  ๋ณ‘๋ ฌ ์‹คํ–‰ ๋“ฑ๋„ ๊ณ ๋ คํ•ด์•ผ ํ•˜๋ฏ€๋กœ ์ŠคํŠธ๋ฆผ ๋ฆฌ๋“€์‹ฑ ๊ธฐ๋Šฅ ๊ตฌํ˜„์€ ์ƒ๊ฐ๋ณด๋‹ค ๋ณต์žกํ•˜๋‹ค.

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

combiner ๋ฉ”์„œ๋“œ๋Š” ๋งˆ์ง€๋ง‰์œผ๋กœ ๋ฆฌ๋“€์‹ฑ ์—ฐ์‚ฐ์—์„œ ์‚ฌ์šฉํ•  ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

combiner์˜ ์ŠคํŠธ๋ฆผ์€ ์„œ๋กœ ๋‹ค๋ฅธ ์„œ๋ธŒํŒŒํŠธ๋ฅผ ๋ณ‘๋ ฌ๋กœ ์ฒ˜๋ฆฌํ•  ๋•Œ ๋ˆ„์ ์ž๊ฐ€ ์ด ๊ฒฐ๊ณผ๋ฅผ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ• ์ง€ ์ •์˜ํ•œ๋‹ค. toList์˜ combiner๋Š” ๋น„๊ต์  ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ฆ‰, ์ŠคํŠธ๋ฆผ์˜ ๋‘ ๋ฒˆ์งธ ์„œ๋ธŒ ํŒŒํŠธ์—์„œ ์ˆ˜์ง‘ํ•œ ํ•ญ๋ชฉ ๋ฆฌ์ŠคํŠธ๋ฅผ ์ฒซ ๋ฒˆ์งธ ์„œ๋ธŒํŒŒํŠธ ๊ฒฐ๊ณผ ๋ฆฌ์ŠคํŠธ์˜ ๋’ค์— ์ถ”๊ฐ€ํ•˜๋ฉด ๋œ๋‹ค.

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

combiner ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•˜๋ฉด ์ŠคํŠธ๋ฆผ์˜ ๋ฆฌ๋“€์‹ฑ์„ ๋ณ‘๋ ฌ๋กœ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ์ŠคํŠธ๋ฆผ์„ ๋ถ„ํ• ํ•ด์•ผ ํ•˜๋Š”์ง€ ์ •์˜ํ•˜๋Š” ์กฐ๊ฑด์ด ๊ฑฐ์ง“์œผ๋กœ ๋ฐ”๋€Œ๊ธฐ ์ „๊นŒ์ง€ ์›๋ž˜ ์ŠคํŠธ๋ฆผ์„ ์žฌ๊ท€์ ์œผ๋กœ ๋ถ„ํ• ํ•œ๋‹ค(๋ณดํ†ต ๋ถ„์‚ฐ๋œ ์ž‘์—…์˜ ํฌ๊ธฐ๊ฐ€ ๋„ˆ๋ฌด ์ž‘์•„์ง€๋ฉด ๋ณ‘๋ ฌ ์ˆ˜ํ–‰์˜ ์†๋„๋Š” ์ˆœ์ฐจ ์ˆ˜ํ–‰์˜ ์†๋„๋ณด๋‹ค ๋Š๋ ค์ง„๋‹ค. ์ฆ‰, ๋ณ‘๋ ฌ ์ˆ˜ํ–‰์˜ ํšจ๊ณผ๊ฐ€ ์ƒ์‡„๋œ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ ํ”„๋กœ์„ธ์‹ฑ ์ฝ”์–ด์˜ ๊ฐœ์ˆ˜๋ฅผ ์ดˆ๊ณผํ•˜๋Š” ๋ณ‘๋ ฌ ์ž‘์—…์€ ํšจ์œจ์ ์ด์ง€ ์•Š๋‹ค.)
  • ๊ทธ๋ฆผ์—์„œ ๋ณด์—ฌ์ฃผ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ชจ๋“  ์„œ๋ธŒ์ŠคํŠธ๋ฆผ์˜ ๊ฐ ์š”์†Œ์— ๋ฆฌ๋“€์‹ฑ ์—ฐ์‚ฐ์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ ์šฉํ•ด์„œ ์„œ๋ธŒ์ŠคํŠธ๋ฆผ์„ ๋ณ‘๋ ฌ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋งˆ์ง€๋ง‰์—๋Š” ์ปฌ๋ ‰ํ„ฐ์˜ combiner ๋ฉ”์„œ๋“œ๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜๋กœ ๋ชจ๋“  ๋ถ€๋ถ„๊ฒฐ๊ณผ๋ฅผ ์Œ์œผ๋กœ ํ•ฉ์นœ๋‹ค. ์ฆ‰, ๋ถ„ํ• ๋œ ๋ชจ๋“  ์„œ๋ธŒ์ŠคํŠธ๋ฆผ์˜ ๊ฒฐ๊ณผ๋ฅผ ํ•ฉ์น˜๋ฉด์„œ ์—ฐ์‚ฐ์ด ์™„๋ฃŒ๋œ๋‹ค.

Characteristics ๋ฉ”์„œ๋“œ

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

์ŠคํŠธ๋ฆผ์„ ๋ณ‘๋ ฌ๋กœ ๋ฆฌ๋“€์Šคํ•  ๊ฒƒ์ธ์ง€ ๊ทธ๋ฆฌ๊ณ  ๋ณ‘๋ ฌ๋กœ ๋ฆฌ๋“€์Šคํ•œ๋‹ค๋ฉด ์–ด๋–ค ์ตœ์ ํ™”๋ฅผ ์„ ํƒํ•ด์•ผ ํ• ์ง€ ํžŒํŠธ๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

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

  • UNORDERED

    : ๋ฆฌ๋“€์‹ฑ ๊ฒฐ๊ณผ๋Š” ์ŠคํŠธ๋ฆผ ์š”์†Œ์˜ ๋ฐฉ๋ฌธ ์ˆœ์„œ๋‚˜ ๋ˆ„์  ์ˆœ์„œ์— ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š๋Š”๋‹ค.

  • CONCURRENT

    : ๋‹ค์ค‘ ์Šค๋ ˆ๋“œ์—์„œ accumulator ํ•จ์ˆ˜๋ฅผ ๋™์‹œ์— ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ์ด ์ปฌ๋ ‰ํ„ฐ๋Š” ์ŠคํŠธ๋ฆผ์˜ ๋ณ‘๋ ฌ ๋ฆฌ๋“€์‹ฑ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ปฌ๋ ‰ํ„ฐ์˜ ํ”Œ๋ž˜๊ทธ์— UNORDERED๋ฅผ ํ•จ๊ป˜ ์„ค์ •ํ•˜์ง€ ์•Š์•˜๋‹ค๋ฉด ๋ฐ์ดํ„ฐ ์†Œ์Šค๊ฐ€ ์ •๋ ฌ๋˜์–ด ์žˆ์ง€ ์•Š์€(์ฆ‰, ์ง‘ํ•ฉ์ฒ˜๋Ÿผ ์š”์†Œ์˜ ์ˆœ์„œ๊ฐ€ ๋ฌด์˜๋ฏธํ•œ) ์ƒํ™ฉ์—์„œ๋งŒ ๋ณ‘๋ ฌ ๋ฆฌ๋“€์‹ฑ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

  • IDENTITY_FINISH

    : finisher ๋ฉ”์„œ๋“œ๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜๋Š” ๋‹จ์ˆœํžˆ identity๋ฅผ ์ ์šฉํ•  ๋ฟ์ด๋ฏ€๋กœ ์ด๋ฅผ ์ƒ๋žตํ•  ์ˆ˜ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋ฆฌ๋“€์‹ฑ ๊ณผ์ •์˜ ์ตœ์ข… ๊ฒฐ๊ณผ๋กœ ๋ˆ„์ ์ž ๊ฐ์ฒด๋ฅผ ๋ฐ”๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ๋˜ํ•œ ๋ˆ„์ ์ž A๋ฅผ ๊ฒฐ๊ณผ R๋กœ ์•ˆ์ „ํ•˜๊ฒŒ ํ˜•๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

ToListCollector์—์„œ ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๋ฅผ ๋ˆ„์ ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•œ ๋ฆฌ์ŠคํŠธ๊ฐ€ ์ตœ์ข… ๊ฒฐ๊ณผ ํ˜•์‹์ด๋ฏ€๋กœ ์ถ”๊ฐ€ ๋ณ€ํ™˜์ด ํ•„์š”์—†๋‹ค.

๋”ฐ๋ผ์„œ ToListCollector๋Š” IDENTITY_FINISH๋‹ค. ํ•˜์ง€๋งŒ ๋ฆฌ์ŠคํŠธ์˜ ์ˆœ์„œ๋Š” ์ƒ๊ด€์ด ์—†์œผ๋ฏ€๋กœ UNORDERED๋‹ค. ๋งˆ์ง€๋ง‰์œผ๋กœ ToListCollector๋Š” CONCURRENT๋‹ค.

์š”์†Œ์˜ ์ˆœ์„œ๊ฐ€ ๋ฌด์˜๋ฏธํ•œ ๋ฐ์ดํ„ฐ ์†Œ์Šค์—ฌ์•ผ ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

6.5.2 ์‘์šฉํ•˜๊ธฐ

์ปค์Šคํ…€ ToListCollector ๊ตฌํ˜„ํ•˜๊ธฐ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import java.util.*;
import java.util.function.*;
import java.util.stream.Collector;
import static java.util.stream.Collector.Characteristics.*;

public class ToListCollector<T> implements Collect<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 Collections.unmodifiableSet(EnumSet.of(
            IDENTITY_FINISH, CONCURRENT)); // ์ปฌ๋ ‰ํ„ฐ์˜ ํ”Œ๋ž˜๊ทธ๋ฅผ IDENTITY_FINISH, CONCURRENT๋กœ ์„ค์ •
    }
}

์œ„ ๊ตฌํ˜„์ด Collectors.toList ๋ฉ”์„œ๋“œ๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒฐ๊ณผ์™€ ์™„์ „ํžˆ ๊ฐ™์€ ๊ฒƒ์€ ์•„๋‹ˆ์ง€๋งŒ ์‚ฌ์†Œํ•œ ์ตœ์ ํ™”๋ฅผ ์ œ์™ธํ•˜๋ฉด ๋Œ€์ฒด๋กœ ๋น„์Šทํ•˜๋‹ค. ํŠนํžˆ ์ž๋ฐ” API์—์„œ ์ œ๊ณตํ•˜๋Š” ์ปฌ๋ ‰ํ„ฐ๋Š” ์‹ฑ๊ธ€ํ†ค Collections.emptyList()๋กœ ๋นˆ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

์ด์ œ ์ž๋ฐ”์—์„œ ์ œ๊ณตํ•˜๋Š” API ๋Œ€์‹  ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  ์ปฌ๋ ‰ํ„ฐ๋ฅผ ๋ฉ”๋‰ด ์ŠคํŠธ๋ฆผ์˜ ๋ชจ๋“  ์š”๋ฆฌ๋ฅผ ์ˆ˜์ง‘ํ•˜๋Š” ์˜ˆ์ œ์—์„œ ์‚ฌ์šฉ๊ฐ€๋Šฅํ•˜๋‹ค.

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

๋‹ค์Œ์€ ๊ธฐ์กด์˜ ์ฝ”๋“œ๋‹ค.

1
List<Dish> dishes = menuStream.collect(toList());

๊ธฐ์กด ์ฝ”๋“œ์˜ toList๋Š” ํŒฉํ† ๋ฆฌ์ง€๋งŒ ์šฐ๋ฆฌ ToListCollector๋Š” new๋กœ ์ธ์Šคํ„ด์Šคํ™”ํ•œ๋‹ค๋Š” ์ ์ด ๋‹ค๋ฅด๋‹ค.

์ปฌ๋ ‰ํ„ฐ ๊ตฌํ˜„์„ ๋งŒ๋“ค์ง€ ์•Š๊ณ ๋„ ์ปค์Šคํ…€ ์ˆ˜์ง‘ ์ˆ˜ํ–‰ํ•˜๊ธฐ

IDENTITY_FINISH ์ˆ˜์ง‘ ์—ฐ์‚ฐ์—์„œ๋Š” Collector ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์™„์ „ํžˆ ์ƒˆ๋กœ ๊ตฌํ˜„ํ•˜์ง€ ์•Š๊ณ ๋„ ๊ฐ™์€ ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค. Stream์€ ์„ธ ํ•จ์ˆ˜ (๋ฐœํ–‰, ๋ˆ„์ , ํ•ฉ์นจ)์„ ์ธ์ˆ˜๋กœ ๋ฐ›๋Š”๋‹ค.

This post is licensed under CC BY 4.0 by the author.