hades

[๋ชจ๋˜ ์ž๋ฐ” ์ธ ์•ก์…˜] ์ŠคํŠธ๋ฆผ ๋ณธ๋ฌธ

๐Ÿƒ๐Ÿป‍โ™‚๏ธ ๊ธฐ๋ณธํ›ˆ๋ จ/Java

[๋ชจ๋˜ ์ž๋ฐ” ์ธ ์•ก์…˜] ์ŠคํŠธ๋ฆผ

hades1 2024. 9. 5. 20:28

ํ•„์š”์„ฑ

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์นผ๋กœ๋ฆฌ๊ฐ€ 100๋ณด๋‹ค ๋‚ฎ์€ ์š”๋ฆฌ๋ช…์„ ์„ ํƒํ•˜๋ผ๋Š” SQL ์ฟผ๋ฆฌ๋ฌธ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

SELECT name FROM dishes WHERE calorie < 100

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

 

๋งŽ์€ ์š”์†Œ๋ฅผ ํฌํ•จํ•˜๋Š” ์ปฌ๋ ‰์…˜์˜ ์„ฑ๋Šฅ์„ ๋†’์ด๊ณ  ์‹ถ๋‹ค. ์„ฑ๋Šฅ์„ ๋†’์ด๋ ค๋ฉด, ๋ฉ€ํ‹ฐ์ฝ”์–ด ์•„ํ‚คํ…์ฒ˜๋ฅผ ํ™œ์šฉํ•ด์„œ ๋ณ‘๋ ฌ๋กœ ์ปฌ๋ ‰์…˜ ์š”์†Œ๋ฅผ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•œ๋‹ค.

 

๋˜ํ•œ, ํ†ตํ™”๋ณ„๋กœ ํŠธ๋žœ์žญ์…˜์„ ๊ทธ๋ฃนํ™”ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค. ์ฝ”๋“œ๊ฐ€ ์ƒ๋‹นํžˆ ๊ธธ๋‹ค.

Map<Currency, List<Transaction>> transactionsByCurrencies = new HashMap<>();
for (Transaction transaction : transactions){
    Currency currency = transaction.getCurrency();
    List<Transaction> transactionForCurrency = transactionsByCurrencies.get(currency);
    if (transactionForCurrency == null){
        transactionForCurrency = new ArrayList<>();
        transactionsByCurrencies.put(currency, transactionForCurrency);
    }
    transactionForCurrency.add(transaction)
}

 

ํ•˜์ง€๋งŒ, ์ŠคํŠธ๋ฆผ์„ ์ด์šฉํ•˜์—ฌ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

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

 

์ด์™€ ๊ฐ™์€ ํ•„์š”์„ฑ์œผ๋กœ ๋“ฑ์žฅํ•œ ๊ฒƒ์ด ์ŠคํŠธ๋ฆผ์ด๋‹ค!

 

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

์ŠคํŠธ๋ฆผ์„ ์ด์šฉํ•˜๋ฉด, ์„ ์–ธํ˜•(๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ์ž„์‹œ ์ฝ”๋“œ ๋Œ€์‹  ์งˆ์˜๋กœ ํ‘œํ˜„)์œผ๋กœ ์ปฌ๋ ‰์…˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค. ๋˜ํ•œ, ์ŠคํŠธ๋ฆผ์„ ์ด์šฉํ•˜๋ฉด, ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ์ฝ”๋“œ๋ฅผ ๊ตฌํ˜„ํ•˜์ง€ ์•Š์•„๋„ ๋ฐ์ดํ„ฐ๋ฅผ ํˆฌ๋ช…ํ•˜๊ฒŒ ๋ณ‘๋ ฌ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

 

์•„๋ž˜๋Š” ์ด ์žฅ์ ๋“ค์„ ์‚ฌ์šฉํ•˜์—ฌ 100 ๋ฏธ๋งŒ ์นผ๋กœ๋ฆฌ๋กœ ํ•„ํ„ฐ๋งํ•˜๊ณ , ์นผ๋กœ๋ฆฌ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ •๋ ฌํ•˜๊ณ , ์ด๋ฆ„์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฆฌ์ŠคํŠธ๋ฅผ ์–ป๋Š” ๊ฒƒ์„ ๊ตฌํ˜„ํ•œ ๊ฒƒ์ด๋‹ค.

List<Dish> menu = new ArrayList<>();
List<String> lowCaloricDishesName = menu.parallelStream()
        .filter((dish -> dish.getCalories() < 100))
        .sorted(Comparator.comparing(Dish::getCalories))
        .map(Dish::getName)
        .collect(Collectors.toList());

๋ฃจํ”„๋‚˜ if ์กฐ๊ฑด๋ฌธ ๋“ฑ์˜ ์ œ์–ด ๋ธ”๋ก์„ ์‚ฌ์šฉํ•ด์„œ ๋™์ž‘์„ ๊ตฌํ˜„ํ•˜์ง€ ์•Š์•„๋„, ์„ ์–ธํ˜•์œผ๋กœ ๋™์ž‘์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋˜ํ•œ ์—ฌ๋Ÿฌ ์—ฐ์‚ฐ์„ ์—ฐ๊ฒฐํ•ด๋„ ์—ฌ์ „ํžˆ ๊ฐ€๋…์„ฑ๊ณผ ๋ช…ํ™•์„ฑ์ด ์œ ์ง€๋œ๋‹ค.

 

filter, sorted, map, collect ๊ฐ™์€ ์—ฐ์‚ฐ์€ ๊ณ ์ˆ˜์ค€ ๋นŒ๋”ฉ ๋ธ”๋ก์œผ๋กœ ์ด๋ฃจ์–ด์ ธ ์žˆ์œผ๋ฏ€๋กœ, ํŠน์ • ์Šค๋ ˆ๋“œ ๋ชจ๋ธ์— ์ œํ•œ๋˜์ง€ ์•Š๊ณ  ์ž์œ ๋กญ๊ฒŒ ์–ด๋–ค ์ƒํ™ฉ์—์„œ๋“  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ฒฐ๊ณผ์ ์œผ๋กœ ์ŠคํŠธ๋ฆผ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ณผ์ •์—์„œ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ๊ณผ์ •์„ ๋ณ‘๋ ฌํ™”ํ•˜๋Š” ์ด์ ์„ ์–ป๊ณ , ์Šค๋ ˆ๋“œ์™€ ๋ฝ์„ ๊ฑฑ์ •ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.

 

์ž๋ฐ”8์˜ ์ŠคํŠธ๋ฆผ API์˜ ํŠน์ง•์„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์š”์•ฝํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ์„ ์–ธํ˜• : ๋” ๊ฐ„๊ฒฐํ•˜๊ณ  ๊ฐ€๋…์„ฑ์ด ์ข‹์•„์ง„๋‹ค.
  • ์กฐ๋ฆฝํ•  ์ˆ˜ ์žˆ์Œ : ์—ฌ๋Ÿฌ API๋ฅผ ์—ฐ๊ฒฐํ•˜๋Š” ๊ฒƒ์„ ๋งํ•˜๋ฉฐ, ์œ ์—ฐ์„ฑ์ด ์ข‹์•„์ง„๋‹ค.
  • ๋ณ‘๋ ฌํ™” : ์„ฑ๋Šฅ์ด ์ข‹์•„์ง„๋‹ค.

 

์•ž์œผ๋กœ ์‚ฌ์šฉํ•  ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

public class Dish {
    private final String name;
    private final boolean vegetarian;
    private final int calories;
    private final Type type;

    public Dish(String name, boolean vegetarian, int calories, Type type) {
        this.name = name;
        this.vegetarian = vegetarian;
        this.calories = calories;
        this.type = type;
    }

    public String getName() {
        return name;
    }

    public boolean isVegetarian() {
        return vegetarian;
    }

    public int getCalories() {
        return calories;
    }

    public Type getType() {
        return type;
    }

    @Override
    public String toString() {
        return name;
    }

