Skip to content

Latest commit

ย 

History

History
1020 lines (680 loc) ยท 22.3 KB

File metadata and controls

1020 lines (680 loc) ยท 22.3 KB

java8 - Stream

์ŠคํŠธ๋ฆผ์ด๋ž€ ๋ฌด์—‡์ธ๊ฐ€?

์ŠคํŠธ๋ฆผ : ์ปฌ๋ ‰์…˜์ด๋‚˜ ๋ฐฐ์—ด์˜ ๋ฐ˜๋ณต์„ ๋ฉ‹์ง€๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ธฐ๋Šฅ

  • ๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ ์ฝ”๋“œ๋ฅผ ๊ตฌํ˜„ํ•˜์ง€ ์•Š์•„๋„ ๋ฐ์ดํ„ฐ๋ฅผ ํˆฌ๋ช…ํ•˜๊ฒŒ ๋ณ‘๋ ฌ๋กœ ์ฒ˜๋ฆฌํ•จ.
//๊ธฐ์กด ์ฝ”๋“œ
List<Dish> lowCaloricDishes = new ArrayList<>();
for(Dish d : menu){
  if(d.getCalories() < 400){
    lowCaloricDishes.add(d);
  }
}

Collections.sort(lowCaloricDishes, new Comparator<Dish>(){
  public int compare(Dish d1, Dish d2){
    return Integer.compare(d1.getCalories(), d2.getCalories());
  }
});
  
List<String> lowCaloricDishesName = new ArrayList<>();
for(Dish d: lowCaloricDishes){
  lowCaloricDishesName.add(d.getName());
}

//java 8
List<String> lowCaloricDishesName = menu.stream()
  .filter(d->d.getCalories() < 400)
  .sorted(comparing(Dishes::getCalories))
  .map(Dish::getName)
  .collect(toList());
  • ์„ ์–ธํ˜•์œผ๋กœ ์ฝ”๋“œ ๊ตฌํ˜„
  • ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ๊ณผ์ •์„ ๋ณ‘๋ ฌ๋กœ ํ•จ -> ๋ฝ์„ ๊ฑธ ํ•„์š”๊ฐ€ ์—†์Œ - ์„ฑ๋Šฅ์ด ์ข‹์•„์ง
  • ์œ ์—ฐ์„ฑ์ด ์ข‹์•„์ง

์ŠคํŠธ๋ฆผ

๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ์—ฐ์‚ฐ์„ ์ง€์›ํ•˜๋„๋ก ์†Œ์Šค์—์„œ ์ถ”์ถœ๋œ ์—ฐ์†๋œ ์š”์†Œ

  • ์—ฐ์†๋œ ์š”์†Œ
  • ์†Œ์Šค : ์ปฌ๋ ‰์…˜, ๋ฐฐ์—ด , I/O ์ž์› ๋“ฑ์˜ ๋ฐ์ดํ„ฐ ์ œ๊ณต ์†Œ์Šค๋กœ ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ์†Œ๋น„
  • ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ์—ฐ์‚ฐ : filter, map, reduce, find, match, sort ๋“ฑ
  • ํŒŒ์ดํ”„๋ผ์ด๋‹
  • ๋‚ด๋ถ€ ๋ฐ˜๋ณต

์ปฌ๋ ‰์…˜๊ณผ์˜ ์ฐจ์ด

= ๋ฐ์ดํ„ฐ๋ฅผ ์–ธ์ œ ๊ณ„์‚ฐ ํ•˜๋А๋ƒ

์ปฌ๋ ‰์…˜ - ์ปฌ๋ ‰์…˜์ด ์ถ”๊ฐ€๋˜๊ธฐ ์ „์— ๊ณ„์‚ฐ๋˜์–ด์•ผํ•จ - ์ปฌ๋ ‰์…˜์˜ ๋ชจ๋“  ์š”์†Œ๋ฅผ ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅํ•ด์•ผํ•จ, ์ถ”๊ฐ€ํ•˜๋ ค๋Š” ์š”์†Œ๋Š” ๋ฏธ๋ฆฌ ๊ณ„์‚ฐ ๋˜์–ด์•ผํ•จ.

