π¦ chap6. μ€νΈλ¦Ό μΌλ‘ λ°μ΄ν° μμ§
6.1 컬λ ν°λ 무μμΈκ°?
ν¨μν νλ‘κ·Έλλ°μμλ β무μβμ μνλμ§ μ§μ λͺ μν μ μμ΄ μ΄λ ν λ°©λ²μΌλ‘ μ΄λ₯Ό μ»μμ§λ μ κ²½ μΈ νμκ° μλ€.
6.1.1 κ³ κΈ λ¦¬λμ± κΈ°λ₯μ μννλ 컬λ ν°
μ€νΈλ¦Όμ collect
λ₯Ό νΈμΆνλ©΄ μ€νΈλ¦Όμ μμμ 리λμ± μ°μ°μ΄ μνλλ€.
λͺ λ Ήν νλ‘κ·Έλλ°μμ μ°λ¦¬κ° μ§μ ꡬνν΄μΌ νλ μμ μ΄ μλμΌλ‘ μνλλ€.
λ³΄ν΅ ν¨μλ₯Ό μμλ‘ λ³νν λ 컬λ ν°λ₯Ό μ μ©νλ©° μ΅μ’ κ²°κ³Όλ₯Ό μ μ₯νλ μλ£κ΅¬μ‘°μ κ°μ λμ νλ€.
μλ₯Ό λ€μ΄ κ°μ₯ λ§μ΄ μ¬μ©νλ μ§κ΄μ μΈ μ μ λ©μλλ‘ toList
λ μ€νΈλ¦Όμ λͺ¨λ μμλ₯Ό 리μ€νΈλ‘ μμ§νλ€.
1
2
List<Transaction> transactions =
transactionStream.collect(Collectors.toList());
6.1.2 미리 μ μλ 컬λ ν°
Collectors
μμ μ 곡νλ λ©μλμ κΈ°λ₯μ ν¬κ² μΈ κ°μ§λ‘ ꡬλΆν μ μλ€.
- μ€νΈλ¦Ό μμλ₯Ό νλμ κ°μΌλ‘ 리λμ€νκ³ μμ½
- μμ κ·Έλ£Ήν
- μμ λΆν
6.2 리λμ±κ³Ό μμ½
컬λ ν°λ‘ μ€νΈλ¦Όμ νλͺ©μ 컬λ μ μΌλ‘ μ¬κ΅¬μ±ν μ μλ€.
μ¦ μ»¬λ ν°λ‘ μ€νΈλ¦Όμ λͺ¨λ νλͺ©μ νλμ κ²°κ³Όλ‘ ν©μΉ μ μλ€.
1
2
3
4
5
long howManyDishes = menu.stream().collect(Collectors.counting());
->
long howManyDishes = menu.stream().count();
6.2.1 μ€νΈλ¦Όκ°μμ μ΅λκ°κ³Ό μ΅μκ° κ²μ
Collectors.maxBy
Collectors.minBy
λ μ€νΈλ¦Όμ μ΅λκ°κ³Ό μ΅μκ°μ κ³μ°ν μ μλ€.
1
2
3
4
5
6
Comparator<Dish> dishCaloriesComparator =
Comparator.comparingInt(Dish::getCalories);
Optional<Dish> mostCalorieDish =
menu.stream()
.collect(maxBy(dishCaloriesComparator));
6.2.2 μμ½ μ°μ°
Collectors.summingInt
λΌλ μμ½ ν©ν°λ¦¬ λ©μλλ κ°μ²΄λ₯Ό int λ‘ λ§€ννλ ν¨μλ₯Ό μΈμλ‘ λ°λλ€.
μ΄ μΈμλ‘ μ λ¬λ ν¨μλ κ°μ²΄λ₯Ό int λ‘ λ§€νν 컬λ ν°λ₯Ό λ°ννλ€.
κ·Έλ¦¬κ³ summingInt
κ° collect
λ©μλλ‘ μ λ¬λλ©΄ μμ½ μμ
μ μννλ€.
1
int totalCalories = menu.stream().collect(summingInt(Dish::getCalories));
Collectors.summingLong
κ³Ό Collectors.summingDouble
λ©μλλ κ°μ λ°©μμΌλ‘ λμνλ€.
νκ· κ° κ³μ° λ±μ μ°μ° μμ½ κΈ°λ₯λ μ 곡λλ€.
1
2
double avgCalories =
menu.stream().collect(averagingInt(Dish::getCalories));
λ κ° μ΄μμ μμ½ μ°μ°μ ν λ²μ μνν΄μΌ ν λλ summarizingInt
κ° λ°ννλ 컬λ ν°λ₯Ό μ¬μ©ν μ μλ€.
1
2
IntSummaryStatistics menuStatistics =
menu.stream().collect(summarizingInt(Dish::getCalories));
menuStatistics κ°μ²΄λ₯Ό μΆλ ₯νλ©΄ λ€μκ³Ό κ°μ μ λ³΄κ° νμΈλλ€.
1
2
IntSummaryStatistics{count = 9, sum = 4300, min = 120,
average = 477.777778, max = 800}
6.2.3 λ¬Έμμ΄ μ°κ²°
joining
ν©ν 리 λ©μλλ₯Ό μ΄μ©νλ©΄ κ° κ°μ²΄μ toString
λ©μλλ₯Ό νΈμΆν΄μ μΆμΆν λͺ¨λ λ¬Έμμ΄μ νλμ λ¬Έμμ΄λ‘ μ°κ²°ν΄μ λ°ννλ€.
1
String shortMenu = menu.stream().map(Dish::getName).collect(joining());
λ΄λΆμ μΌλ‘ StringBuilder
λ₯Ό μ΄μ©ν΄μ λ¬Έμμ΄μ νλλ‘ λ§λ λ€.
λ μμ μ¬μ΄μ κ΅¬λΆ λ¬Έμμ΄μ λ£μ μ μλλ‘ μ€λ²λ‘λλ joining
λ μ 곡νλ€.
1
2
String shortMenu = menu.stream().map(Dish::getName)
.collect(joining(", "));
6.2.4 λ²μ© 리λμ± μμ½ μ°μ°
Collectors.reducing
μΌλ‘λ μ΄λ€μ μ μν μ μλ€.
νΉνλ 컬λ ν°λ₯Ό μ¬μ©ν μ΄μ λ νλ‘κ·Έλλ°μ νΈμμ± λλ¬Έμ΄λ€.
1
2
int totalCalories = menu.stream().collect(reducing(
0, Dish::getCalories, (i, j) -> i + j));
reducing
μ μΈ κ°μ μΈμλ₯Ό λ°λλ€.
- 리λμ± μ°μ°μ μμκ° λλ μ€νΈλ¦Όμ μΈμκ° μμ λλ λ°νκ°
- λ³ν ν¨μ
- κ°μ μ’ λ₯μ λ νλͺ©μ νλμ κ°μΌλ‘ μ°μ°νλ Operator
1
2
3
Optional<Dish> mostCalorieDish =
menu.stream().collect(reducing(
(d1, d2) -> d1.getCalories() > d2.getCalories() ? d1 : d2));
λ§μ½ reducing
μ΄ ν κ°μ μΈμλ₯Ό κ°μ§λ©΄ μ€νΈλ¦Όμ 첫 λ²μ§Έ μμλ₯Ό μμ μμ (1λ²μ§Έ μΈμ) λ‘ λ°λκ²,
κ·Έλ¦¬κ³ μμ μ κ·Έλλ‘ λ°ννλ νλ± ν¨μλ₯Ό λ λ²μ§Έ μΈμλ‘ λ°λ κ²μΌλ‘ κ°μ£Όνλ€.
κ·Έλμ λ§μ½ λΉ μ€νΈλ¦Όμ΄ λ겨μ§λ©΄ μμκ°μ΄ μ€μ λμ§ μλ μν©μ΄ λ²μ΄μ§κΈ° λλ¬Έμ Optional μ λ°ννλ€.
reducing μμ λλ€ λμ Integer ν¨μλ₯Ό μ¨μ λ κ°λ¨νκ² λ§λ€ μλ μλ€.
1
2
3
4
5
int totalCalories = menu.stream().collect(reducing(0,
Dish::getCalories,
Integer::sum));
int totalCalories = menu.stream().mapToInt(Dish::getCalories).sum();
6.3 κ·Έλ£Ήν
μλ° 8μ ν¨μνμ μ΄μ©νλ©΄ κ°λ μ±μλ ν μ€μ μ½λλ‘ κ·Έλ£Ήνλ₯Ό ꡬνν γ γ μλ€.
1
2
Map<Dish.Type, List<Dish>> dishesByType =
menu.stream().collect(groupingBy(Dish::getType));
groupingBy ν¨μλ₯Ό κΈ°μ€μΌλ‘ μ€νΈλ¦Όμ΄ κ·Έλ£Ήνλλ―λ‘ μ΄λ₯Ό λΆλ₯ ν¨μλΌκ³ λΆλ₯Έλ€.
λ¨μν μμ± μ κ·Όμ λμ λ 볡μ‘ν λΆλ₯ κΈ°μ€μ΄ νμν μν©μμλ λ©μλ μ°Έμ‘°λ₯Ό λΆλ₯ ν¨μλ‘ μ¬μ©ν μ μλ€.
λμ λλ€ ννμμΌλ‘ μ΄λ₯Ό ꡬνν μ μλ€.
1
2
3
4
5
6
7
8
9
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 κ·Έλ£Ήνλ μμ μ‘°μ
μμλ₯Ό κ·Έλ£Ήν ν λ€μμλ κ° κ²°κ³Ό κ·Έλ£Ήμ μμλ₯Ό μ‘°μνλ μ°μ°μ΄ νμνλ€.
1
2
3
Menu<Dish.Type, List<Dish>> caloricDishesByType =
menu.stream().filter(dish -> dish.getCalories() > 500)
.collect(groupingBy(Dish::getType));
μ΄ μ½λμ λ¨μ μ μλ κ²°κ³Όμ λν΄μλ Key μμ²΄κ° μ¬λΌμ§λ€λ μ μ΄λ€.
κ·Έλμ groupingBy ν©ν 리 λ©μλλ₯Ό μ€λ²λ‘λν΄ μ΄ λ¬Έμ λ₯Ό ν΄κ²°νλ€.
1
2
3
4
Menu<Dish.Type, List<Dish>> caloricDishesByType =
menu.stream()
.collect(groupingBy(Dish::getType,
filtering(dish -> dish.getCalories() > 500, toList())));
filtering
λ©μλλ Collectors
ν΄λμ€μ λ λ€λ₯Έ μ μ ν©ν 리 λ©μλλ‘ νλ λμΌμ΄νΈλ₯Ό μΈμλ‘ λ°μ μ¬κ·Έλ£Ήν νλ€.
κ·Έλμ λΉμ΄μλ λͺ©λ‘λ νλͺ©μΌλ‘ μΆκ°λλ€.
mapping
λ©μλλ κ·Έλ£Ήμ κ° μ리λ₯Ό κ΄λ ¨ μ΄λ¦ λͺ©λ‘μΌλ‘ λ³νν μ μλ€.
1
2
3
4
Menu<Dish.Type, List<String>> dishNamesByType =
menu.stream()
.collect(groupingBy(Dish::getType,
mapping(Dish::getName, toList())));
flatMapping
컬λ ν°λ₯Ό μ΄μ©νλ©΄ μ§ν©μΌλ‘ κ·Έλ£Ήννκ³ μ€λ³΅μ μ κ±°ν μ μλ€.
1
2
3
4
Menu<Dish.Type, Set<String>> dishNamesByType =
menu.stream()
.collect(groupingBy(Dish::getType,
flatMapping(Dish::getName).stream(), toSet())));
6.3.2 λ€μμ€ κ·Έλ£Ήν
groupingBy
λ₯Ό μ΄μ©ν΄ νλͺ©μ λ€μμ€μΌλ‘ κ·Έλ£Ήνν μ μλ€.
1
2
3
4
5
6
7
8
9
10
11
12
13
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;
})
)
);
n μμ€ κ·Έλ£Ήνμ κ²°κ³Όλ n μμ€ νΈλ¦¬ κ΅¬μ‘°λ‘ ννλλ n μμ€ λ§΅μ΄ λλ€.
6.3.3 μλΈκ·Έλ£ΉμΌλ‘ λ°μ΄ν° μμ§
groupingBy
λ‘ λ겨주λ 컬λ ν°μ νμμ μ νμ΄ μλ€.
1
2
Map<Dish.Type, Long> typesCount = menu.stream().collect(
groupingBy(Dish::getType, counting()));
1
2
3
4
Map<Dish.Type, Optional<Dish>> mostCaloricByType =
menu.stream()
.collect(groupingBy(Dish::getType,
maxBy(comparingInt(Dish::getCalories))));
ν©ν 리 λ©μλ 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)));
6.4 λΆν
λΆν μ λΆν ν¨μλΌ λΆλ¦¬λ νλ λμΌμ΄νΈλ₯Ό λΆλ₯ ν¨μλ‘ μ¬μ©νλ€.
λΆν ν¨μλ Boolean μ κΈ°μ€μΌλ‘ λλ μ°Έ, κ±°μ§ κ·Έλ£ΉμΌλ‘λ§ λΆλ¦¬λλ€.
1
2
Map<Boolean, List<Dish>> partitionedMenu =
menu.stream().collect(partitioningBy(Dish::isVegerarian));
6.4.1 λΆν μ μ₯μ
λΆν ν¨μκ° λ°ννλ μ°Έ, κ±°μ§ μμμ μ€νΈλ¦Ό 리μ€νΈλ₯Ό λͺ¨λ μ μ§νλ€λ κ²μ΄ μ₯μ μ΄λ€.
컬λ ν°λ₯Ό λ λ²μ§Έ μΈμλ‘ μ λ¬ν μ μλ μ€λ²λ‘λλ λ²μ λ μλ€.
1
2
3
4
Map<Boolean, Map<Dish.Type, List<Dish>>> vegetarianDishesByType =
menu.stream().collect(
partitioningBy(Dish::isVegerarian,
groupingBy(Dish::getType)));
μ΄λ₯Ό μ΄μ©νμ¬ μ±μ μ리μ μ±μμ΄ μλ μ리 κ°κ°μ κ·Έλ£Ήμμ κ°μ₯ μΉΌλ‘λ¦¬κ° λμ μ리λ μ°Ύμ μ μλ€.
1
2
3
4
5
Map<Boolean, Map<Dish.Type, List<Dish>>> vegetarianDishesByType =
menu.stream().collect(
partitioningBy(Dish::isVegerarian,
collectingAndThen(maxBy(comparingInt(Dish::getCalories)),
Optional.get)));
6.4.2 μ«μλ₯Ό μμμ λΉμμλ‘ λΆν νκΈ°
1
2
3
4
public boolean isPrime(int candidate) {
return IntStream.range(2, candidate)
.nonMatch(i -> candidate % i == 0);
}
1
2
3
4
5
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 : μμ§ μ°μ° κ²°κ³Ό κ°μ²΄μ νμ (λκ² μ»¬λ μ )
μ°λ¦¬κ° λ§λ€μ΄λ³Ό μ½λ ν°μ νμμ μ΄λ λ€.
1
public class ToListCollector<T> implements Collector<T, List<T>, List<T>>
6.5.1 Collector μΈν°νμ΄μ€μ λ©μλ μ΄ν΄λ³΄κΈ°
- Supplier
supplier
λ©μλλ λΉ λμ μ μΈμ€ν΄μ€λ₯Ό λ§λλ νλΌλ―Έν°κ° μλ ν¨μμ΄λ€.
1
2
3
public Supplier<List<T>> supplier() {
return () -> new ArrayList<T>();
}
- Acuumulator
accumulator
λ©μλλ 리λμ± μ°μ°μ μννλ ν¨μλ₯Ό λ°ννλ€.
1
2
3
public BiConsumar<List<T>, T> accumulator() {
return (list, item) -> list.add(item);
}
- Finisher
finisher
λ©μλλ μ€νΈλ¦Ό νμμ λλ΄κ³ λμ μ κ°μ²΄λ₯Ό μ΅μ’
κ²°κ³Όλ‘ λ°ννλ©΄μ λμ κ³Όμ μ λλΌ λ νΈμΆν ν¨μλ₯Ό λ°νν΄μΌ νλ€.
1
2
3
public Function<List<T>, List<T>> finisher() {
return Function.identity();
}
- Combiner
combiner
λ©μλλ 리λμ± μ°μ°μμ μ¬μ©ν ν¨μλ₯Ό λ°ννλ€.
μ€νΈλ¦Όμ μλ‘ λ€λ₯Έ μλΈννΈλ₯Ό λ³λ ¬λ‘ μ²λ¦¬ν λ λμ μκ° μ΄ κ²°κ³Όλ₯Ό μ΄λ»κ² μ²λ¦¬ν μ§ μ μνλ€.
1
2
3
4
5
6
public BinaryOperator<List<T>> combiner() {
return (list1, list2) -> {
list1.addAll(list2);
return list1;
}
}
- Characteristics
Characteristics
λ©μλλ 컬λ ν°μ μ°μ°μ μ μνλ λΆλ³ μ§ν©μ λ°ννλ€.
Characteristics
λ μΈ νλͺ©μ ν¬ν¨νλ μ΄κ±°νμ΄λ€.
- UNORDERED : 리λμ± κ²°κ³Όλ μ€νΈλ¦Ό μμ λ°©λ¬Έ μμλ λμ μμμ μν₯λ°μ§ μμ
- CONCURRENT : λ€μ€ μ€λ λμμ
acuumulator
ν¨μλ₯Ό λμμ νΈμΆν μ μμΌλ©° λ³λ ¬ 리λμ±μ μνν μ μλ€. - IDENTITY_FINISH :
finisher
λ©μλκ° λ°ννλ ν¨μλ λ¨μνidentity
λ₯Ό μ μ©ν λΏμ΄λ―λ‘ μ΄λ₯Ό μλ΅ν μ μλ€.
μ΄μ μ°λ¦¬κ° λ§λ 컬λ ν°λ₯Ό μμ μ μ¬μ©ν μ μλ€.
1
List<Dish> dishes = menuStream.collect(new ToListCollector<Dish>());