    public enum Type { MEAT, FISH, OTHER };
}
List<Dish> menu = Arrays.asList(
        new Dish("pork", false, 800, Dish.Type.MEAT),
        new Dish("beef", false, 700, Dish.Type.MEAT),
        new Dish("chicken", false, 400, Dish.Type.MEAT),
        new Dish("french fries", true, 530, Dish.Type.OTHER),
        new Dish("rice", true, 350, Dish.Type.OTHER),
        new Dish("season fruit", true, 120, Dish.Type.OTHER),
        new Dish("pizza", true, 550, Dish.Type.OTHER),
        new Dish("prawns", false, 300, Dish.Type.FISH),
        new Dish("salmon", false, 450, Dish.Type.FISH)
);

 

์ŠคํŠธ๋ฆผ ์‹œ์ž‘ํ•˜๊ธฐ

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

  • ์—ฐ์†๋œ ์š”์†Œ : ์ปฌ๋ ‰์…˜๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์ŠคํŠธ๋ฆผ์€ ํŠน์ • ์š”์†Œ ํ˜•์‹์œผ๋กœ ์ด๋ฃจ์–ด์ง„ ์—ฐ์†๋œ ๊ฐ’ ์ง‘ํ•ฉ์˜ ์ธํ„ฐํŽ˜์ด์Šค์ด๋‹ค. ์ปฌ๋ ‰์…˜์˜ ์ฃผ์ œ๋Š” ๋ฐ์ดํ„ฐ๊ณ , ์ŠคํŠธ๋ฆผ์˜ ์ฃผ์ œ๋Š” ๊ณ„์‚ฐ์ด๋‹ค.
  • ์†Œ์Šค : ์ŠคํŠธ๋ฆผ์€ ์ปฌ๋ ‰์…˜, ๋ฐฐ์—ด, I/O ์ž์› ๋“ฑ์˜ ๋ฐ์ดํ„ฐ ์ œ๊ณต ์†Œ์Šค๋กœ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ์†Œ๋น„ํ•œ๋‹ค.
  • ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ์—ฐ์‚ฐ : filter, map, reduce, find, match, sort ๋“ฑ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐ์ž‘ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ŠคํŠธ๋ฆผ ์—ฐ์‚ฐ์€ ์ˆœ์ฐจ์ ์œผ๋กœ ๋˜๋Š” ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ํŒŒ์ดํ”„๋ผ์ด๋‹ : ๋Œ€๋ถ€๋ถ„์˜ ์ŠคํŠธ๋ฆผ ์—ฐ์‚ฐ์€ ์ŠคํŠธ๋ฆผ ์—ฐ์‚ฐ๋ผ๋ฆฌ ์—ฐ๊ฒฐํ•ด์„œ ์ปค๋‹ค๋ž€ ํŒŒ์ดํ”„๋ผ์ธ์„ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๋„๋ก ์ŠคํŠธ๋ฆผ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  • ๋‚ด๋ถ€ ๋ฐ˜๋ณต : ๋ฐ˜๋ณต์ž๋ฅผ ์ด์šฉํ•ด์„œ ๋ช…์‹œ์ ์œผ๋กœ ๋ฐ˜๋ณตํ•˜๋Š” ์ปฌ๋ ‰์…˜๊ณผ ๋‹ฌ๋ฆฌ ์ŠคํŠธ๋ฆผ์€ ๋‚ด๋ถ€ ๋ฐ˜๋ณต์ž๋ฅผ ์ด์šฉํ•œ๋‹ค.

 

์ŠคํŠธ๋ฆผ๊ณผ ์ปฌ๋ ‰์…˜

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

 

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

 

๋”ฑ ํ•œ ๋ฒˆ๋งŒ ํƒ์ƒ‰ํ•  ์ˆ˜ ์žˆ๋‹ค

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

 

์™ธ๋ถ€ ๋ฐ˜๋ณต๊ณผ ๋‚ด๋ถ€ ๋ฐ˜๋ณต

์ปฌ๋ ‰์…˜์— ๋ฐ˜๋ณต๋ฌธ์„ ์‚ฌ์šฉํ•˜๋ฉด ์™ธ๋ถ€ ๋ฐ˜๋ณต์ด ์ผ์–ด๋‚˜๊ณ , ์ŠคํŠธ๋ฆผ์„ ์‚ฌ์šฉํ•˜๋ฉด ๋‚ด๋ถ€ ๋ฐ˜๋ณต์ด ์ผ์–ด๋‚œ๋‹ค.

List<String> names = new ArrayList<>();
for (Dish dish : menu){
    names.add(dish.getName());
}

์™ธ๋ถ€ ๋ฐ˜๋ณต์€ ๋ฐฉ๋ฐ”๋‹ฅ์— ์–ด์งˆ๋Ÿฌ์ ธ ์žˆ๋Š” ์žฅ๋‚œ๊ฐ์„ ํ•˜๋‚˜์”ฉ ์ฐพ์•„ ์ƒ์ž์— ๋„ฃ๋Š” ๊ณผ์ •์„ ๋ฐ˜๋ณตํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™๋‹ค.

 

List<String> names = menu.stream()
    .map(Dish::getName)
    .collect(Collectors.toList());

๋‚ด๋ถ€ ๋ฐ˜๋ณต์€ ๋ฐฉ๋ฐ”๋‹ฅ์— ์–ด์งˆ๋Ÿฌ์ ธ ์žˆ๋Š” ์žฅ๋‚œ๊ฐ์„ ๋ชจ๋‘ ์ฐพ์•„ ํ•œ๊บผ๋ฒˆ์— ๋ชจ์•„์„œ ํ•œ ๋ฒˆ์— ์ƒ์ž์— ๋„ฃ๋Š” ๊ฒƒ๊ณผ ๊ฐ™๋‹ค. ๋ณ‘๋ ฌ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ , ์ตœ์ ํ™”ํ•˜์—ฌ ์ฒ˜๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์žฅ์ ์ด ์žˆ๋‹ค.

 

์ŠคํŠธ๋ฆผ์€ ๋‚ด๋ถ€ ๋ฐ˜๋ณต์„ ์‚ฌ์šฉํ•˜๋Š”๋ฐ, ์ด ๋ฐ˜๋ณต ๊ณผ์ •์€ ์šฐ๋ฆฌ๊ฐ€ ์‹ ๊ฒฝ์“ฐ์ง€ ์•Š์•„๋„ ๋œ๋‹ค. ํ•˜์ง€๋งŒ ์ด๋Ÿฐ ์ด์ ์„ ๋ˆ„๋ฆฌ๋ ค๋ฉด ๋ฐ˜๋ณต์„ ์ˆจ๊ฒจ์ฃผ๋Š” filter์™€ map ๊ฐ™์€ ์—ฐ์‚ฐ์ด ์ •์˜๋˜์–ด ์žˆ์–ด์•ผ ํ•˜๋Š”๋ฐ, ์ž๋ฐ” ์–ธ์–ด ์„ค๊ณ„์ž๋“ค์ด ์ด ์—ฐ์‚ฐ๋“ค์„ ์ œ์ž‘ํ•ด๋‘์—ˆ๋‹ค.

 

์ŠคํŠธ๋ฆผ ์—ฐ์‚ฐ

List<String> highCaloricDish = menu.stream()
    .filter(dish -> dish.getCalories() > 300)
    .map(Dish::getName)
    .limit(3)
    .collect(Collectors.toList());

์œ„ ์ฝ”๋“œ์—์„œ ์—ฐ์‚ฐ์„ ๋‘ ๊ทธ๋ฃน์œผ๋กœ ๋ถ„๋ฅ˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ์ค‘๊ฐ„ ์—ฐ์‚ฐ : filter, map, limit์™€ ๊ฐ™์ด ์„œ๋กœ ์—ฐ๊ฒฐ๋˜์–ด ํŒŒ์ดํ”„๋ผ์ธ์„ ํ˜•์„ฑํ•˜๋Š” ์—ฐ์‚ฐ, ์ŠคํŠธ๋ฆผ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  • ์ตœ์ข… ์—ฐ์‚ฐ : collect์™€ ๊ฐ™์ด ์ŠคํŠธ๋ฆผ์„ ๋‹ซ๋Š” ์—ฐ์‚ฐ

 

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