์ŠคํŠธ๋ฆผ

  • ์š”์ฒญํ•  ๋•Œ๋งŒ ์š”์†Œ๋ฅผ ๊ณ„์‚ฐ - ์‚ฌ์šฉ์ž๊ฐ€ ์š”์ฒญํ•  ๋•Œ๋งŒ ๊ฐ’์„ ๊ณ„์‚ฐํ•จ

  • ๋”ฑ ํ•œ๋ฒˆ๋งŒ ํƒ์ƒ‰ํ•จ - ํƒ์ƒ‰๋œ ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๋Š” ์†Œ๋น„๋จ.

    List<String> title = Arrays.asList("Java8", "In", "Action");
    Stream<String> s = title.stream();
    s.forEach(System.out::println);
    s.forEach(System.out::println); //error
  • ์™ธ๋ถ€๋ฐ˜๋ณต(์ปฌ๋ ‰์…˜์—์„œ ์ง์ ‘ ์š”์†Œ๋ฅผ ๋ฐ˜๋ณต)๊ณผ ๋‚ด๋ถ€๋ฐ˜๋ณต(์ŠคํŠธ๋ฆผ์—์„œ ์•Œ์•„์„œ ์ฒ˜๋ฆฌ)

    • ๋‚ด๋ถ€ํ•จ์ˆ˜๊ฐ€ ์ข‹์€ ์ด์œ 
      • ํˆฌ๋ณ‘ํ•˜๊ฒŒ ๋ณ‘๋ ฌ๋กœ ์ฒ˜๋ฆฌํ•˜๊ฑฐ๋‚˜ ๋” ์ตœ์ ํ™”๋œ ๋‹ค์–‘ํ•œ ์ˆœ์„œ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Œ.
      • ์šฐ๋ฆฌ๊ฐ€ ์‹ ๊ฒฝ์“ฐ์ง€ ์•Š์•„๋„๋จ

์ค‘๊ฐ„์—ฐ์‚ฐ

List<String> menus = 
  menu.stream()
  .filter(d -> {
    System.out.println("filtering" + d.getName());
    return d.getCalories() > 300;
  })
  .map(d -> {
    System.out.println("mapping"+ d.getName());
    return d.getName();
  })
  .limit(3)
  .collect(toList());
filtering pork
mapping pork
filtering beef
mapping beef
filtering chicken
mapping chicken
//๊ทธ ์ด์ƒ์€ ํ•„ํ„ฐ๋ง ํ•˜์ง€ ์•Š์Œ
  • Filter, map, limit, sorted, distinct

์ตœ์ข…์—ฐ์‚ฐ

  • Count, forEach, collect

์ŠคํŠธ๋ฆผ ํ™œ์šฉ

ํ•„ํ„ฐ๋ง๊ณผ ์Šฌ๋ผ์ด์‹ฑ

ํ”Œ๋ ˆ๋””์ผ€์ดํŠธ๋กœ ํ•„ํ„ฐ๋ง

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

๊ณ ์œ  ์š”์†Œ ํ•„ํ„ฐ๋ง

List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
numbers.stream()
    .filter(i -> i % 2 ==0)
    .distinct() //์ค‘๋ณต ์š”์†Œ ์ œ๊ฑฐ
    .forEach(System.out::println);

์ŠคํŠธ๋ฆผ ์ถ•์†Œ

List<Dish> dishes = menu.stream()
    .filter(d -> d.getCalories() > 300)
    .limit(3) //์ตœ๋Œ€ n๊ฐœ์˜ ์š”์†Œ๋ฅผ ๋ฐ˜ํ™˜
    .collect(toList());

์š”์†Œ ๊ฑด๋„ˆ๋›ฐ๊ธฐ

List<Dish> dishes = menu.stream()
    .filter(d -> d.getCalories() > 300)
    .skip(2) // n ๋‹จ๊ณ„ ๊ฑด๋„ˆ ๋›ฐ๊ธฐ
    .collect(toList())

๋งคํ•‘

flatMap

List<String> uniqueCharacters = 
    words.stream()
    .map(w -> w.split(""))
    .flatMap(Arrays::stream) //ํ‰๋ฉดํ™”๋œ ์ŠคํŠธ๋ฆผ์„ ๋ฐ˜ํ™˜ ๋ฐฐ์—ด์ด ์•„๋‹Œ ์š”์†Œ ํ•˜๋‚˜์”ฉ
    .distinct()
    .collect(Collectors.toList()); 

๊ฒ€์ƒ‰๊ณผ ๋งค์นญ

ํ”„๋ ˆ๋””์ผ€์ดํŠธ๊ฐ€ ์ ์–ด๋„ ํ•œ ์š”์†Œ์™€ ์ผ์น˜ํ•˜๋Š” ์ง€ ํ™•์ธ

