์ผ | ์ | ํ | ์ | ๋ชฉ | ๊ธ | ํ |
---|---|---|---|---|---|---|
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 |
- ์์ ์ ๋ ฌ
- ์ด๋ถ ํ์
- ์คํ
- ์ต๋จ ๊ฒฝ๋ก
- Reversing
- java
- CVE
- error
- ๋ถํ ์ ๋ณต
- DP
- ์ฐ์ ์์ ํ
- thymeleaf
- ๋ฐ์ดํฌ์คํธ๋ผ
- GCP
- dfs
- ๋งต
- ๋ฐฑํธ๋ํน
- BFS
- Spring
- web
- ๋ฌธ์์ด
- ์๋ฎฌ๋ ์ด์
- ์ฌ๊ท
- ๋์ ํฉ
- dynamic debugging
- ๊ตฌํ
- c++
- OS
- ๊ทธ๋ฆฌ๋
- JPA
- Today
- Total
hades
[๋ชจ๋ ์๋ฐ ์ธ ์ก์ ] ์คํธ๋ฆผ ๋ณธ๋ฌธ
ํ์์ฑ
๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์นผ๋ก๋ฆฌ๊ฐ 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)));
'๐๐ปโโ๏ธ ๊ธฐ๋ณธํ๋ จ > Java' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[๋ชจ๋ ์๋ฐ ์ธ ์ก์ ] ์ปฌ๋ ์ API ๊ฐ์ (0) | 2024.09.20 |
---|---|
[๋ชจ๋ ์๋ฐ ์ธ ์ก์ ] ๋ณ๋ ฌ ๋ฐ์ดํฐ ์ฒ๋ฆฌ์ ์ฑ๋ฅ (0) | 2024.09.19 |
[๋ชจ๋ ์๋ฐ ์ธ ์ก์ ] ๋๋ค ํํ์ (0) | 2024.09.04 |
[๋ชจ๋ ์๋ฐ ์ธ ์ก์ ] ๋์ ํ๋ผ๋ฏธํฐํ ์ฝ๋ ์ ๋ฌํ๊ธฐ (0) | 2024.09.03 |
[Java] ์๋ฐ ๊ธฐ์ด (0) | 2024.08.27 |