์ค‘๊ฐ„ ์—ฐ์‚ฐ์€ ์ŠคํŠธ๋ฆผ์„ ๋ฐ˜ํ™˜ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์—ฌ๋Ÿฌ ์ค‘๊ฐ„ ์—ฐ์‚ฐ์„ ์—ฐ๊ฒฐํ•ด์„œ ์งˆ์˜๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค. ์ค‘๊ฐ„ ์—ฐ์‚ฐ์˜ ์ค‘์š”ํ•œ ํŠน์ง•์€ ์ตœ์ข… ์—ฐ์‚ฐ์„ ์‹คํ–‰ํ•˜๊ธฐ ์ „๊นŒ์ง€ ์•„๋ฌด ์—ฐ์‚ฐ๋„ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š๋Š” ๊ฒŒ์œผ๋ฆ„์ด๋‹ค. ๋ชจ๋“  ์ค‘๊ฐ„ ์—ฐ์‚ฐ์„ ์ตœ์ข… ์—ฐ์‚ฐ์œผ๋กœ ํ•œ ๋ฒˆ์— ์ฒ˜๋ฆฌํ•œ๋‹ค.

List<String> highCaloricDish = menu.stream()
                .filter(dish -> {
                    System.out.println("filtering:" + dish.getName());
                    return dish.getCalories() > 300;
                })
                .map(dish -> {
                    System.out.println("mapping:" + dish.getName());
                    return dish.getName();
                })
                .limit(3)
                .collect(Collectors.toList());
filtering:pork
mapping:pork
filtering:beef
mapping:beef
filtering:chicken
mapping:chicken

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

 

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

์ตœ์ข… ์—ฐ์‚ฐ์€ ์ŠคํŠธ๋ฆผ ํŒŒ์ดํ”„๋ผ์ธ์—์„œ ๊ฒฐ๊ณผ๋ฅผ ๋„์ถœํ•œ๋‹ค. ๋ณดํ†ต ์ตœ์ข… ์—ฐ์‚ฐ์— ์˜ํ•ด List, Integer, void ๋“ฑ ์ŠคํŠธ๋ฆผ์ด ์•„๋‹Œ ๊ฒฐ๊ณผ๊ฐ€ ๋ฐ˜ํ™˜๋œ๋‹ค.

 

ํ•„ํ„ฐ๋ง

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

filter ๋ฉ”์†Œ๋“œ๋Š” ํ”„๋ ˆ๋””์ผ€์ดํŠธ๋ฅผ ์ธ์ˆ˜๋กœ ๋ฐ›์•„์„œ ํ”„๋ ˆ๋””์ผ€์ดํŠธ์™€ ์ผ์น˜ํ•˜๋Š” ๋ชจ๋“  ์š”์†Œ๋ฅผ ํฌํ•จํ•˜๋Š” ์ŠคํŠธ๋ฆผ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

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

 

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

์ŠคํŠธ๋ฆผ์€ ๊ณ ์œ  ์š”์†Œ๋กœ ์ด๋ฃจ์–ด์ง„ ์ŠคํŠธ๋ฆผ์„ ๋ฐ˜ํ™˜ํ•˜๋Š” distinct ๋ฉ”์†Œ๋“œ๋„ ์ง€์›ํ•œ๋‹ค.(๊ณ ์œ  ์—ฌ๋ถ€๋Š” ์ŠคํŠธ๋ฆผ์—์„œ ๋งŒ๋“  ๊ฐ์ฒด์˜ hashCode, equals๋กœ ๊ฒฐ์ •๋œ๋‹ค) 

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 2, 4, 1);
numbers.stream().filter(x -> x % 2 == 0).distinct().forEach(System.out::println);

 

์ŠคํŠธ๋ฆผ ์Šฌ๋ผ์ด์‹ฑ

ํ”„๋ ˆ๋””์ผ€์ดํŠธ๋ฅผ ์ด์šฉํ•œ ์Šฌ๋ผ์ด์‹ฑ

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

List<Dish> specialMenu = Arrays.asList(
        new Dish("seasonal fruit", true, 120, Dish.Type.OTHER),
        new Dish("prawns", false, 300, Dish.Type.FISH),
        new Dish("rice", true, 350, Dish.Type.OTHER),
        new Dish("chicken", false, 400, Dish.Type.MEAT),
        new Dish("french fries", true, 530, Dish.Type.OTHER)
);
List<Dish> slicedMenu1 = specialMenu.stream()
        .takeWhile((Dish dish) -> dish.getCalories() < 320)
        .collect(Collectors.toList());
        
List<Dish> slicedMenu2 = specialMenu.stream()
        .takeWhile((Dish dish) -> dish.getCalories() < 320)
        .collect(Collectors.toList());

 

takeWhile์€ ์ฒ˜์Œ๋ถ€ํ„ฐ ๊ฑฐ์ง“์ด ๋˜๋Š” ์ง€์  ์ „๊นŒ์ง€ ์กฐ๊ฑด์— ๋ถ€ํ•ฉํ•œ ๊ฒƒ๋“ค์„ ๋‚จ๊ธฐ๋Š” ๊ฒƒ์ด๊ณ , dropWhile์€ ์ฒ˜์Œ๋ถ€ํ„ฐ ๊ฑฐ์ง“์ด ๋˜๋Š” ์ง€์  ์ „๊นŒ์ง€ ์กฐ๊ฑด์— ๋ถ€ํ•ฉํ•˜๋Š” ๊ฒƒ๋“ค์„ ๋ฒ„๋ฆฌ๋Š” ๊ฒƒ์ด๋‹ค. ์ˆœ์ฐจ์ ์œผ๋กœ ์ง„ํ–‰๋œ๋‹ค๋Š” ๊ฒƒ์— ์œ ์˜ํ•ด์•ผ ํ•œ๋‹ค.

 

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

์ŠคํŠธ๋ฆผ์—์„œ ์ฃผ์–ด์ง„ ๊ฐ’ ์ดํ•˜์˜ ํฌ๊ธฐ๋ฅผ ๊ฐ–๋Š” ์ƒˆ๋กœ์šด ์ŠคํŠธ๋ฆผ์„ ๋ฐ˜ํ™˜ํ•˜๋Š” limit(n) ๋ฉ”์†Œ๋“œ๋ฅผ ์ง€์›ํ•œ๋‹ค. ์ •๋ ฌ ์—ฌ๋ถ€์™€ ๊ด€๊ณ„์—†์ด, ์ตœ๋Œ€ n๊ฐœ์˜ ์š”์†Œ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. 

List<Dish> dishes = specialMenu.stream()
    .filter(dish -> dish.getCalories() > 300)
    .limit(3)
    .collect(Collectors.toList());

 

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

์ŠคํŠธ๋ฆผ์—์„œ ์ฒ˜์Œ n๊ฐœ์˜ ์š”์†Œ๋ฅผ ์ œ์™ธํ•œ ์ŠคํŠธ๋ฆผ์„ ๋ฐ˜ํ™˜ํ•˜๋Š” skip(n) ๋ฉ”์†Œ๋“œ๋ฅผ ์ง€์›ํ•œ๋‹ค. 

List<Dish> dishes2 = menu.stream()
    .filter(d -> d.getCalories() > 300)
    .skip(2)
    .collect(Collectors.toList());

 

๋งคํ•‘

์ŠคํŠธ๋ฆผ์˜ ๊ฐ ์š”์†Œ์— ํ•จ์ˆ˜ ์ ์šฉํ•˜๊ธฐ

map ๋ฉ”์†Œ๋“œ๋Š” ์ธ์ˆ˜๋กœ ํ•จ์ˆ˜๋ฅผ ์ œ๊ณต๋ฐ›์•„ ์ œ๊ณต๋ฐ›์€ ํ•จ์ˆ˜๋ฅผ ์ŠคํŠธ๋ฆผ์˜ ๊ฐ ์š”์†Œ์— ์ ์šฉํ•œ ๊ฒฐ๊ณผ๊ฐ€ ์ƒˆ๋กœ์šด ์š”์†Œ๋กœ ๋งคํ•‘๋œ๋‹ค.('๊ณ ์นœ๋‹ค'๋ผ๋Š” ๊ฐœ๋…๋ณด๋‹ค๋Š” '์ƒˆ๋กœ์šด ๋ฒ„์ „์„ ๋งŒ๋“ ๋‹ค'๋ผ๋Š” ๊ฐœ๋…์— ๊ฐ€๊นŒ์šฐ๋ฏ€๋กœ '๋งคํ•‘'์ด๋ผ๋Š” ๋‹จ์–ด๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค).