if(menu.stream().anyMatch(Dish::isVegetarian)){
    System.out.println("The menu is (someWhat) vegetarian friendly!");
}

๋ชจ๋“  ์š”์†Œ์™€ ์ผ์น˜ํ•˜๋Š”์ง€ ๊ฒ€์‚ฌ

boolean isHealthy = menu.stream().allMatch(d -> d.getCalories < 1000);

๋ฐ˜๋Œ€

boolean isHealty = menu.stream().nonMatch(d -> d.getCalories() >= 1000);

์š”์†Œ ๊ฒ€์ƒ‰

Optiopnal<Dish> dish = menu.stream()
    .filter(Dish::isVegetarian)
    .findAny();

Optional ์ด๋ž€

๊ฐ’์˜ ์กด์žฌ๋‚˜ ๋ถ€์žฌ ์—ฌ๋ถ€๋ฅผ ํ‘œํ˜„ํ•˜๋Š” ์ปจํ…Œ์ด๋„ˆ ํด๋ž˜์Šค

  • isPresent() ๊ฐ’ ํฌํ•จ ์—ฌ๋ถ€
  • ifPresent(Consumer block)
  • T get() ๊ฐ’ ์กด์žฌํ•˜๋ฉด ๋ด”ํ•œ ์—†์œผ๋ฉด NoSuchElementException
  • T orElse(T other)

findFirst() - ์ฒซ๋ฒˆ์งธ ์š”์†Œ

List<Integer> somNumbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> firstSquareDivisibleByThree = someNumbers.stream()
    .map(x -> x*x)
    .filter(x -> x%3 == 0)
    .findFirst();

๋ฆฌ๋“€์‹ฑ

์š”์†Œ์˜ ํ•ฉ

๊ธฐ์กด

int sum = 0;
for (int x ; numbers){
    sum+=x;
}

-> stream

int sum = numbers.stream().reduce(0, (a, b) -> a + b);
int sum = numbers.stream().reduce(0, Integer::sum);

-> ์ดˆ๊ธฐ๊ฐ’์ด ์—†๋‹ค๋ฉด

Optional<Integer> max = numbers.stream().reduce(Integer::max);
  • Optional ๊ฐ์ฒด๋ฅผ ๋ฆฌํ„ดํ•จ

reduce ๋ฉ”์„œ๋“œ์˜ ์žฅ์ ๊ณผ ๋ณ‘๋ ฌํ™”

  • ๋ณ‘๋ ฌ๋กœ reduce๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Œ

    int sum = numbers.parallelStream().reduce(0, Integer::sum);