List<String> dishNames = menu.stream().map(Dish::getName).collect(Collectors.toList());

์œ„ ์ฝ”๋“œ์™€ ๊ฐ™์ด, ๋ณธ๋ž˜๋Š” List<Dish>์˜€์ง€๋งŒ, List<String>์œผ๋กœ ๋ณ€ํ™˜๋˜์—ˆ๋‹ค.

 

์˜ˆ์ œ๋กœ ์š”๋ฆฌ๋ช…์˜ ๊ธธ์ด๋ฅผ ์•Œ๊ณ  ์‹ถ๋‹ค๋ฉด, ์•„๋ž˜์™€ ๊ฐ™์ด ๊ตฌํ˜„ํ•œ๋‹ค.

List<Integer> dishNameLengths = menu.stream()
                .map(Dish::getName)
                .map(String::length)
                .collect(Collectors.toList());

 

์ŠคํŠธ๋ฆผ ํ‰๋ฉดํ™”

๋‹จ์–ด๋กœ ์ด๋ฃจ์–ด์ง„ ๋ฆฌ์ŠคํŠธ๋กœ๋ถ€ํ„ฐ ๊ณ ์œ  ๋ฌธ์ž๋กœ ์ด๋ฃจ์–ด์ง„ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ ค๊ณ  ํ•˜๋ฉด, ๋‹จ์–ด๋ฅผ ๋ชจ๋‘ ๋ถ„๋ฆฌํ•˜๊ณ , ์ค‘๋ณต์„ ์—†์• ๋ฉด ๊ฒฐ๊ณผ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค. List<String>์ด ๋‚˜์™€์•ผ ํ•˜๋Š”๋ฐ, List<String[]>์ด ๋‚˜์˜จ๋‹ค.

List<String[]> characters = words.stream()
    .map(word -> word.split(""))
    .distinct()
    .collect(Collectors.toList());

 

๋ฐฐ์—ด์„ ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋งŒ๋“œ๋Š” Arrays.stream์„ map์œผ๋กœ ์ ์šฉํ•ด๋ณด์ž.

words.stream()
    .map(word -> word.split(""))
    .map(Arrays::stream)
    .distinct()
    .collect(Collectors.toList())

List<Stream<String>>์ด ๋˜๋ฉด์„œ ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋˜์ง€ ์•Š๋Š”๋‹ค.

 

flatMap์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค. 

words.stream()
    .map(word -> word.split(""))
    .flatMap(Arrays::stream)
    .distinct()
    .collect(Collectors.toList());

flatMap์€ ๊ฐ ๋ฐฐ์—ด์„ ์ŠคํŠธ๋ฆผ์ด ์•„๋‹ˆ๋ผ ์ŠคํŠธ๋ฆผ์˜ ์ฝ˜ํ…์ธ ๋กœ ๋งคํ•‘ํ•œ๋‹ค. ์ฆ‰, flatMap์€ ํ•˜๋‚˜์˜ ํ‰๋ฉดํ™”๋œ ์ŠคํŠธ๋ฆผ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

 

์ œ๊ณฑ ๋ฆฌ์ŠคํŠธ ๋งŒ๋“ค๊ธฐ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> squares = list.stream().map(x -> x * x).collect(Collectors.toList());

 

๋‘ ๋ฆฌ์ŠคํŠธ์˜ ์ˆ˜๋กœ ์ด๋ฃจ์–ด์ง„ ์ˆซ์ž ์Œ์˜ ๋ฆฌ์ŠคํŠธ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

List<Integer> numbers1 = Arrays.asList(1, 2, 3);
List<Integer> numbers2 = Arrays.asList(4, 5);

List<int[]> pairs = numbers1.stream()
        .flatMap(i -> numbers2.stream()
                .filter(j -> (i + j) % 3 == 0)
                .map(j -> new int[]{i, j})
        )
        .collect(Collectors.toList());

filter, map ๋ชจ๋‘ stream์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค๋Š” ๊ฒƒ์— ์œ ์˜ํ•˜์ž. flatMap์ด ์•„๋‹ˆ๋ผ map์ผ ๊ฒฝ์šฐ, List<Stream<int[]>>๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

 

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

anyMatch

ํ”„๋ ˆ๋””์ผ€์ดํŠธ๊ฐ€ ์ฃผ์–ด์ง„ ์ŠคํŠธ๋ฆผ์—์„œ ์ ์–ด๋„ ํ•œ ์š”์†Œ์™€ ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์ธํ•  ๋•Œ, anyMatch ๋ฉ”์†Œ๋“œ๋ฅผ ์ด์šฉํ•œ๋‹ค.

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

anyMatch๋Š” ๋ถˆ๋ฆฌ์•ˆ์„ ๋ฐ˜ํ™˜ํ•˜๋ฏ€๋กœ, ์ตœ์ข… ์—ฐ์‚ฐ์ด๋‹ค.

 

allMatch 

์ŠคํŠธ๋ฆผ์˜ ๋ชจ๋“  ์š”์†Œ๊ฐ€ ์ฃผ์–ด์ง„ ํ”„๋ ˆ๋””์ผ€์ดํŠธ์™€ ์ผ์น˜ํ•˜๋Š”์ง€ ๊ฒ€์‚ฌํ•œ๋‹ค.

boolean isHealthy = menu.stream().allMatch(dish -> dish.getCalories() < 300);

 

noneMatch

์ŠคํŠธ๋ฆผ์˜ ๋ชจ๋“  ์š”์†Œ๊ฐ€ ์ฃผ์–ด์ง„ ํ”„๋ ˆ๋””์ผ€์ดํŠธ์™€ ์ผ์น˜ํ•˜์ง€ ์•Š๋Š”์ง€ ํ™•์ธํ•œ๋‹ค. allMatch์™€ ๋ฐ˜๋Œ€ ์—ฐ์‚ฐ์ด๋‹ค.

boolean isNotHealthy = menu.stream().noneMatch(dish -> dish.getCalories() >= 300);

 

anyMatch, allMatch, noneMatch๋Š” ์‡ผํŠธ ์„œํ‚ท ๊ธฐ๋ฒ•์„ ์ˆ˜ํ–‰ํ•œ๋‹ค. ์ฆ‰, ์ „์ฒด ์ŠคํŠธ๋ฆผ์„ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š์•„๋„ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, anyMatch์˜ ๊ฒฝ์šฐ, ์กฐ๊ฑด์— ๋งž๋Š” ๊ฒƒ์ด ํ•˜๋‚˜๋ผ๋„ ์žˆ์œผ๋ฉด ๋ฐ”๋กœ ์ฐธ, allMatch์™€ noneMatch์˜ ๊ฒฝ์šฐ, ์กฐ๊ฑด์— ๋งž์ง€ ์•Š๋Š” ๊ฒƒ์ด ํ•˜๋‚˜๋ผ๋„ ์žˆ์œผ๋ฉด ๋ฐ”๋กœ ๊ฑฐ์ง“์ด ๋œ๋‹ค.

 

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

findAny ๋ฉ”์†Œ๋“œ๋Š” ํ˜„์žฌ ์ŠคํŠธ๋ฆผ์—์„œ ์ž„์˜์˜ ์š”์†Œ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์ŠคํŠธ๋ฆผ ํŒŒ์ดํ”„๋ผ์ธ์€ ์‡ผํŠธ ์„œํ‚ท์„ ์ด์šฉํ•ด์„œ ๊ฒฐ๊ณผ๋ฅผ ์ฐพ๋Š” ์ฆ‰์‹œ ์‹คํ–‰์„ ์ข…๋ฃŒํ•œ๋‹ค.

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

 

Optional์ด๋ž€?

Optional<T> ํด๋ž˜์Šค๋Š” ๊ฐ’์˜ ์กด์žฌ๋‚˜ ๋ถ€์žฌ ์—ฌ๋ถ€๋ฅผ ํ‘œํ˜„ํ•˜๋Š” ์ปจํ…Œ์ด๋„ˆ ํด๋ž˜์Šค์ด๋‹ค. ์œ„ ์ฝ”๋“œ์—์„œ findAny๊ฐ€ ์•„๋ฌด ์š”์†Œ๋„ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋‹ค. null์€ ์‰ฝ๊ฒŒ ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๊ธฐ ๋•Œ๋ฌธ์— ์ž๋ฐ”8 ์„ค๊ณ„์ž๋Š” ์•ˆ์ „ํ•˜๊ฒŒ  Optional<T>๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค.

  • isPresent() : Optional์ด ๊ฐ’์„ ํฌํ•จํ•˜๋ฉด true, ์•„๋‹ˆ๋ผ๋ฉด false ๋ฐ˜ํ™˜
  • ifPresent()(Consumer<T> block) : ๊ฐ’์ด ์žˆ์œผ๋ฉด ๋ฐ˜ํ™˜ ํ˜•์‹ void์ธ ๋ธ”๋ก์„ ์‹คํ–‰ํ•œ๋‹ค.
  • T get() : ๊ฐ’์ด ์กด์žฌํ•˜๋ฉด ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๊ณ , ๊ฐ’์ด ์—†์œผ๋ฉด NoSuchElementException์„ ์ผ์œผํ‚จ๋‹ค.
  • T orElse(T other) : ๊ฐ’์ด ์žˆ์œผ๋ฉด ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๊ณ , ๊ฐ’์ด ์—†์œผ๋ฉด other์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. 
menu.stream()
    .filter(Dish::isVegetarian)
    .findAny()
    .ifPresent(dish -> System.out.println(dish.getName()));

 

 

์ฒซ ๋ฒˆ์งธ ์š”์†Œ ์ฐพ๊ธฐ

์ŠคํŠธ๋ฆผ์—์„œ ๋…ผ๋ฆฌ์ ์ธ ์•„์ดํ…œ ์ˆœ์„œ๊ฐ€ ์ •ํ•ด์ ธ ์žˆ์„ ๋•Œ, ์ฒซ ๋ฒˆ์งธ ์š”์†Œ๋ฅผ ์ฐพ์œผ๋ ค๋ฉด, findFirst()๋ฅผ ์ด์šฉํ•œ๋‹ค.

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

 

findFirst์˜ ์กด์žฌ ์ด์œ 

๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์ผ ๋•Œ, ์‹คํ–‰์— ์žˆ์–ด์„œ ์ฒซ ๋ฒˆ์งธ ์š”์†Œ๋ฅผ ์ฐพ๊ธฐ๊ฐ€ ์–ด๋ ต๋‹ค. ๋”ฐ๋ผ์„œ ์š”์†Œ ๋ฐ˜ํ™˜ ์ˆœ์„œ๊ฐ€ ์ƒ๊ด€์ด ์—†๋‹ค๋ฉด, findFirst๋ณด๋‹ค๋Š” ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์—์„œ๋Š” ์ œ์•ฝ์ด ์ ์€ findAny๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

 

๋ฆฌ๋“€์‹ฑ

๋ชจ๋“  ์ŠคํŠธ๋ฆผ ์š”์†Œ๋ฅผ ์ฒ˜๋ฆฌํ•ด์„œ ๊ฐ’์œผ๋กœ ๋„์ถœํ•˜๋Š” ๊ฒƒ์„ ๋ฆฌ๋“€์‹ฑ ์—ฐ์‚ฐ์ด๋ผ๊ณ  ํ•œ๋‹ค.

 

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

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

int sum = numbers.stream().reduce(0, (x, y) -> x + y);

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

 

 

reduce๋ฅผ ์ด์šฉํ•˜์—ฌ ์ŠคํŠธ๋ฆผ์—์„œ ์š”์†Œ์˜ ํ•ฉ์„ ๊ตฌํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ๋•Œ, reduce๋Š” ๋‘ ๊ฐœ์˜ ์ธ์ˆ˜๋ฅผ ๊ฐ–๋Š”๋‹ค.

  • ์ดˆ๊นƒ๊ฐ’ 0
  • ๋‘ ์š”์†Œ๋ฅผ ์กฐํ•ฉํ•ด์„œ ์ƒˆ๋กœ์šด ๊ฐ’์„ ๋งŒ๋“œ๋Š” BinaryOperator<T>

 

Optional<Integer> sum = numbers.stream().reduce((x, y) -> x + y);

์ดˆ๊นƒ๊ฐ’์„ ์ง€์ •ํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ, stream์— ์•„๋ฌด ์š”์†Œ๋„ ์—†์„ ๊ฒฝ์šฐ๋ฅผ ๋Œ€๋น„ํ•˜์—ฌ Optional ๊ฐ์ฒด๊ฐ€ ๋ฐ˜ํ™˜๋œ๋‹ค.

 

์ตœ๋Œ“๊ฐ’๊ณผ ์ตœ์†Ÿ๊ฐ’

reduce๋ฅผ ์ด์šฉํ•ด์„œ ์ŠคํŠธ๋ฆผ์—์„œ ์ตœ๋Œ“๊ฐ’๊ณผ ์ตœ์†Ÿ๊ฐ’์„ ์ฐพ์„ ์ˆ˜ ์žˆ๋‹ค. ์ด ๋•Œ, reduce๋Š” ๋‘ ์ธ์ˆ˜๋ฅผ ๋ฐ›๋Š”๋‹ค.

  • ์ดˆ๊นƒ๊ฐ’
  • ์ŠคํŠธ๋ฆผ์˜ ๋‘ ์š”์†Œ๋ฅผ ํ•ฉ์ณ์„œ ํ•˜๋‚˜์˜ ๊ฐ’์œผ๋กœ ๋งŒ๋“œ๋Š” ๋ฐ ์‚ฌ์šฉํ•  ๋žŒ๋‹ค
Optional<Integer> max = numbers.stream().reduce(Integer::max);
Optional<Integer> max = numbers.stream().reduce((x, y) -> x > y ? x : y);

 

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

reduce๋ฅผ ์ด์šฉํ•˜๋ฉด, ๋‚ด๋ถ€ ๋ฐ˜๋ณต์ด ์ถ”์ƒํ™”๋˜๋ฉด์„œ, ๋‚ด๋ถ€ ๊ตฌํ˜„์—์„œ ๋ณ‘๋ ฌ๋กœ reduce๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.

 

์‹ค์ „ ์—ฐ์Šต

public class Trader {
    private final String name;
    private final String city;

    public Trader(String name, String city) {
        this.name = name;
        this.city = city;
    }

    public String getName() {
        return name;
    }

    public String getCity() {
        return city;
    }

    @Override
    public String toString() {
        return "Trader:" + this.name + " in " + this.city;
    }
}
public class Transaction {
    private final Trader trader;
    private final int year;
    private final int value;

    public Transaction(Trader trader, int year, int value) {
        this.trader = trader;
        this.year = year;
        this.value = value;
    }

    public Trader getTrader() {
        return trader;
    }

    public int getYear() {
        return year;
    }

    public int getValue() {
        return value;
    }

    @Override
    public String toString() {
        return "{" +
                "trader=" + trader +
                ", year=" + year +
                ", value=" + value +
                '}';
    }
}
Trader raoul = new Trader("Raoul", "Cambridge");
Trader mario = new Trader("Mario", "Milan");
Trader alan = new Trader("Alan", "Cambridge");
Trader brian = new Trader("Brian", "Cambridge");

List<Transaction> transactions = Arrays.asList(
        new Transaction(brian, 2011, 300),
        new Transaction(raoul, 2012, 1000),
        new Transaction(raoul, 2011, 400),
        new Transaction(mario, 2012, 710),
        new Transaction(mario, 2012, 700),
        new Transaction(alan, 2012, 950)
)

 

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