์‹ค์ „ ์—ฐ์Šต

  1. 2011๋…„์— ์ผ์–ด๋‚œ ๋ชจ๋“  ํŠธ๋žœ์žญ์…˜์„ ์ฐพ์•„ ๊ฐ’์„ ์˜ค๋ฆ„์ฐจ์ˆœ์œผ๋กœ ์ •๋ฆฌํ•˜์‹œ์˜ค.

    transactions.stream()
        .filter(transaction -> transaction.getYear() == 2011)
        .sorted(comparing(Transaction::getValue))
        .collect(toList())
  2. ๊ฑฐ๋ž˜์ž๊ฐ€ ๊ทผ๋ฌดํ•˜๋Š” ๋ชจ๋“  ๋„์‹œ๋ฅผ ์ค‘๋ณต ์—†์ด ๋‚˜์—ดํ•˜๋ผ.

    List<String> cities = transaction.stream()
        .map(transaction -> transaction.getTrader().getCity())
        .distinct()
        .collect(toSet());
  3. ์ผ€์ž„๋ธŒ๋ฆฌ์ง€์— ๊ทผ๋ฌดํ•˜๋Š” ๋ชจ๋“  ๊ฑฐ๋ž˜์ž๋ฅผ ์ฐพ์•„์„œ ์ด๋ฆ„์ˆœ์œผ๋กœ ์ •๋ ฌํ•˜๋ผ

    List<Trader> traders = transcations.stream()
        .map(Transaction::getTrader)
        .filter(trader -> trader.getCity().equals("Cambridge"))
        .distinct()
        .sorted(comparing(Trader::getName))
        .collect(toList());
        
  4. ๋ชจ๋“  ๊ฑฐ๋ž˜์ž์˜ ์ด๋ฆ„์„ ์•ŒํŒŒ๋ฒณ์ˆœ์œผ๋กœ ์ •๋ ฌํ•ด์„œ ๋ฐ˜ํ™˜ํ•˜๋ผ

    String traderStr = transactions.stream()
        .map(transaction -> transaction.getTrader().getName())
        .distinct()
        .sorted()
        .reduce("", (n1, n2)->n1+n2); //collect(joining())
  5. ๋ฐ€๋ผ๋…ธ์— ๊ฑฐ๋ž˜์ž๊ฐ€ ์žˆ๋Š”๊ฐ€?

    boolean milanBased = transaction.stream()
        .anyMatch(transaction -> transaction.getTrader().getCity().equals("Milan"))
  6. ์ผ€์ž„๋ธŒ๋ฆฌ์ง€์— ๊ฑฐ์ฃผํ•˜๋Š” ๊ฑฐ๋ž˜์ž์˜ ๋ชจ๋“  ํŠธ๋žœ์žญ์…˜๊ฐ’์„ ์ถœ๋ ฅํ•˜์‹œ์˜ค

    transactions.stream()
        .filter(t -> "Cambridge".equals(t.getTrader().getCity()))
        .map(Transaction::getValue)
        .forEach(System.out::println);
  7. ์ „์ฒด ํŠธ๋žœ์žญ์…˜ ์ค‘ ์ตœ๋Œ“๊ฐ’์€ ?

    Optional<Integer> hightestValue = transaction.stream()
        .map(Transaction::getValue)
        .reduce(Integer::max)
  8. ์ „์ฒด ํŠธ๋žœ์žญ์…˜ ์ค‘ ์ตœ์†Ÿ๊ฐ’์€?

    Optional<Transaction> smallestTransaction = transactions.stream()
        .min(comparing(Transaction::getValue))

์ˆซ์žํ˜• ์ŠคํŠธ๋ฆผ

๊ธฐ์กด ์ŠคํŠธ๋ฆผ

int calories = menu.stream()
    .map(Dish::getCalories)
    .reduce(Integer::sum);

->

int calories = menu.stream()
    .map(Dish::getCalories)
    .sum();

์•ˆ๋จ!!! why? ์ŠคํŠธ๋ฆผ ์˜ ์š”์†Œ ํ˜•์‹์€ Integer์ง€๋งŒ ์ธํ„ฐํŽ˜์ด์Šค์—๋Š” sum ๋ฉ”์„œ๋“œ๊ฐ€ ์—†๋‹ค.

-> ๊ธฐ๋ณธํ˜• ํŠนํ™” ์ŠคํŠธ๋ฆผ ๋“ฑ์žฅ

int calories = menu.stream()
    .mapToInt(Dish::getCalories)
    .sum(); //IntStream ์ธํ„ฐํŽ˜์ด์Šค์—์„œ ์ œ๊ณตํ•˜๋Š” sum๋ฉ”์„œ๋“œ ์ด์šฉ

IntStream ์—์„œ ๋‹ค์‹œ Stream์œผ๋กœ ๋Œ์•„๊ฐ€๋ ค๋ฉด?

Stream<Integer> stream = intStream.boxed();

but ์š”์†Œ๊ฐ€ ์—†์„ ๋•Œ ๊ธฐ๋ณธ์œผ๋กœ 0์„ ๋ฐ˜ํ™˜ํ•จ -> ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ์Œ.

OptionalInt

OptionalInt maxCalories = menu.stream()
    .mapToInt(Dish::getCalories)
    .max();

int max = maxCalories.orElse(1);

์ˆซ์ž ๋ฒ”์œ„

ํŠน์ • ๋ฒ”์œ„์˜ ์ˆซ์ž๋ฅผ ์ด์šฉํ•ดํ•˜๋Š” ์ƒํ™ฉ

IntStream evenNumbers = IntStream.rangeClosed(1, 100)
    .filter(n -> n % 2 == 0); //1 - 100 ๊นŒ์ง€ ์ง์ˆ˜ ์ŠคํŠธ๋ฆผ

system.out.println(evenNumbers.count());

์ŠคํŠธ๋ฆผ ๋งŒ๋“ค๊ธฐ

Stream<String> stream = Stream.of("Java 8", "Lambdas ", "In ", "Actions");
stream.map(String::toUpperCase).forEach(System.out::println);

Stream.empty(); //๋นˆ ์ŠคํŠธ๋ฆผ ๋งŒ๋“ค๊ธฐ