List<Transaction> tr2011 = transactions.stream()
    .filter(t -> t.getYear() == 2011)
    .sorted(Comparator.comparing(Transaction::getValue))
    .collect(Collectors.toList());

 

๊ฑฐ๋ž˜์ž๊ฐ€ ๊ทผ๋ฌดํ•˜๋Š” ๋ชจ๋“  ๋„์‹œ๋ฅผ ์ค‘๋ณต ์—†์ด ๋‚˜์—ดํ•˜์‹œ์˜ค.

List<String> cities = transactions.stream()
    .map(t -> t.getTrader().getCity())
    .distinct()
    .collect(Collectors.toList());

 

์ผ€์ž„๋ธŒ๋ฆฌ์ง€์—์„œ ๊ทผ๋ฌดํ•˜๋Š” ๋ชจ๋“  ๊ฑฐ๋ž˜์ž๋ฅผ ์ฐพ์•„์„œ ์ด๋ฆ„์ˆœ์œผ๋กœ ์ •๋ ฌํ•˜์‹œ์˜ค.

List<Trader> traders = transactions.stream()
    .map(Transaction::getTrader)
    .filter(trader -> trader.getCity().equals("Cambridge"))
    .distinct()
    .sorted(Comparator.comparing(Trader::getName))
    .collect(Collectors.toList());

 

๋ชจ๋“  ๊ฑฐ๋ž˜์ž์˜ ์ด๋ฆ„์„ ์•ŒํŒŒ๋ฒณ ์ˆœ์œผ๋กœ ์ •๋ ฌํ•ด์„œ ๋ฐ˜ํ™˜ํ•˜์‹œ์˜ค.

String traderStr = transactions.stream()
    .map(Transaction::getTrader)
    .map(Trader::getName)
    .distinct()
    .sorted()
    .reduce("", (n1, n2) -> n1 + " " + n2);

 

๋ฐ€๋ผ๋…ธ์— ๊ฑฐ๋ž˜์ž๊ฐ€ ์žˆ๋Š”๊ฐ€?

boolean milanBased = transactions.stream()
    .anyMatch(transaction -> transaction.getTrader().getCity().equals("Milan"));

 

์ผ€์ž„๋ธŒ๋ฆฌ์ง€์— ๊ฑฐ์ฃผํ•˜๋Š” ๊ฑฐ๋ž˜์ž์˜ ๋ชจ๋“  ํŠธ๋žœ์žญ์…˜๊ฐ’์„ ์ถœ๋ ฅํ•˜์‹œ์˜ค.

transactions.stream()
                .filter(transaction -> transaction.getTrader().getCity().equals("Cambridge"))
                .map(Transaction::getValue)
                .forEach(System.out::println);

 

์ „์ฒด ํŠธ๋žœ์žญ์…˜ ์ค‘ ์ตœ๋Œ“๊ฐ’์€ ์–ผ๋งˆ์ธ๊ฐ€?

Optional<Integer> maxValue = transactions.stream()
                .map(Transaction::getValue)
                .reduce(Integer::max);

 

์ „์ฒด ํŠธ๋žœ์žญ์…˜ ์ค‘ ์ตœ์†Ÿ๊ฐ’์€ ์–ผ๋งˆ์ธ๊ฐ€?

Optional<Integer> minValue = transactions.stream()
                .map(Transaction::getValue)
                .reduce(Integer::min);

 

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

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

reduce ๋ฉ”์†Œ๋“œ๋กœ ํ•ฉ์„ ๊ตฌํ•  ๋•Œ, ์›๋ž˜ calrories๋Š” int์ด๋ฏ€๋กœ, Integer๋กœ ๋ฐ•์‹ฑํ•˜๊ณ , ํ•ฉ๊ณ„๋ฅผ ๊ณ„์‚ฐํ•˜๊ธฐ ์œ„ํ•ด ๊ธฐ๋ณธํ˜•์œผ๋กœ ์–ธ๋ฐ•์‹ฑ ํ•˜๋Š” ๋น„์šฉ์ด ํฌํ•จ๋œ๋‹ค. ์ŠคํŠธ๋ฆผ์— sum ๋ฉ”์†Œ๋“œ๊ฐ€ ์žˆ๋‹ค๋ฉด ์ข‹๊ฒ ์ง€๋งŒ, sum ๋ฉ”์†Œ๋“œ๋Š” ์—†๋‹ค. Stream<Dish>์ผ ๊ฒฝ์šฐ, ๋ฌด์—‡์„ ํ•ฉํ•ด์•ผ ํ•˜๋Š”์ง€ ์•Œ ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. 

 

๊ธฐ๋ณธํ˜• ํŠนํ™” ์ŠคํŠธ๋ฆผ

์ž๋ฐ” 8์—์„œ๋Š” IntStream, DoubleStream, LongStream๊ณผ ๊ฐ™์€ ๊ธฐ๋ณธํ˜• ํŠนํ™” ์ŠคํŠธ๋ฆผ์„ ์ œ๊ณตํ•œ๋‹ค. ๊ฐ๊ฐ์˜ ์ธํ„ฐํŽ˜์ด์Šค๋Š” sum, max์™€ ๊ฐ™์ด ์ˆซ์ž ๊ด€๋ จ ๋ฆฌ๋“€์‹ฑ ์—ฐ์‚ฐ ์ˆ˜ํ–‰ ๋ฉ”์†Œ๋“œ๋ฅผ ์ œ๊ณตํ•œ๋‹ค. ๋˜ํ•œ, ํ•„์š”ํ•  ๋•Œ ๋‹ค์‹œ ๊ฐ์ฒด ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ณต์›ํ•˜๋Š” ๊ธฐ๋Šฅ๋„ ์ œ๊ณตํ•œ๋‹ค. ํŠนํ™” ์ŠคํŠธ๋ฆผ์€ ์˜ค์ง ๋ฐ•์‹ฑ ๊ณผ์ •์—์„œ ์ผ์–ด๋‚˜๋Š ํšจ์œจ์„ฑ๊ณผ ๊ด€๋ จ์ด ์žˆ๊ณ , ์ถ”๊ฐ€ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜์ง€๋Š” ์•Š๋Š”๋‹ค.

 

์ˆซ์ž ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋งคํ•‘

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

 

mapToInt ๋ฉ”์†Œ๋“œ๋กœ Stream<Integer>๊ฐ€ ์•„๋‹Œ ๊ธฐ๋ณธํ˜• ํŠนํ™” ์ŠคํŠธ๋ฆผ์ธ IntStream์„ ๋ฐ˜ํ™˜ํ•˜๊ณ , ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. sum, min, max, average ๋“ฑ๋„ ์ด์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

๊ฐ์ฒด ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ณต์›ํ•˜๊ธฐ

boxed ๋ฉ”์†Œ๋“œ๋ฅผ ์ด์šฉํ•ด์„œ ๊ธฐ๋ณธํ˜• ํŠนํ™” ์ŠคํŠธ๋ฆผ์„ ์ผ๋ฐ˜ ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

IntStream intStream = menu.stream().mapToInt(Dish::getCalories);
Stream<Integer> boxed = intStream.boxed();

 

๊ธฐ๋ณธ๊ฐ’ : OptionalInt

sum์ด ์ŠคํŠธ๋ฆผ ์š”์†Œ๊ฐ€ ์—†์œผ๋ฉด, 0์„ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ๊ณผ ๋‹ฌ๋ฆฌ, max๋Š” Optional ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์ŠคํŠธ๋ฆผ ์š”์†Œ๊ฐ€ ์—†์„ ๋•Œ 0์„ ๋ฐ˜ํ™˜ํ•  ๊ฒฝ์šฐ, ์ตœ๋Œ“๊ฐ’์ด 0์ด๋ผ๋Š” ๊ฒƒ์ธ์ง€ ์—†์–ด์„œ 0์ธ์ง€ ์•Œ ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

OptionalInt maxCalories = menu.stream()
    .mapToInt(Dish::getCalories)
    .max();
    
int max = maxCalories.orElse(-999999);

 

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

ํŠน์ • ๋ฒ”์œ„์˜ ์ˆซ์ž๋ฅผ ์ด์šฉํ•ด์•ผ ํ•˜๋Š” ์ƒํ™ฉ์ผ ๋•Œ, range์™€ rangeClosed๋ผ๋Š” ๋‘ ๊ฐ€์ง€ ์ •์  ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊ธฐ๋ณธํ˜• ํŠนํ™” ์ŠคํŠธ๋ฆผ์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค. range๋Š” ๋‘ ๋ฒˆ์งธ ์ธ์ˆ˜์ธ ์ข…๋ฃŒ๊ฐ’์ด ํฌํ•จ๋˜์ง€ ์•Š๋Š”๋‹ค.

IntStream evenNumbers = IntStream.range(1, 100).filter(i->i%2==0);
System.out.println(evenNumbers.count());

IntStream evenNumbers2 = IntStream.rangeClosed(1, 100).filter(i->i%2==0);
System.out.println(evenNumbers2.count());

 

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

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

์ž„์˜์˜ ๊ฐ’์„ ์ธ์ˆ˜๋กœ ๋ฐ›๋Š” ์ •์  ๋ฉ”์†Œ๋“œ Stream.of๋ฅผ ์ด์šฉํ•ด์„œ ์ŠคํŠธ๋ฆผ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

Stream<String> stream = Stream.of("Modern", "Java", "In", "Action");

 

๋ฐฐ์—ด๋กœ ์ŠคํŠธ๋ฆผ ๋งŒ๋“ค๊ธฐ

๋ฐฐ์—ด์„ ์ธ์ˆ˜๋กœ ๋ฐ›๋Š” ์ •์  ๋ฉ”์†Œ๋“œ Arrays.stream์„ ์ด์šฉํ•ด์„œ ์ŠคํŠธ๋ฆผ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

int[] numbers = {1,2,3,4,5};
IntStream stream = Arrays.stream(numbers);

 

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

ํŒŒ์ผ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋“ฑ์˜ I/O ์—ฐ์‚ฐ์— ์‚ฌ์šฉํ•˜๋Š” ์ž๋ฐ”์˜ ๋น„๋ธ”๋ก I/O API๋„ ์ŠคํŠธ๋ฆผ API๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์—…๋ฐ์ดํŠธ ๋˜์—ˆ๋‹ค. java.nio.file.Files์˜ ๋งŽ์€ ์ •์  ๋ฉ”์†Œ๋“œ๊ฐ€ ์ŠคํŠธ๋ฆผ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด Files.lines๋Š” ์ฃผ์–ด์ง„ ํŒŒ์ผ์˜ ํ–‰ ์ŠคํŠธ๋ฆผ์„ ๋ฌธ์ž์—ด๋กœ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

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){

}

 

ํ•จ์ˆ˜๋กœ ๋ฌดํ•œ ์ŠคํŠธ๋ฆผ ๋งŒ๋“ค๊ธฐ

์ŠคํŠธ๋ฆผ API๋Š” ํ•จ์ˆ˜์—์„œ ์ŠคํŠธ๋ฆผ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋Š” ๋‘ ์ •์  ๋ฉ”์†Œ๋“œ Stream.iterate์™€ Stream.generate๋ฅผ ์ œ๊ณตํ•œ๋‹ค. ๋‘ ์—ฐ์‚ฐ์„ ์ด์šฉํ•ด์„œ ๋ฌดํ•œ ์ŠคํŠธ๋ฆผ, ์ฆ‰ ํฌ๊ธฐ๊ฐ€ ๊ณ ์ •๋˜์ง€ ์•Š์€ ์ŠคํŠธ๋ฆผ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค. ๋ฌดํ•œํ•œ ๊ฐ’์„ ์›ํ•˜์ง€ ์•Š์„ ๋•Œ๋Š” limit๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

 

iterate ๋ฉ”์†Œ๋“œ

Stream.iterate(new int[]{0,1}, t-> new int[]{t[1], t[0]+t[1]})
                .limit(20)
                .forEach(t-> System.out.println("(" + t[0] + "," + t[1] + ")"));

 

์ž๋ฐ”9์˜ iterate๋Š” ๋‘ ๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ํ”„๋ ˆ๋””์ผ€์ดํŠธ๋ฅผ ์ง€์›ํ•œ๋‹ค. ๋”ฐ๋ผ์„œ, ์ถ”๊ฐ€ ๋ฉ”์†Œ๋“œ๋ฅผ ๋ถ™์ด์ง€ ์•Š์•„๋„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

IntStream.iterate(0, n->n<100, n->n+4)
                .forEach(System.out::println);

 

์ด๊ฒƒ์„ ๋‹ค๋ฅด๊ฒŒ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์€ ๋ฌด์—‡์ผ๊นŒ? filter๋ฅผ ์ƒ๊ฐํ•  ์ˆœ ์žˆ์ง€๋งŒ, iterate๊ฐ€ ๋ฌดํ•œ ์ŠคํŠธ๋ฆผ์„ ์ƒ์„ฑํ•˜๋ฉด, filter๋Š” ๋ฌดํ•œ ๊ฐœ์˜ ์š”์†Œ๋ฅผ ๋ด์•ผํ•˜๋ฏ€๋กœ ๋๋‚˜์ง€ ์•Š๋Š”๋‹ค.

 

takeWhile์„ ์ด์šฉํ•˜๋Š” ๊ฒƒ์ด ๋ฐ”๋žŒ์งํ•˜๋‹ค.

IntStream.iterate(0, n->n+4)
                .takeWhile(n->n<100)
                .forEach(System.out::println);

 

generate ๋ฉ”์†Œ๋“œ

๋ฌดํ•œ ์ŠคํŠธ๋ฆผ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์ง€๋งŒ, iterate์™€ ๋‹ฌ๋ฆฌ ์ƒ์‚ฐ๋œ ๊ฐ ๊ฐ’์„ ์—ฐ์†์ ์œผ๋กœ ๊ณ„์‚ฐํ•˜์ง€๋Š” ์•Š๋Š”๋‹ค. generate๋Š” Supplier<T>๋ฅผ ์ธ์ˆ˜๋กœ ๋ฐ›์•„์„œ ์ƒˆ๋กœ์šด ๊ฐ’์„ ์ƒ์‚ฐํ•œ๋‹ค.

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

 

 

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

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

 

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

 

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

 

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

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

collect์—์„œ๋Š” ๋ฆฌ๋“€์‹ฑ ์—ฐ์‚ฐ์„ ์ด์šฉํ•ด์„œ ์ŠคํŠธ๋ฆผ์˜ ๊ฐ ์š”์†Œ๋ฅผ ๋ฐฉ๋ฌธํ•˜๋ฉด์„œ ์ปฌ๋ ‰ํ„ฐ๊ฐ€ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•œ๋‹ค.

 

๋ณดํ†ต ํ•จ์ˆ˜๋ฅผ ์š”์†Œ๋กœ ๋ณ€ํ™˜ํ•  ๋•Œ๋Š” ์ปฌ๋ ‰ํ„ฐ๋ฅผ ์ ์šฉํ•˜๋ฉฐ ์ตœ์ข… ๊ฒฐ๊ณผ๋ฅผ ์ €์žฅํ•˜๋Š” ์ž๋ฃŒ๊ตฌ์กฐ์— ๊ฐ’์„ ๋ˆ„์ ํ•œ๋‹ค.

 

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

Collectors์—์„œ ์ œ๊ณตํ•˜๋Š” ๋ฉ”์†Œ๋“œ์˜ ๊ธฐ๋Šฅ์€ ํฌ๊ฒŒ ์„ธ ๊ฐ€์ง€๋กœ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ๋‹ค.

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

 

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

๊ฐœ์ˆ˜ ๊ตฌํ•˜๊ธฐ

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

๊ธฐ๋ณธํ˜• ํŠนํ™” ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ฐ”๊พธ๋Š” ์ž‘์—…์„ ํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค.

 

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

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

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