๋ฐฐ์—ด์ผ ๋•Œ

int[] numbers = {2, 3, 5, 7, 11, 13};
int sum = Arrays.stream(numbers).sum(); //intstream ์ƒ์„ฑ๋จ

ํŒŒ์ผ์ผ ๋•Œ

long uniqueWords = 0;
try(Stream<String> lines = 
    Files.lines(Paths.get("data.txt"), Charset.defaultCharset())){
    uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" ")))
        .distinct()
        .count();
}catch(IOException e){
    
}

iterate

Stream.iterate(0, n -> n+2 )
                .limit(10)
                .forEach(System.out::println);
0
2
4
6
8
10
12
14
16
18

iterate ๋ฌดํ•œ ์ŠคํŠธ๋ฆผ์„ ๋งŒ๋“ ๋‹ค

generate

Stream.generate(Math::random)
                .limit(5)
                .forEach(System.out::println);

์ƒˆ๋กœ์šด ๊ฐ’์„ ์ƒ์‚ฐํ•œ๋‹ค.

iterate๋‚˜ generate ๋ชจ๋‘ limit๊ฐ€ ์—†์œผ๋ฉด ์–ธ ๋ฐ”์šด๋“œ ์ƒํƒœ๊ฐ€ ๋จ. ๋ฌดํ•œ...

IntSupplier fib = new IntSupplier(){
            private int previous = 0;
            private int current =1;
            public int getAsInt(){
                int oldPrevious = this.previous;
                int nextValue = this.previous + this.current;
                this.previous = this.current;
                this.current = nextValue;
                return oldPrevious;
            }
        };

IntStream.generate(fib).limit(10).forEach(System.out::println); //๊ณ„์‚ฐ ์ค‘์—์„  fib ๊ฐ€๋ณ€ ๊ฐ์ฒด์ž„

์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘

๊ธฐ์กด ์ฝ”๋“œ

Map<Currency, List<Transaction>> transactionsByCurrencies = new HashMap<>();

for(Transaction transaction : transactions){
  Currency = transaction.getCurrencty();
  List<Transaction> transactionsForCurrency = transactionsByCurrencies.get(currency);
  
  if(transactionsForCurrency == null){
    transactionsForCurrency.put(currencty, transactionsForCurrency);
  }
  
  transactionsForCurrency.add(transaction);
}

stream

Map<Currency, List<Transaction>> transactionsByCurrencies = transactions.stream().collect(groupingBy(Transaction::getCurrency));

์ปฌ๋ ‰ํ„ฐ๋ž€?

ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ : '๋ฌด์—‡'์„ ์–ป์„์ง€ ์ง์ ‘ ๋ช…์‹œ but ์–ด๋–ค ๋ฐฉ๋ฒ•์œผ๋กœ ์–ป์„์ง€๋Š” ๋…ธ๊ด€์‹ฌ

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

์œ„ ์ฝ”๋“œ์—์„œ ์ŠคํŠธ๋ฆผ์ด ๊ฐ ํŠธ๋žœ์žญ์…˜์„ ํƒ์ƒ‰ํ•˜๊ณ  currency๋ฅผ ์ถ”์ถœํ•˜์—ฌ group ํ•˜๋Š” ๊ณผ์ •์„ ์ง์ ‘ ๊ตฌํ˜„ํ•˜์ง€ ์•Š์•„๋„ ๋จ

๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ toList()

List<Transaction> transactions = transactionsTream.collect(Collector.toList());

์ปฌ๋ ‰ํ„ฐ๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ

  • ์ŠคํŠธ๋ฆผ ์š”์†Œ๋ฅผ ํ•˜๋‚˜์˜ ๊ฐ’์œผ๋กœ ๋ฆฌ๋“€์Šคํ•˜๊ณ  ์š”์•ฝ
  • ์š”์†Œ ๊ทธ๋ฃนํ™”
  • ์š”์†Œ ๋ถ„ํ• 

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

Collector.counting -> import static java.util.stream.Collector.*; ์ถ”๊ฐ€ ํ›„ -> count()

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

์ตœ๋Œ“๊ฐ’๊ณผ ์ตœ์†Ÿ๊ฐ‘ ๊ตฌํ•˜๊ธฐ

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

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

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

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

Collectors.summingInt ํ™œ์šฉ

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