๋น„๊ต ๋Œ€์ƒ์„ ์ง€์ •ํ•˜์—ฌ Comparator๋ฅผ ๋งŒ๋“ค๊ณ , maxBy์— ์ „๋‹ฌํ•œ๋‹ค. menu๊ฐ€ ๋น„์–ด์žˆ๋‹ค๋ฉด, ๋ฐ˜ํ™˜๋˜๋Š” ๊ฒƒ์ด ์—†๊ธฐ ๋•Œ๋ฌธ์— ์•ˆ์ „ํ•˜๊ฒŒ Optional๋กœ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

 

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

ํ•ฉ๊ณ„

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

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

 

ํ‰๊ท 

int ์š”์†Œ๋“ค์„ ์ด์šฉํ•˜์—ฌ double ํƒ€์ž…์˜ ํ‰๊ท ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

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

 

์š”์†Œ ์ˆ˜, ํ•ฉ๊ณ„, ํ‰๊ท , ์ตœ๋Œ“๊ฐ’, ์ตœ์†Ÿ๊ฐ’

IntSummaryStatistics menuStatistics = menu.stream().collect(Collectors.summarizingInt(Dish::getCalories));
System.out.println(menuStatistics);	// IntSummaryStatistics{count=9, sum=4200, min=120, average=466.666667, max=800}

 

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

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

String menuNames = menu.stream().map(Dish::getName).collect(Collectors.joining(", "));
// pork, beef, chicken, french fries, rice, season fruit, pizza, prawns, salmon

 

๊ทธ๋ฃนํ™”

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

Map<Dish.Type, List<Dish>> dishesByType = menu.stream()
        .collect(Collectors.groupingBy(Dish::getType));
System.out.println(dishesByType);
// {OTHER=[french fries, rice, season fruit, pizza], MEAT=[pork, beef, chicken], FISH=[prawns, salmon]}

 

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

 

๋‹จ์ˆœํ•œ ์†์„ฑ ์ ‘๊ทผ์ž ๋Œ€์‹  ๋” ๋ณต์žกํ•œ ๋ถ„๋ฅ˜ ๊ธฐ์ค€์ด ํ•„์š”ํ•œ ์ƒํ™ฉ์—์„œ๋Š” ๋ฉ”์†Œ๋“œ ์ฐธ์กฐ๋ฅผ ๋ถ„๋ฅ˜ ํ•จ์ˆ˜๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค. ์ด๋Ÿด ๋•Œ๋Š” ๋žŒ๋‹ค ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜์—ฌ ๊ทธ๋ฃนํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค.

public enum CaloricLevel {DIET, NORMAL, FAT};

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

 

๊ทธ๋ฃนํ™”๋œ ์š”์†Œ ์กฐ์ž‘

์š”์†Œ๋ฅผ ๊ทธ๋ฃนํ™”ํ•œ ๋‹ค์Œ์—๋Š” ๊ฐ ๊ฒฐ๊ณผ ๊ทธ๋ฃน์˜ ์š”์†Œ๋ฅผ ์กฐ์ž‘ํ•˜๋Š” ์—ฐ์‚ฐ์ด ํ•„์š”ํ•˜๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, 500์นผ๋กœ๋ฆฌ๊ฐ€ ๋„˜๋Š” ์š”๋ฆฌ๋งŒ ๊ทธ๋ฃนํ™”ํ•œ๋‹ค๊ณ  ํ•  ๋•Œ, ํ•„ํ„ฐ๋ง ํ›„์— ๊ทธ๋ฃนํ™”๋ฅผ ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒฐ๊ณผ๊ฐ€ ์ดˆ๋ž˜๋œ๋‹ค.

Map<Dish.Type, List<Dish>> collect = menu.stream()
        .filter(dish -> dish.getCalories() > 500)
        .collect(Collectors.groupingBy(Dish::getType));
System.out.println(collect);
// {OTHER=[french fries, pizza], MEAT=[pork, beef]}

๋ฌธ์ œ๋Š” FISH์— ํ•ด๋‹นํ•˜๋Š” ๊ฒƒ๋“ค์€ 500์นผ๋กœ๋ฆฌ ์ดํ•˜์ด๊ธฐ ๋•Œ๋ฌธ์—, ํ‚ค ์ž์ฒด๊ฐ€ ์‚ฌ๋ผ์ง„๋‹ค.

 

groupingBy ๋ฉ”์†Œ๋“œ๋Š” ๋‘ ๋ฒˆ์งธ ์ธ์ˆ˜๋ฅผ ๊ฐ–๋„๋ก ์˜ค๋ฒ„๋กœ๋“œํ•ด ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ฒŒ ํ•ด์ค€๋‹ค.

Map<Dish.Type, List<Dish>> caloricDishesByType = menu.stream()
        .collect(Collectors.groupingBy(Dish::getType, Collectors.filtering(dish -> dish.getCalories() > 500, Collectors.toList())));
System.out.println(caloricDishesByType);
// {FISH=[], MEAT=[pork, beef], OTHER=[french fries, pizza]}

 

Map<String, List<String>> disTags์—์„œ ๊ฐ ํ˜•์‹์˜ ์š”๋ฆฌ์˜ ํƒœ๊ทธ๋ฅผ ๊ฐ„ํŽธํ•˜๊ฒŒ ์ถ”์ถœํ•  ์ˆ˜ ์žˆ๋‹ค.

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

 

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

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

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;
            }))));
            
// {FISH={DIET=[prawns], NORMAL=[salmon]}, MEAT={DIET=[chicken], FAT=[pork], NORMAL=[beef]}, OTHER={DIET=[rice, season fruit], NORMAL=[french fries, pizza]}}

 

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

groupingBy์˜ ๋‘ ๋ฒˆ์งธ ์ธ์ˆ˜๋กœ ๋„˜๊ฒจ์ฃผ๋Š” ์ปฌ๋ ‰ํ„ฐ์˜ ํ˜•์‹์€ ์ œํ•œ์ด ์—†๋‹ค. groupingBy(f)๋Š” ์‚ฌ์‹ค groupingBy(f, toList())์˜ ์ถ•์•ฝํ˜•์ด๋‹ค.

Map<Dish.Type, Long> typesCount = menu.stream()
        .collect(Collectors.groupingBy(Dish::getType, Collectors.counting()));
System.out.println(typesCount);

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

 

์š”๋ฆฌ์˜ ์ข…๋ฅ˜๋งˆ๋‹ค ๊ฐ€์žฅ ๋†’์€ ์นผ๋กœ๋ฆฌ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

Map<Dish.Type, Optional<Dish>> mostCaloricByType = menu.stream()
    .collect(Collectors.groupingBy(Dish::getType,
            Collectors.maxBy(Comparator.comparingInt(Dish::getCalories))));

 

๋งˆ์ง€๋ง‰ ๊ทธ๋ฃนํ™” ์—ฐ์‚ฐ์—์„œ ๋งต์˜ ๋ชจ๋“  ๊ฐ’์„ Optional๋กœ ๊ฐ์‹ธ๊ณ  ์‹ถ์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋‹ค. ์ฆ‰, ๋‹ค์Œ์ฒ˜๋Ÿผ ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ Collectors.collectingAndThen์œผ๋กœ ์ปฌ๋ ‰ํ„ฐ๊ฐ€ ๋ฐ˜ํ™˜ํ•œ ๊ฒฐ๊ณผ๋ฅผ ๋‹ค๋ฅธ ํ˜•์‹์œผ๋กœ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

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

๋ฆฌ๋“€์‹ฑ ์ปฌ๋ ‰ํ„ฐ๋Š” Optional.empty()๋ฅผ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ, ์•ˆ์ „ํ•œ ์ฝ”๋“œ์ด๋‹ค.

 

๋ถ„ํ• 

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

Map<Boolean, List<Dish>> partitionedMenu = menu.stream().collect(Collectors.partitioningBy(Dish::isVegetarian));

 

groupingBy์™€ ๋น„์Šทํ•˜๊ฒŒ ๋‘ ๋ฒˆ์งธ ์ธ์ˆ˜์— groupingBy๋ฅผ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค.

Map<Boolean, Map<Dish.Type, List<Dish>>> vegeterianDishesByType = menu.stream().collect(Collectors.partitioningBy(Dish::isVegetarian, Collectors.groupingBy(Dish::getType)));