์ดˆ๊ธฐ๊ฐ’ 0์—์„œ ์‹œ์ž‘ํ•ด์„œ ๊ฐ์ฒด๋ฅผ getCalories ๋กœ ๋ณ€ํ™˜ํ›„ ๋”ํ•œ๋‹ค

average

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

summarizing (์š”์•ฝ๋œ ์ •๋ณด๋ฅผ ๊ฐ์ฒด๋กœ ๋ฐ˜ํ™˜)

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

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

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

String shortMenu = menu.stream().map(Dish::getName).collect(joining());
  • joining ๋ฉ”์„œ๋“œ๋Š” StringBuilder๋ฅผ ์ด์šฉํ•ด์„œ ๋ฌธ์ž์—ด์€ ๋งŒ๋“ค์–ด์คŒ
String shortMenu = menu.stream().collect(joining());

toString ์‚ฌ์šฉ์‹œ map ์‚ฌ์šฉ ์•ˆํ•ด๋„๋จ.

๊ตฌ๋ถ„์ž๋ฅผ ๋„ฃ๊ณ  ์‹ถ๋‹ค๋ฉด?

String shortMenu = menu.stream().collect(joining(", "));
  • ๊ทธ๋ƒฅ ๋„ฃ์œผ๋ฉด ๋จ

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

collector๋ฅผ ํ™œ์šฉํ•œ ๊ฒƒ๋“ค์€ reduce ๋กœ ๋‹ค ๊ตฌํ˜„ ๊ฐ€๋Šฅ (ํŽธ๋ฆฌํ•จ ๋•Œ๋ฌธ์— collector ์‚ฌ์šฉ)

reducing

  • ์ฒซ๋ฒˆ์งธ ์ธ์ž๋Š” ์ธ์ˆ˜๊ฐ€ ์—†์„ ๋•Œ ๋ฐ˜ํ™˜๊ฐ’
  • ๋ณ€ํ™˜์‹
  • ๊ณ„์‚ฐ์‹
int totalCalories = menu.stream().collect(reducing(0, Dish::getCalories, (i, j) -> i + j));

ํ•œ๊ฐœ์˜ ์ธ์ˆ˜๋ฅผ ๊ฐ€์งˆ ๋•Œ๋Š”?

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

์–ธ์ œ collect, reducing์„ ์‚ฌ์šฉํ•ด์•ผํ•˜๋‚˜?

์ƒˆ๋กœ์šด ๊ฐ์ฒด๋ฅผ ํ• ๋‹นํ•˜๋ฉด ์„ฑ๋Šฅ ์ €ํ•˜๊ฐ€ ์ผ์–ด๋‚˜๋ฏ€๋กœ collect ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ๋” ์ข‹์Œ

**์—ฌ๋Ÿฌ๊ฐ€์ง€ ๋ฐฉ๋ฒ• **

int totalCalories = menu.stream().collect(reducing(0, Dish::getCalories, Integer::sum));
int totlaCalories = menu.stream().map(Dish::getCaloreis).reduce(Integer::sum).get();
int totalCalories = menu.stream().mapToInt(Dish::getCalories).sum();

์ƒํ™ฉ์— ๋”ฐ๋ผ์„œ ์„ ํƒํ•˜๋ฉด๋จ.

๊ทธ๋ฃนํ™”

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

์ŠคํŠธ๋ฆผ -> ๋ถ„๋ฅ˜ํ•จ์ˆ˜ -> ๊ทธ๋ฃนํ™” ๋งต์— ๋ถ„๋ฅ˜

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

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;
      }
    })));

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

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

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

Map<Dish.Type, Dish> mostcaloricByType = =
  menu.stream()
  .collect(groupingBy(Dish::getType, collectingAndThen(
  		maxBy(comparingInt(Dish::getCalories)), Optional::get)));

422BB445-5246-4918-AFAA-DF08C80911CB_1_105_c

๋ถ„ํ• 

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

  • boolean ํ˜•์‹์˜ ๋งต
Map<Boolean, List<Dish>> partitionedMenu = menu.stream().collect(partitioningBy(Dish::isVegeterian));
{false = [pork, beef, chicken, prawns, salmon], 
true = [french fries, rice, season fruit, pizza]}
List<Dish> vegetarianDishes = partitionedMenu.get(true); //true์ธ ๊ฐ’์„ ๋ชจ๋‘ ๋ฐ˜ํ™˜
//==
List<Dish> vegetarianDishes = menu.stream().filter(Dish::isVegetarian).collect(toList());

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

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

Map<Boolean, Map<Dish.Type, List<Dish>>> vegetarianDishesByType = menu.stream().collect(partitioningBy(Dish::isVegetarian, groupingBy(Dish::getType)));
{false = {FISH=[prawns, salmon], MEAT=[pork, beef, chicken]},
true = {OTHER=[french fries, rice, season fruit, pizza]}};

๊ฐ€์žฅ ์นผ๋กœ๋ฆฌ๊ฐ€ ๋†’์€ ์š”๋ฆฌ ์ฐพ์„ ์‹œ

Map<Boolean, Dish> mostCaloricPartitionedByVegetarian = 
  menu.stream().collect(
		partitioningBy(Dish::isvegetarian, collectingAndThen(
    	maxBy(comparingInt(Dish::getCalories)), Optional::get
    )));
{false=pork, true=pizza}

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

public boolean isPrime(int candidate){
  return Intstream.range(2, candidate) //1 ~ candidate - 1๊นŒ์ง€์˜ ์ˆ˜ ์ƒ์„ฑ
    .nonMatch(i -> candidate % i == 0); //๋‚˜๋ˆŒ ์ˆ˜ ์—†์œผ๋ฉด ์ฐธ ๋ฐ˜ํ™˜
}

-> ๋” ์—ฐ์ƒ๋Ÿ‰ ์ค„์ด๋ฉด?

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

-> partitioningBy์„ ํ™œ์šฉํ•˜๋ฉด?

public Map<Boolean, List<Integer>> partitionPrimes(int n){
  return IntStream.rangeClosed(2, n).boxed()
    .collect(
  		partitioningBy(candidate -> isPrime(candidate)));
}

์ง€๊ธˆ๊นŒ์ง€ ๋ฐฐ์šด collector

ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ ๋ฐ˜ํ™˜ ํ˜•์‹ ์‚ฌ์šฉ ์˜ˆ์ œ
toList List ์ŠคํŠธ๋ฆผ์˜ ๋ชจ๋“  ํ•ญ๋ชฉ์„ ๋ฆฌ์ŠคํŠธ๋กœ ์ˆ˜์ง‘
toSet Set ์ŠคํŠธ๋ฆผ์˜ ๋ชจ๋“  ํ•ญ๋ชฉ์„ ์ค‘๋ณต์ด ์—†๋Š” ์ง‘ํ•ฉ์œผ๋กœ ์ˆ˜์ง‘
toCollection Collection ์ŠคํŠธ๋ฆผ์˜ ๋ชจ๋“  ํ•ญ๋ชฉ์„ ๊ณต๊ธ‰์ž๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์ปฌ๋ ‰์…˜์œผ๋กœ ์ˆ˜์ง‘ex) .collect(toCollection(), ArrayList::new);

image-20200113144644260

Collector ์ธํ„ฐํŽ˜์ด์Šค

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์€ ์ˆ˜์ง‘ ์—ฐ์‚ฐ ๊ฒฐ๊ณผ ๊ฐ์ฒด์˜ ํ˜•์‹

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

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

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

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

๋ฆฌ๋“€์‹ฑ ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜

public BiComsumer<List<T>, T>accumulator(){
  return (list, item) -> list.add(item); //List::add
}

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

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

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

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

์ˆœ์ฐจ ๋ฆฌ๋“€์‹ฑ ๊ณผ์ •์˜ ๋…ผ๋ฆฌ์  ์ˆœ์„œ

  1. A accumulator = collector.supplier().get();
  2. ์ŠคํŠธ๋ฆผ์— ์š”์†Œ๊ฐ€ ๋‚จ์•„ ์žˆ๋Š”๊ฐ€? -> ์˜ˆ - Collector.accumulator().accept(accumulator, next) -> 2๋ฒˆ ๋‹ค์‹œ ์‹คํ–‰
  3. R result = collector.finisher().apply(accumulator);
  4. return result;
public BinaryOperator<List<T>> combiner(){
  return (list1, list2) =?{
    list1.addAll(list2);
    return list1;
  }
}

Characteristics

  • unordered
    • ์ŠคํŠธ๋ฆผ ์š”์†Œ์˜ ๋ฐฉ๋ฌธ ์ˆœ์„œ๋‚˜ ๋ˆ„์  ์ˆœ์„œ์— ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š์Œ
  • concurrent
    • ๋‹ค์ค‘ ์Šค๋ ˆ๋“œ์—์„œ accumulatorํ•จ์ˆ˜๋ฅผ ๋™์‹œ์— ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ์ด ์ปฌ๋ ‰ํ„ฐ๋Š” ์ŠคํŠธ๋ฆผ์˜ ๋ณ‘๋ ฌ ๋ฆฌ๋“€์‹ฑ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Œ.
  • Identity_finish
    • Finisher ๋ฉ”์„œ๋“œ๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜๋Š” ๋‹จ์ˆœํžˆ identity๋ฅผ ์ ์šฉํ•  ๋ฟ์ด๋ฏ€๋กœ ์ด๋ฅผ ์ƒ๋žตํ•  ์ˆ˜ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ตœ์ข… ๊ฒฐ๊ณผ๋กœ ๋ˆ„์ ์ž ๊ฐ์ฒด๋ฅผ ๋ฐ”๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ
public class ToListCollector<T> implements Collector<T, List<T>, List<T>>{
  
  @Override
  public Supplier<List<T>> supplier(){ //์ˆ˜์ง‘ ์—ฐ์‚ฐ์˜ ์‹œ๋ฐœ์ 
    return ArrayList::new;
  }
  
  @Override
  public BiComsumer<List<T>, T> accumulator(){ //ํƒ์ƒ‰ํ•œ ํ•ญ๋ชฉ์„ ๋ˆ„์ ํ•˜๊ณ  ๋ฐ”๋กœ ๋ˆ„์ ์ž๋ฅผ ๊ณ ์นจ
    return List::add;
  }
  
  @Override
  public Function<List<T>, List<T>> finisher(){ //ํ•ญ๋“ฑ ํ•จ์ˆ˜
    return Function.indentity();
  }
  
  @Oeverride
  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));
  }
}
List<Dish> dishes = menuStream.collect(new ToListCollector<Dish>());

๊ธฐ์กด ์ฝ”๋“œ

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

๊ตฌํ˜„ํ•˜์ง€ ์•Š๊ณ  ๋งŒ๋“ค๊ธฐ (IDENTITY_FINISH ์ผ๋•Œ)

List<Dish> dishes = menuStream.collect(
ArrayList::new, List::add, List::addAll);

n์ดํ•˜์˜ ์ž์—ฐ์ˆ˜๋ฅผ ์†Œ์ˆ˜์™€ ๋น„์†Œ์ˆ˜๋กœ ๋ถ„๋ฅ˜ํ•˜๊ธฐ

public Map<Boolean, List<Integer>> partitionPrimes(int n){
  return IntStream.rangeClosed(2, n).boxed()
    .collect(partitioningBy(candidate -> isPrime(candidate)))
}

์†Œ์ˆ˜๋กœ๋งŒ ๋‚˜๋ˆ„๊ธฐ

  1. Collector ํด๋ž˜์Šค ์‹œ๊ทธ๋‹ˆ์ฒ˜ ์ •์˜
public class PrimeNumbersCollector implements Collector<Integer, Map<Boolean, List<Integer>>, Map<Boolean, List<Integer>>>
  1. ๋ฆฌ๋“€์‹ฑ ์—ฐ์‚ฐ ๊ตฌํ˜„
    @Override
    public BiConsumer<Map<Boolean, List<Integer>>, Integer> accumulator() {
        return (Map<Boolean, List<Integer>> acc, Integer candidate)->{
            acc.get(isPrime(acc.get(true), candidate))
              .add(candidate);
        };
    }
  1. ๋ณ‘๋ ฌ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ์ปฌ๋ ‰ํ„ฐ ๋งŒ๋“ค๊ธฐ
    @Override
    public BinaryOperator<Map<Boolean, List<Integer>>> combiner() {
        return (Map<Boolean, List<Integer>> map1, Map<Boolean, List<Integer>> map2)->{
            map1.get(true).addAll(map2.get(true));
            map1.get(false).addAll(map2.get(false));
            return map1;
        };
    }
  1. finisher ๋ฉ”์„œ๋“œ์™€ ์ปฌ๋ ‰ํ„ฐ์˜ characteristics ๋ฉ”์„œ๋“œ
    @Override
    public Function<Map<Boolean, List<Integer>>, Map<Boolean, List<Integer>>> finisher() {
        return Function.identity();
    }
    @Override
    public Set<Characteristics> characteristics() {
        return Collections.unmodifiableSet(EnumSet.of(Characteristics.IDENTITY_FINISH));
    }

์ฐธ๊ณ 

Java 8 in action