๐น chap5. ์คํธ๋ฆผ ํ์ฉ
5.1 ํํฐ๋ง
์คํธ๋ฆผ์ ์์๋ฅผ ์ ํํ๋ ๋ฐฉ๋ฒ์ ๋ํ ๋ด์ฉ์ด๋ค.
5.1.1 ํ๋ ๋์ผ์ดํธ๋ก ํํฐ๋ง
filter ๋ฉ์๋๋ ํ๋ ๋์ผ์ดํธ(boolean์ ๋ฐํํ๋ ํจ์)๋ฅผ ์ธ์๋ก ๋ฐ์์ ํ๋ ๋์ผ์ดํธ์ ์ผ์นํ๋ ๋ชจ๋ ์์๋ฅผ ํฌํจํ๋ ์คํธ๋ฆผ์ ๋ฐํํ๋ค.
1
2
3
List<Dish> vegetarianMenu = menu.stream()
.filter(Dish::isVegetarian) // ์ฑ์ ์๋ฆฌ์ธ์ง ํ์ธํ๋ ๋ฉ์๋ ์ฐธ์กฐ
.collect(toList());
5.1.2 ๊ณ ์ ์์ ํํฐ๋ง
์คํธ๋ฆผ์ ๊ณ ์ ์์๋ก ์ด๋ฃจ์ด์ง ์คํธ๋ฆผ์ ๋ฐํํ๋ distinct ๋ฉ์๋๋ ์ง์ํ๋ค(๊ณ ์ ์ฌ๋ถ๋ ์คํธ๋ฆผ์์ ๋ง๋ ๊ฐ์ฒด์ hashCode, equals๋ก ๊ฒฐ์ ๋๋ค).
1
2
3
4
5
List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
numbers.stream()
.filter(i -> i % 2 == 0)
.distinct()
.forEach(System.out::println);
5.2 ์คํธ๋ฆผ ์ฌ๋ผ์ด์ฑ
์คํธ๋ฆผ์ ์์๋ฅผ ์ ํํ๊ฑฐ๋ ์คํตํ๋ ๋ฐฉ๋ฒ์ด๋ค.
5.2.1 ํ๋ ๋์ผ์ดํธ๋ฅผ ์ด์ฉํ ์ฌ๋ผ์ด์ฑ
์๋ฐ 9๋ ์คํธ๋ฆผ์ ์์๋ฅผ ํจ๊ณผ์ ์ผ๋ก ์ ํํ ์ ์๋๋ก takeWhile, dropWhile ๋ ๊ฐ์ง ์๋ก์ด ๋งค์๋๋ฅด ์ง์ํ๋ค.
takeWhile ํ์ฉ
์๋ฆฌ ๋ชฉ๋ก์ ๋ค์๊ณผ ๊ฐ๋ค.
1
2
3
4
5
6
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));
320 ์นผ๋ก๋ฆฌ ์ดํ์ ์๋ฆฌ๋ฅผ ์ ํํ๊ธฐ ์ํด์ ์ด๋ป๊ฒ ํด์ผํ ๊น? ์ด์ filter์์ ๋ฐฐ์ด ๋ด์ฉ์ ์๋์ ๊ฐ๋ค.
1
2
3
4
List<Dish> filteredMenu
= specialMenu.stream()
.filter(dish -> dish.getCalories() < 320)
.collect(toList());
์๋ฆฌ ๋ฆฌ์คํธ๊ฐ ์ด๋ฏธ ์นผ๋ก๋ฆฌ ์์ผ๋ก ์ ๋ ฌ๋์ด ์๋ค๋ ์ฌ์ค์ ์ฃผ๋ชฉํ์. filter ์ฐ์ฐ์ ์ด์ฉํ๋ฉด ์ ์ฒด ์คํธ๋ฆผ์ ๋ฐ๋ณตํ๋ฉด์ ๊ฐ ์์์ ํ๋ ๋์ผ์ดํธ๋ฅผ ์ ์ฉํ๊ฒ ๋๋ค.
๋ฐ๋ผ์ ์ด๋ฏธ ์ ๋ ฌ๋์ด ์๋ค๋ ์ฌ์ค์ ์ด์ฉํด 320 ์นผ๋ก๋ฆฌ๋ณด๋ค ํฌ๊ฑฐ๋ ๊ฐ์ ์๋ฆฌ๊ฐ ๋์์ ๋ ๋ฐ๋ณต ์์ ์ ์ค๋จํ ์ ์๋ค. ์ด๋ ํฐ ์คํธ๋ฆผ์์๋ ์๋นํ ์ฐจ์ด๊ฐ ์์ ์ ์๋ค.
takeWhile ์ฐ์ฐ์ ์ด์ฉํ๋ฉด ๋ฌดํ ์คํธ๋ฆผ์ ํฌํจํ ๋ชจ๋ ์คํธ๋ฆผ์ ํ๋ ๋์ผ์ดํธ๋ฅผ ์ ์ฉํด ์คํธ๋ฆผ์ ์ฌ๋ผ์ด์คํ ์ ์๋ค.
1
2
3
4
List<Dish> slicedMenu1
= specialMenu.stream()
.takeWhile(dish -> dish.getCalories() < 320)
.collect(toList());
dropWhile ํ์ฉ
320 ์นผ๋ก๋ฆฌ๋ณด๋ค ํฐ ์์(๋๋จธ์ง ์์)๋ฅผ ํ์ํ๊ธฐ ์ํด์๋ dropWhile์ ์ด์ฉํ๋ฉด ๋๋ค.
1
2
3
4
List<Dish> slicedMenu2
= specialMenu.stream()
.dropWhile(dish -> dish.getCalories() < 320)
.collect(toList());
dropWhile์ takeWhile๊ณผ ์ ๋ฐ๋์ ์์ ์ ์ํํ๋ค. dropWhile์ ํ๋ ๋์ผ์ดํธ๊ฐ ์ฒ์์ผ๋ก ๊ฑฐ์ง์ด ๋๋ ์ง์ ๊น์ง ๋ฐ๊ฒฌ๋ ์์๋ฅผ ๋ฒ๋ฆฐ๋ค. ํ๋ ๋์ผ์ดํธ๊ฐ ๊ฑฐ์ง์ด ๋๋ฉด ๊ทธ ์ง์ ์์ ์์ ์ ์ค๋จํ๊ณ ๋จ์ ์์๋ฅผ ๋ฐํํ๋ค. dropWhile์ ๋ฌดํํ ๋จ์ ์์๋ฅผ ๊ฐ์ง ๋ฌดํ ์คํธ๋ฆผ์์๋ ๋์ํ๋ค.
5.2.2 ์คํธ๋ฆผ ์ถ์
limit
์คํธ๋ฆผ์ ์ฃผ์ด์ง ๊ฐ ์ดํ์ ํฌ๊ธฐ๋ฅผ ๊ฐ๋ ์๋ก์ด ์คํธ๋ฆผ์ ๋ฐํํ๋ limit(n) ๋ฉ์๋๋ฅผ ์ง์ํ๋ค.
1
2
3
4
List<Dish> dishes = specialMenu.stream()
.filter(dish -> dish.getCalories() > 300)
.limit(3)
.collect(toList());
ํ๋ ๋์ผ์ดํธ์ ์ผ์นํ๋ ์ฒ์ ์ธ ์์๋ฅผ ์ ํํ ๋ค์์ ์ฆ์ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๋ค.
โ ๋ชจ๋ ์์๋ฅผ ํํฐ๋ง ํ์ง ์๋๋ค!
์ ๋ ฌ๋์ง ์์ ์คํธ๋ฆผ์๋ limit์ ์ฌ์ฉํ ์ ์๋ค. ์์ค๊ฐ ์ ๋ ฌ๋์ด ์์ง ์์๋ค๋ฉด limit์ ๊ฒฐ๊ณผ๋ ์ ๋ ฌ๋์ง ์์ ์ํ๋ก ๋ฐํ๋๋ค.
5.2.3 ์์ ๊ฑด๋๋ฐ๊ธฐ
skip
์คํธ๋ฆผ์ ์ฒ์ n๊ฐ ์์๋ฅผ ์ ์ธํ ์คํธ๋ฆผ์ ๋ฐํํ๋ skip(n) ๋ฉ์๋๋ฅผ ์ง์ํ๋ค. n๊ฐ ์ดํ์ ์์๋ฅผ ํฌํจํ๋ skip(n)์ ํธ์ถํ๋ฉด ๋น ์คํธ๋ฆผ์ด ๋ฐํ๋๋ค.
limit(n)๊ณผ skip(n)์ ์ํธ ๋ณด์์ ์ธ ์ฐ์ฐ์ ์ํํ๋ค.
1
2
3
4
5
// ์ฒ์ ๋ ์๋ฆฌ๋ ๊ฑด๋๋ด ๋ค์ 300์นผ๋ก๋ฆฌ ๋๋ ์๋ฆฌ ๋ฐํ
List<Dish> dishes = menu.stream()
.filter(d -> d.getCalories() > 300)
.skip(2)
.collect(toList());
Quiz. ํํฐ๋ง
์คํธ๋ฆผ์ ์ด์ฉํด์ ์ฒ์ ๋ฑ์ฅํ๋ ๋ ๊ณ ๊ธฐ ์๋ฆฌ๋ฅผ ํํฐ๋งํ์์ค.
์ ๋ต
1 2 3 4
List<Dish> meatDishes = menu.stream() .filter(d -> d.getType() == Dish.Type.MEAT) .limit(2) .collect(toList());
5.3 ๋งคํ
์คํธ๋ฆผ์ ํน์ ๋ฐ์ดํฐ๋ฅผ ์ ํํ๋ ๊ธฐ๋ฅ์ด๋ค.
ex. ํน์ ๊ฐ์ฒด์์ ํน์ ๋ฐ์ดํฐ ์ ํ
5.3.1 ์คํธ๋ฆผ์ ๊ฐ ์์์ ํจ์ ์ ์ฉํ๊ธฐ
map
์คํธ๋ฆผ์ ํจ์๋ฅผ ์ธ์๋ก ๋ฐ๋ map ๋ฉ์๋๋ฅผ ์ง์ํ๋ค. ์ธ์๋ก ์ ๊ณต๋ ํจ์๋ ๊ฐ ์์์ ์ ์ฉ๋๋ฉฐ ํจ์๋ฅผ ์ ์ฉํ ๊ฒฐ๊ณผ๊ฐ ์๋ก์ด ์์๋ก ๋งคํ๋๋ค. (์ด ๊ณผ์ ์ โ๊ธฐ์กด์ ๊ฐ์ ๊ณ ์น๋คโ๋ผ๋ ๊ฐ๋ ๋ณด๋ค โ์๋ก์ด ๋ฒ์ ์ ๋ง๋ ๋คโ๋ผ๋ ๊ฐ๋ ์ ๊ฐ๊น์ฐ๋ฏ๋ก ๋ณํ์ ๊ฐ๊น์ด ๋งคํ์ด๋ผ๋ ๋จ์ด๋ฅผ ์ฌ์ฉํ๋ค.)
1
2
3
4
// Dish::getName์ map ๋ฉ์๋๋ก ์ ๋ฌํด ์คํธ๋ฆผ์ ์๋ฆฌ๋ช
์ถ์ถ
List<String> dishNames = menu.stream()
.map(Dish::getName)
.collect(toList());
getName์ ๋ฌธ์์ด์ ๋ฐํํ๋ฏ๋ก map ๋ฉ์๋์ ์ถ๋ ฅ ์คํธ๋ฆผ์ Stream
์ด๋ฒ์ ๋ค๋ฅธ ์์๋ฅผ ๋ณด์.
1
2
3
4
5
// String::length๋ฅผ map ๋ฉ์๋๋ก ์ ๋ฌํด ์คํธ๋ฆผ์ ๊ธธ์ด๋ฅผ ์ถ์ถ
List<String> words = Arrays.asList("Modern", "Java", "In", "Action");
List<Integer> wordLengths = words.stream()
.map(String::length)
.collect(toList());
์ ๋ ์์๋ฅผ ํฉ์ณ๋ณด๋ฉด
1
2
3
4
5
// ์๋ฆฌ๋ช
์ ๊ธธ์ด ์ถ์ถ
List<Integer> dishNameLengths = menu.stream()
.map(Dish::getName)
.map(String::length)
.collect(toList());
์ด์ฒ๋ผ map ๋ฉ์๋๋ฅผ ์ฐ๊ฒฐํด์ ์ฌ์ฉํ ์ ์๋ค.
5.3.2 ์คํธ๋ฆผ์ ํ๋ฉดํ
๋ฆฌ์คํธ์์ ๊ณ ์ ๋ฌธ์๋ก ์ด๋ฃจ์ด์ง ๋ฆฌ์คํธ๋ฅผ ๋ฐํํ๋ ค ํ๋ค. ์๋ฅผ ๋ค์ด [โHelloโ, โWorldโ] ๋ฆฌ์คํธ๊ฐ ์๋ค๋ฉด ๊ฒฐ๊ณผ๋ก [โHโ, โeโ, โlโ, โoโ, โWโ, โrโ, โdโ]๋ฅผ ํฌํจํ๋ ๋ฆฌ์คํธ๊ฐ ๋ฐํ๋์ด์ผ ํ๋ค.
๋ฆฌ์คํธ์ ์๋ ๊ฐ ๋จ์ด๋ฅผ ๋ฌธ์๋ก ๋งคํํ ๋ค์ distinct๋ก ์ค๋ณต๋ ๋ฌธ์๋ฅผ ํํฐ๋งํด ์ฝ๊ฒ ํด๊ฒฐํ ์ ์์ ๊ฒ์ด๋ผ ์๊ฐํ ๊ฒ์ด๋ค.
1
2
3
4
words.stream()
.map(word -> word.split(""))
.distinct()
.collect(toList());
์ ์ฝ๋์์ map์ผ๋ก ์ ๋ฌํ ๋๋ค๋ ๊ฐ ๋จ์ด์ String[] (๋ฌธ์์ด ๋ฐฐ์ด)์ ๋ฐํํ๋ค๋ ์ ์ด ๋ฌธ์ ๋ค. ๋ฐ๋ผ์ map ๋ฉ์๋๊ฐ ๋ฐํํ ์คํธ๋ฆผ์ ํ์์ Stream<String[]>์ด๋ค. ์ฐ๋ฆฌ๊ฐ ์ํ๋ ๊ฒ์ ๋ฌธ์์ด์ ์คํธ๋ฆผ์ ํํํ Stream
์ ๊ฐ์ ๋ฌธ์ ๋ฅผ flatMap์ด๋ผ๋ ๋ฉ์๋๋ฅผ ์ด์ฉํด์ ํด๊ฒฐํ ์ ์๋ค.
map๊ณผ Arrays.stream ํ์ฉ
์ฐ์ ๋ฐฐ์ด ์คํธ๋ฆผ ๋์ ๋ฌธ์์ด ์คํธ๋ฆผ์ด ํ์ํ๋ค. ๋ฌธ์์ด์ ๋ฐ์ ์คํธ๋ฆผ์ ๋ง๋๋ Arrays.stream() ๋ฉ์๋๊ฐ ์๋ค.
1
2
String[] arrayOfWords = {"Goodbye", "World"};
Stream<String> streamOfwords = Arrays.stream(arrayOfWords);
์ ์์ ์ ํ์ดํ๋ผ์ธ์ Arrays.stream() ๋ฉ์๋๋ฅผ ์ ์ฉํด๋ณด์.
1
2
3
4
5
words.stream()
.map(word -> word.split("")) // ๊ฐ ๋จ์ด๋ฅผ ๊ฐ๋ณ ๋ฌธ์์ด ๋ฐฐ์ด๋ก ๋ณํ
.map(Arrays::stream) // ๊ฐ ๋ฐฐ์ด์ ๋ณ๋์ ์คํธ๋ฆผ์ผ๋ก ์์ฑ
.distinct()
.collect(toList());
๊ฒฐ๊ตญ ์คํธ๋ฆผ ๋ฆฌ์คํธ(์๋ฐํ ๋ฐ์ง๋ฉด List<Stream
flatMap ์ฌ์ฉ
flatMap์ ์ฌ์ฉํ๋ฉด ๋ค์์ฒ๋ผ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์๋ค.
1
2
3
4
5
6
List<String> uniqueCharacters =
words.stream()
.map(word -> word.split("")) // ๊ฐ ๋จ์ด๋ฅผ ๊ฐ๋ณ ๋ฌธ์๋ฅผ ํฌํจํ๋ ๋ฐฐ์ด๋ก ๋ณํ
.flatMap(Arrays::stream) // ์์ฑ๋ ์คํธ๋ฆผ์ ํ๋์ ์คํธ๋ฆผ์ผ๋ก ํ๋ฉดํ
.distinct()
.collect(toList());
flatMap์ ๊ฐ ๋ฐฐ์ด์ ์คํธ๋ฆผ์ด ์๋๋ผ ์คํธ๋ฆผ์ ์ฝํ ์ธ ๋ก ๋งคํํ๋ค. ์ฆ, map(Arrays::stream)๊ณผ ๋ฌ๋ฆฌ flatMap์ ํ๋์ ํ๋ฉดํ๋ ์คํธ๋ฆผ์ ๋ฐํํ๋ค.
flatMap ๋ฉ์๋๋ ์คํธ๋ฆผ์ ๊ฐ ๊ฐ์ ๋ค๋ฅธ ์คํธ๋ฆผ์ผ๋ก ๋ง๋ ๋ค์์ ๋ชจ๋ ์คํธ๋ฆผ์ ํ๋์ ์คํธ๋ฆผ์ผ๋ก ์ฐ๊ฒฐํ๋ ๊ธฐ๋ฅ์ ์ํํ๋ค.
- Quiz. ๋งคํ
- ์ซ์ ๋ฆฌ์คํธ๊ฐ ์ฃผ์ด์ก์ ๋ ๊ฐ ์ซ์์ ์ ๊ณฑ๊ทผ์ผ๋ก ์ด๋ฃจ์ด์ง ๋ฆฌ์คํธ๋ฅผ ๋ฐํํ์์ค. ์๋ฅผ ๋ค์ด [1, 2, 3, 4, 5]๊ฐ ์ฃผ์ด์ง๋ฉด [1, 4, 9, 16, 25]๋ฅผ ๋ฐํํด์ผ ํ๋ค.
- ์ ๋ต
1 2 3 4
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); List<Integer> squares = numbers.stream() .map(n -> n * n) .collect(toList());
- ๋ ๊ฐ์ ์ซ์ ๋ฆฌ์คํธ๊ฐ ์์ ๋ ๋ชจ๋ ์ซ์ ์์ ๋ฆฌ์คํธ๋ฅผ ๋ฐํํ์์ค. ์๋ฅผ ๋ค์ด ๋ ๊ฐ์ ๋ฆฌ์คํธ [1, 2, 3]๊ณผ [3, 4]๊ฐ ์ฃผ์ด์ง๋ฉด [(1, 3), (1, 4), (2, 3), (2, 4), (3, 3), (3, 4)]๋ฅผ ๋ฐํํด์ผ ํ๋ค.
- ์ ๋ต
1 2 3 4 5 6 7 8
List<Integer> numbers1 = Arrays.asList(1, 2, 3); List<Integer> numbers2 = Arrays.asList(3, 4); List<int[]> pairs = numbers1.stream() .flatMap(i -> numbers2.stream() .map(j -> new int[]{i, j}) ) .collect(toList());
๋ ๊ฐ์ map์ ์ด์ฉํด์ ๋ ๋ฆฌ์คํธ๋ฅผ ๋ฐ๋ณตํ ๋ค์์ ์ซ์ ์์ ๋ง๋ค ์ ์๋ค. ํ์ง๋ง ๊ฒฐ๊ณผ๋ก Stream<Stream<Integer[]ยป>๊ฐ ๋ฐํ๋๋ค. ๋ฐ๋ผ์ ๊ฒฐ๊ณผ๋ฅผ Stream<Integer[]>๋ก ํ๋ฉดํํ ์คํธ๋ฆผ์ด ํ์ํ๋ค. ๋ฐ๋ก flatMap์ ์ฌ์ฉํด์ผ ํ๋ค.
- ์ด์ ์์ ์์ ํฉ์ด 3์ผ๋ก ๋๋์ด๋จ์ด์ง๋ ์๋ง ๋ฐํํ๋ ค๋ฉด ์ด๋ป๊ฒ ํด์ผ ํ ๊น? ์๋ฅผ ๋ค์ด (2, 4), (3, 3)์ ๋ฐํํด์ผ ํ๋ค.
์ ๋ต
1 2 3 4 5 6 7 8 9
List<Integer> numbers1 = Arrays.asList(1, 2, 3); List<Integer> numbers2 = Arrays.asList(3, 4); List<int[]> pairs = numbers1.stream() .flatMap(i -> numbers2.stream() .filter(j -> (i + j) % 3 == 0) .map(j -> new int[]{i, j}) ) .collect(toList());
filter๋ฅผ ํ๋ ๋์ผ์ดํธ์ ํจ๊ป ์ฌ์ฉํ๋ฉด ์คํธ๋ฆผ์ ์์๋ฅผ ํํฐ๋งํ ์ ์๋ค. flatMap์ ์คํํ๋ฉด ์ซ์ ์์ ํฌํจํ๋ int[] ์คํธ๋ฆผ์ด ๋ฐํ๋๋ฏ๋ก ํ๋ ๋์ผ์ดํธ๋ฅผ ์ด์ฉํด์ ์ซ์ ์์ ํฉ์ด 3์ผ๋ก ๋๋์ด๋จ์ด์ง๋์ง ํ์ธํ ์ ์๋ค.
- ์ซ์ ๋ฆฌ์คํธ๊ฐ ์ฃผ์ด์ก์ ๋ ๊ฐ ์ซ์์ ์ ๊ณฑ๊ทผ์ผ๋ก ์ด๋ฃจ์ด์ง ๋ฆฌ์คํธ๋ฅผ ๋ฐํํ์์ค. ์๋ฅผ ๋ค์ด [1, 2, 3, 4, 5]๊ฐ ์ฃผ์ด์ง๋ฉด [1, 4, 9, 16, 25]๋ฅผ ๋ฐํํด์ผ ํ๋ค.
5.4 ๊ฒ์๊ณผ ๋งค์นญ
ํน์ ์์ฑ์ด ๋ฐ์ดํฐ ์งํฉ์ ์๋์ง ์ฌ๋ถ๋ฅผ ๊ฒ์ํ๋ ๋ฐ์ดํฐ ์ฒ๋ฆฌ
5.4.1 ํ๋ ๋์ผ์ดํธ๊ฐ ์ ์ด๋ ํ ์์์ ์ผ์นํ๋์ง ํ์ธ
anyMatch
ํ๋ ๋์ผ์ดํธ๊ฐ ์ฃผ์ด์ง ์คํธ๋ฆผ์์ ์ ์ด๋ ํ ์์์ ์ผ์นํ๋์ง ํ์ธํ ๋ ์ฌ์ฉํ๋ค.
1
2
3
4
// menu์ ์ฑ์์๋ฆฌ๊ฐ ์๋์ง ํ์ธํ๋ ์์
if(menu.stream().anyMatch(Dish::isVegetarian)) {
System.out.println("The menu is (somewhat) vegetarian friendly!!");
}
anyMatch๋ boolean์ ๋ฐํํ๋ฏ๋ก ์ต์ข ์ฐ์ฐ์ด๋ค.
5.4.2 ํ๋ ๋์ผ์ดํธ๊ฐ ๋ชจ๋ ์์์ ์ผ์นํ๋์ง ๊ฒ์ฌ
allMatch
anyMatch์ ๋ฌ๋ฆฌ ์คํธ๋ฆผ์ ๋ชจ๋ ์์๊ฐ ์ฃผ์ด์ง ํ๋ ๋์ผ์ดํธ์ ์ผ์นํ๋์ง ๊ฒ์ฌํ๋ค.
1
2
3
// ๊ฑด๊ฐ์ ์ฐพ๊ธฐ(1000์นผ๋ก๋ฆฌ ์ดํ)
boolean isHealthy = menu.stream()
.allMatch(dish -> dish.getCalories() < 1000);
noneMatch
allMatch์ ๋ฐ๋ ์ฐ์ฐ์ ์ํํ๋ค. ์ฆ, ์ฃผ์ด์ง ํ๋ ๋์ผ์ดํธ์ ์ผ์นํ๋ ์์๊ฐ ์๋์ง ํ์ธํ๋ค.
1
2
boolean isHealthy = menu.stream()
.noneMatch(d -> d.getCalories >= 1000);
anyMatch, allMatch, noneMatch๋ ์คํธ๋ฆผ ์ผํธ์ํท ๊ธฐ๋ฒ, ์ฆ ์๋ฐ์ &&, | ย | ์ ๊ฐ์ ์ฐ์ฐ์ ํ์ฉํ๋ค. |
(๋ชจ๋ ์คํธ๋ฆผ์ ์์๋ฅผ ์ฒ๋ฆฌํ์ง ์๊ณ ๋ ๊ฒฐ๊ณผ ๋ฐํ)
5.4.3 ์์ ๊ฒ์
findAny
ํ์ฌ ์คํธ๋ฆผ์์ ์์์ ์์๋ฅผ ๋ฐํํ๋ค. findAny ๋ฉ์๋๋ ๋ค๋ฅธ ์คํธ๋ฆผ ์ฐ์ฐ๊ณผ ์ฐ๊ฒฐํด์ ์ฌ์ฉํ ์ ์๋ค.
1
2
3
4
5
// ์ฑ์ ์๋ฆฌ ์ ํ
Optional<Dish> dish =
menu.stream()
.filter(Dish::isVegetarian)
.findAny();
์คํธ๋ฆผ ํ์ดํ๋ผ์ธ์ ๋ด๋ถ์ ์ผ๋ก ๋จ์ผ ๊ณผ์ ์ผ๋ก ์คํํ ์ ์๋๋ก ์ต์ ํ๋๋ค. ์ฆ, ์ผํธ์ํท์ ์ด์ฉํด์ ๊ฒฐ๊ณผ๋ฅผ ์ฐพ๋ ์ฆ์ ์คํ์ ์ข ๋ฃํ๋ค.
Optional์ด๋?
Optional
Optional์ ๊ฐ์ด ์กด์ฌํ๋์ง ํ์ธํ๊ณ ๊ฐ์ด ์์ ๋ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ ์ง ๊ฐ์ ํ๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.
- isPresent()๋ Optional์ด ๊ฐ์ ํฌํจํ๋ฉด ์ฐธ(true)์ ๋ฐํํ๊ณ , ๊ฐ์ ํฌํจํ์ง ์์ผ๋ฉด ๊ฑฐ์ง(false)์ ๋ฐํํ๋ค.
- ifPresent(Consumer
block)์ ๊ฐ์ด ์์ผ๋ฉด ์ฃผ์ด์ง ๋ธ๋ก์ ์คํํ๋ค. Consumer ํจ์ํ ์ธํฐํ์ด์ค์๋ T ํ์์ ์ธ์๋ฅผ ๋ฐ์ผ๋ฉฐ void๋ฅผ ๋ฐํํ๋ ๋๋ค๋ฅผ ์ ๋ฌํ ์ ์๋ค. - T get()์ ๊ฐ์ด ์กด์ฌํ๋ฉด ๊ฐ์ ๋ฐํํ๊ณ , ๊ฐ์ด ์์ผ๋ฉด NoSuchElementException์ ์ผ์ผํจ๋ค.
- T orElse (T other)๋ ๊ฐ์ด ์์ผ๋ฉด ๊ฐ์ ๋ฐํํ๊ณ , ๊ฐ์ด ์์ผ๋ฉด ๊ธฐ๋ณธ๊ฐ์ ๋ฐํํ๋ค.
1
2
3
4
menu.stream()
.filter(Dish::isVegetarian)
.findAny() // Optional<Dish> ๋ฐํ
.ifPresent(dish -> System.out.println(dish.getName()); // ๊ฐ์ด ์์ผ๋ฉด ์ถ๋ ฅ, ์์ผ๋ฉด ์๋ฌด์ผ๋ ์ผ์ด๋์ง ์๋๋ค.
์ ์ฝ๋์์ findAny๋ก Optional
5.4.4 ์ฒซ ๋ฒ์งธ ์์ ์ฐ๊ธฐ
๋ฆฌ์คํธ ๋๋ ์ ๋ ฌ๋ ์ฐ์ ๋ฐ์ดํฐ๋ก๋ถํฐ ์์ฑ๋ ์คํธ๋ฆผ์ฒ๋ผ ์ผ๋ถ ์คํธ๋ฆผ์๋ ๋ ผ๋ฆฌ์ ์ธ ์์ดํ ์์๊ฐ ์ ํด์ ธ ์์ ์ ์๋ค. ์ด๋ ์คํธ๋ฆผ์์ ์ฒซ ๋ฒ์งธ ์์๋ฅผ ์ฐพ์๋ณด์.
1
2
3
4
5
6
7
// 3์ผ๋ก ๋จ์ด์ง๋ ์ฒซ ๋ฒ์งธ ์ ๊ณฑ๊ฐ์ ๋ฐํ
List<Integer> someNumbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> firstSquareDivisibleByTree =
someNumbers.stream()
.map(n -> n * n)
.filter(n -> n % 3 == 0)
.findFirst();
5.5 ๋ฆฌ๋์ฑ(ํด๋)
๋ชจ๋ ์คํธ๋ฆผ ์์๋ฅผ ์ฒ๋ฆฌํด์ ๊ฐ์ผ๋ก ๋์ถํ๋ ๊ฒ
5.5.1 ์์์ ํฉ
1
2
3
4
5
// ๋ฆฌ์คํธ์ ์ซ์ ์์์ ํฉ
int sum = 0;
for (int x : numbers) {
sum += x;
}
numbers์ ๊ฐ ์์๋ ๊ฒฐ๊ณผ์ ๋ฐ๋ณต์ ์ผ๋ก ๋ํด์ง๋ค. ๋ฆฌ์คํธ์์ ํ๋์ ์ซ์๊ฐ ๋จ์ ๋๊น์ง reduce ๊ณผ์ ์ ๋ฐ๋ณตํ๋ค. ์ฝ๋์๋ ํ๋ผ๋ฏธํฐ 2๊ฐ๋ฅผ ์ฌ์ฉํ๋ค.
- sum ๋ณ์์ ์ด๊น๊ฐ 0
- ๋ฆฌ์คํธ์ด ๋ชจ๋ ์์๋ฅผ ์กฐํฉํ๋ ์ฐ์ฐ(+)
reduce๋ฅผ ์ด์ฉํ๋ฉด ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฐ๋ณต๋ ํจํด์ ์ถ์ํํ ์ ์๋ค. reduce๋ฅผ ์ด์ฉํด์ ๋ค์์ฒ๋ผ ์คํธ๋ฆผ์ ๋ชจ๋ ์์๋ฅผ ๋ํ ์ ์๋ค.
1
int sum = numbers.stream().reduce(0, (a, b) -> a + b);
reduce๋ 2๊ฐ์ ์ธ์๋ฅผ ๊ฐ๋๋ค.
- ์ด๊น๊ฐ 0
- ๋ ์์๋ฅผ ์กฐํฉํด์ ์๋ก์ด ๊ฐ์ ๋ง๋๋ BinaryOperator
. ์์ ์์๋ ๋๋ค ํํ์ (a, b) โ (a + b)๋ฅผ ์ฌ์ฉํ๋ค.
์คํธ๋ฆผ์ด ํ๋์ ๊ฐ์ผ๋ก ์ค์ด๋ค ๋๊น์ง ๋๋ค๋ ๊ฐ ์์๋ฅผ ๋ฐ๋ณตํด์ ์กฐํฉํ๋ค.
๋ฉ์๋ ์ฐธ์กฐ๋ฅผ ์ด์ฉํด์ ์ด ์ฝ๋๋ฅผ ์ข ๋ ๊ฐ๊ฒฐํ๊ฒ ๋ง๋ค ์ ์๋ค. ์๋ฐ 8์์๋ Integer ํด๋์ค์์ ๋ ์ซ์๋ฅผ ๋ํ๋ ์ ์ sum ๋ฉ์๋๋ฅผ ์ ๊ณตํ๋ค. ๋ฐ๋ผ์ ์ง์ ๋๋ค ์ฝ๋๋ฅผ ๊ตฌํํ ํ์๊ฐ ์๋ค.
1
int sum = numbers.stream().reduce(0, Integer::sum);
์ด๊น๊ฐ ์์
์ด๊น๊ฐ์ ๋ฐ์ง ์๋๋ก ์ค๋ฒ๋ก๋๋ reduce๋ ์๋ค. ๊ทธ๋ฌ๋ ์ด reduce๋ Optional ๊ฐ์ฒด๋ฅผ ๋ฐํํ๋ค.
1
Optional<Integer> sum = numbers.stream().reduce((a, b) -> (a + b));
์์ ๊ฒฝ์ฐ ์คํธ๋ฆผ์ ์๋ฌด ์์๋ ์๋ ์ํฉ์์ ์ด๊น๊ฐ์ด ์์ผ๋ฏ๋ก reduce๋ ํฉ๊ณ๋ฅผ ๋ฐํํ ์ ์๋ค. ๋ฐ๋ผ์ ํฉ๊ณ๊ฐ ์์์ ๊ฐ๋ฆฌํฌ ์ ์๋๋ก Optional ๊ฐ์ฒด๋ก ๊ฐ์ผ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๋ค.
5.5.2 ์ต๋๊ฐ๊ณผ ์ต์๊ฐ
์ต๋๊ฐ๊ณผ ์ต์๊ฐ์ ์ฐพ์ ๋๋ reduce๋ฅผ ํ์ฉํ ์ ์๋ค. reduce๋ฅผ ์ด์ฉํด์ ์คํธ๋ฆผ์์ ์ต๋๊ฐ๊ณผ ์ต์๊ฐ์ ์ฐพ๋ ๋ฐฉ๋ฒ์ ์ดํด๋ณด์. reduce๋ ๋ ์ธ์๋ฅผ ๋ฐ๋๋ค.
- ์ด๊น๊ฐ
- ์คํธ๋ฆผ์ ๋ ์์๋ฅผ ํฉ์ณ์ ํ๋์ ๊ฐ์ผ๋ก ๋ง๋๋ ๋ฐ ์ฌ์ฉํ ๋๋ค
1
2
// ์ต๋๊ฐ
Optional<Integer> max = numbers.stream().reduce(Integer::max);
1
2
// ์ต์๊ฐ
Optional<Integer> min = numbers.stream().reduce(Integer::min);
Integer::min ๋์ ๋๋ค ํํ์ (x, y) โ x < y ? x : y๋ฅผ ์ฌ์ฉํด๋ ๋ฌด๋ฐฉํ์ง๋ง ๋ฉ์๋ ์ฐธ์กฐ ํํ์ด ๋ ์ฝ๊ธฐ ์ฝ๋ค.
Quiz. ๋ฆฌ๋์ค
map๊ณผ reduce ๋งค์๋๋ฅผ ์ด์ฉํด์ ์คํธ๋ฆผ์ ์๋ฆฌ ๊ฐ์๋ฅผ ๊ณ์ฐํ์์ค.
์ ๋ต
์คํธ๋ฆผ์ ๊ฐ ์์๋ฅผ 1๋ก ๋งคํํ ๋ค์์ reduce๋ก ์ด๋ค์ ํฉ๊ณ๋ฅผ ๊ณ์ฐํ๋ ๋ฐฉ์์ผ๋ก ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์๋ค. ์ฆ, ์คํธ๋ฆผ์ ์ ์ฅ๋ ์ซ์๋ฅผ ์ฐจ๋ก๋ก ํ๋ค.
1 2 3 4
int count = menu.stream() .map(d -> 1) .reduce(0, (a, b) -> a + b);
map๊ณผ reduce๋ฅผ ์ฐ๊ฒฐํ๋ ๊ธฐ๋ฒ์ ๋งต ๋ฆฌ๋์ค ํจํด์ด๋ผ ํ๋ฉฐ, ์ฝ๊ฒ ๋ณ๋ ฌํํ๋ ํน์ง ๋๋ถ์ ๊ตฌ๊ธ์ด ์น ๊ฒ์์ ์ ์ฉํ๋ฉด์ ์ ๋ช ํด์ก๋ค.
1
long count = menu.stream().count();
์ด๊ฒ๋ ๊ฐ๋ฅํ๋ค.
reduce ๋ฉ์๋์ ์ฅ์ ๊ณผ ๋ณ๋ ฌํ
reduce๋ฅผ ์ด์ฉํ๋ฉด ๋ด๋ถ ๋ฐ๋ณต์ด ์ถ์ํ๋๋ฉด์ ๋ด๋ถ ๊ตฌํ์์ ๋ณ๋ ฌ๋ก reduce๋ฅผ ์คํํ ์ ์๊ฒ ๋๋ค. ๋ฐ๋ณต์ ์ธ ํฉ๊ณ์์๋ sum ๋ณ์๋ฅผ ๊ณต์ ํด์ผ ํ๋ฏ๋ก ์ฝ๊ฒ ๋ณ๋ ฌํํ๊ธฐ ์ด๋ ต๋ค.
์ฝ๋๋ฅผ ๋ณ๋ ฌ๋ก ๋ง๋ค๊ธฐ ์ํด์๋ stream()์ parallelStream()์ผ๋ก ๋ฐ๊พธ๋ฉด ๋๋ค.
1
int sum = numbers.parallelStream().reduce(0, Integer::sum);
์์ฒ๋ผ ๋ณ๋ ฌ๋ก ์คํํ๋ ค๋ฉด ์กฐ๊ฑด์ด ์๋ค. reduce์ ๋๊ฒจ์ค ๋๋ค์ ์ํ(์ธ์คํด์ค ๋ณ์ ๊ฐ์)๊ฐ ๋ฐ๋์ง ๋ง์์ผ ํ๋ฉฐ, ์ฐ์ฐ์ด ์ด๋ค ์์๋ก ์คํ๋๋๋ผ๋ ๊ฒฐ๊ณผ๊ฐ ๋ฐ๋์ง ์๋ ๊ตฌ์กฐ์ฌ์ผ ํ๋ค.
์คํธ๋ฆผ ์ฐ์ฐ : ์ํ ์์๊ณผ ์ํ ์์
- ๋ด๋ถ ์ํ๋ฅผ ๊ฐ์ง ์๋ ์ฐ์ฐ
- map, filter ๋ฑ
- ์ ๋ ฅ ์คํธ๋ฆผ์์ ๊ฐ ์์๋ฅผ ๋ฐ์ 0 ๋๋ ๊ฒฐ๊ณผ๋ฅผ ์ถ๋ ฅ ์คํธ๋ฆผ์ผ๋ก ๋ณด๋ธ๋ค.
- ๋ด๋ถ ์ํ๋ฅผ ๊ฐ๋ ์ฐ์ฐ
- reduce, sum, max ๋ฑ
- ๊ฒฐ๊ณผ๋ฅผ ๋์ ํ ๋ด๋ถ ์ํ๊ฐ ํ์ โ ์คํธ๋ฆผ์์ ์ฒ๋ฆฌํ๋ ์์ ์์ ๊ด๊ณ์์ด ๋ด๋ถ ์ํ์ ํฌ๊ธฐ๋ ํ์ ๋์ด ์๋ค.
- sorted, distinct ๋ฑ์ filter๋ map์ฒ๋ผ ์คํธ๋ฆผ์ ์ ๋ ฅ์ผ๋ก ๋ฐ๋๋ค.
์คํธ๋ฆผ์ ์์๋ฅผ ์ ๋ ฌํ๊ฑฐ๋ ์ค๋ณต์ ์ ๊ฑฐํ๋ ค๋ฉด ๊ณผ๊ฑฐ์ ์ด๋ ฅ์ ์๊ณ ์์ด์ผ ํ๋ค.
(ex. ์ด๋ค์์๋ฅผ ์ถ๋ ฅ ์คํธ๋ฆผ์ผ๋ก ์ถ๊ฐํ๋ ค๋ฉด ๋ชจ๋ ์์๊ฐ ๋ฒํผ์ ์ถ๊ฐ๋์ด ์์ด์ผ ํ๋ค.)
5.6 ์ค์ ์ฐ์ต
ํธ๋์ญ์ (๊ฑฐ๋)์ ์คํํ๋ ๊ฑฐ๋์ ์์ ๋ฅผ ์ดํด๋ณด์. ์ฐ๋ฆฌ์ ๊ด๋ฆฌ์๊ฐ ์ฌ๋ ๊ฐ์ง ์ง๋ฌธ์ ๋ต์ ์ฐพ์ผ๋ผ๊ณ ์์ฒญํ๋ค.
- 2011๋ ์ ์ผ์ด๋ ๋ชจ๋ ํธ๋์ญ์ ์ ์ฐพ์ ๊ฐ์ ์ค๋ฆ์ฐจ์์ผ๋ก ์ ๋ฆฌํ์์ค.
- ๊ฑฐ๋์๊ฐ ๊ทผ๋ฌดํ๋ ๋ชจ๋ ๋์๋ฅผ ์ค๋ณต ์์ด ๋์ดํ์์ค.
- ์ผ์๋ธ๋ฆฌ์ง์์ ๊ทผ๋ฌดํ๋ ๋ชจ๋ ๊ฑฐ๋์๋ฅผ ์ฐพ์์ ์ด๋ฆ์์ผ๋ก ์ ๋ ฌํ์์ค.
- ๋ชจ๋ ๊ฑฐ๋์์ ์ด๋ฆ์ ์ํ๋ฒณ์์ผ๋ก ์ ๋ ฌํด์ ๋ฐํํ์์ค.
- ๋ฐ๋ผ๋ ธ์ ๊ฑฐ๋์๊ฐ ์๋๊ฐ?
- ์ผ์๋ธ๋ฆฌ์ง์ ๊ฑฐ์ฃผํ๋ ๊ฑฐ๋์์ ๋ชจ๋ ํธ๋์ญ์ ๊ฐ์ ์ถ๋ ฅํ์์ค.
- ์ ์ฒด ํธ๋์ญ์ ์ค ์ต๋๊ฐ์ ์ผ๋ง์ธ๊ฐ?
- ์ ์ฒด ํธ๋์ญ์ ์ค ์ต์๊ฐ์ ์ผ๋ง์ธ๊ฐ?
5.6.1 ๊ฑฐ๋์์ ํธ๋์ญ์
๊ฑฐ๋์ ๋ฆฌ์คํธ์ ํธ๋์ญ์ ๋ฆฌ์คํธ์ด๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
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)
);
๋ค์์ Trader์ Transaction์ ํด๋์ค ์ ์๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public class Trader {
private final String name;
private final String city;
public Trader(String n, String c) {
this.name = n;
this.city = c;
}
public String getName() {
return this.name;
}
public String getCity() {
return this.city;
}
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 this.trader;
}
public int getYear() {
return this.year;
}
public int getValue() {
return this.value;
}
public String toString() {
return "{" + this.trader + ", " +
"year: " + this.year + ", " +
"value: " + this.value + "}";
}
}
์ ๋ต
2011๋ ์ ์ผ์ด๋ ๋ชจ๋ ํธ๋์ญ์ ์ ์ฐพ์ ๊ฐ์ ์ค๋ฆ์ฐจ์์ผ๋ก ์ ๋ฆฌํ์์ค.
1 2 3 4 5
List<Transaction> tr2011 = transactions.stream() .filter(transaction -> transaction.getYear() == 2011) .sorted(comparing(Transaction::getValue)) // ํธ๋์ญ์ ๊ฐ์ผ๋ก ์ ๋ ฌ .collect(toList());
๊ฑฐ๋์๊ฐ ๊ทผ๋ฌดํ๋ ๋ชจ๋ ๋์๋ฅผ ์ค๋ณต ์์ด ๋์ดํ์์ค.
1 2 3 4 5
List<String> cities = transactions.stream() .map(transaction -> transaction.getTrader().getCity()) .distinct() // ๊ณ ์ ๋์๋ง ์ ํ .collect(toList());
distinct() ๋์ ์ ์คํธ๋ฆผ์ ์งํฉ์ผ๋ก ๋ณํํ๋ toSet()์ ์ฌ์ฉํ ์ ์๋ค.
1 2 3 4
Set<String> cities = transactions.stream() .map(transaction -> transaction.getTrader().getCity()) .collect(toSet());
์ผ์๋ธ๋ฆฌ์ง์์ ๊ทผ๋ฌดํ๋ ๋ชจ๋ ๊ฑฐ๋์๋ฅผ ์ฐพ์์ ์ด๋ฆ์์ผ๋ก ์ ๋ ฌํ์์ค.
1 2 3 4 5 6 7
List<Trader> traders = transactions.stream() .map(Transaction::getTrader) // ํธ๋์ญ์ ์ ๋ชจ๋ ๊ฑฐ๋์ ์ถ์ถ .filter(trader -> trader.getCity()**.equals("Cambridge")**) .distinct() // ์ค๋ณต์ด ์๋๋ก ํ์ธ .sorted(comparing(Trader::getName)) .collect(toList());
๋ชจ๋ ๊ฑฐ๋์์ ์ด๋ฆ์ ์ํ๋ฒณ์์ผ๋ก ์ ๋ ฌํด์ ๋ฐํํ์์ค.
1 2 3 4 5 6
String traderStr = transactions.stream() .map(transaction -> transaction.getTrader().getName()) .distinct() .sorted() .reduce("", (n1, n2) -> n1 + n2); // ์ด๋ฆ์ ์ํ๋ฒณ ์์ผ๋ก ์ ๋ ฌ
์ ์ฝ๋๋ ๋ชจ๋ ๋ฌธ์์ด์ ๋ฐ๋ณต์ ์ผ๋ก ์ฐ๊ฒฐํด์ ์๋ก์ด ๋ฌธ์์ด ๊ฐ์ฒด๋ฅผ ๋ง๋ ๋ค. ๋ฐ๋ผ์ ํจ์จ์ฑ์ด ๋ถ์กฑํ๋ค. joining()์ ์ด์ฉํด์ ๋ ํจ์จ์ ์ผ๋ก ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์๋ค.(joining์ ๋ด๋ถ์ ์ผ๋ก StringBuilder๋ฅผ ์ด์ฉํ๋ค.)
1 2 3 4 5 6
String traderStr = transactions.stream() .map(transaction -> transaction.getTrader().getName()) .distinct() .sorted() .collect(joining());
๋ฐ๋ผ๋ ธ์ ๊ฑฐ๋์๊ฐ ์๋๊ฐ?
1 2 3 4 5 6
boolean isTraderInMilan = transactions.stream() .anyMatch(transaction -> transaction.getTrader() .getCity() .equals("Milan")); // anyMatch์ ํ๋ ๋์ผ์ดํธ๋ฅผ ์ ๋ฌํด์ ๋ฐ๋ผ๋ ธ์ ๊ฑฐ๋์๊ฐ ์๋์ง ํ์ธ
์ผ์๋ธ๋ฆฌ์ง์ ๊ฑฐ์ฃผํ๋ ๊ฑฐ๋์์ ๋ชจ๋ ํธ๋์ญ์ ๊ฐ์ ์ถ๋ ฅํ์์ค.
1 2 3
transactions.stream() .filter(t -> t.getTrader().getCity().equals("Cambridge")) .forEach(t -> System.out.println(t.getValue()));
์ ์ฒด ํธ๋์ญ์ ์ค ์ต๋๊ฐ์ ์ผ๋ง์ธ๊ฐ?
1 2 3 4
Optional<Integer> max_value = transactions.stream() .map(Transaction::getValue) .reduce(Integer::max);
์ ์ฒด ํธ๋์ญ์ ์ค ์ต์๊ฐ์ ์ผ๋ง์ธ๊ฐ?
1 2 3 4
Optional<Integer> min_value = transactions.stream() .map(Transaction::getValue) .reduce(Integer::min);
๋ ์ฌ์ด ๋ฐฉ๋ฒ๋ ์๋ค.
1 2 3
Optional<Transaction> smallestTransaction = transactions.stream() .min(comparing(Transaction::getValue));
์คํธ๋ฆผ์ ์ต๋๊ฐ์ด๋ ์ต์๊ฐ์ ๊ณ์ฐํ๋ ๋ฐ ์ฌ์ฉํ ํค๋ฅผ ์ง์ ํ๋ Comparator๋ฅผ ์ธ์๋ก ๋ฐ๋ min๊ณผ max ๋ฉ์๋๋ฅผ ์ ๊ณตํ๋ค.
5.7 ์ซ์ํ ์คํธ๋ฆผ
1
2
3
4
// ๋ฉ๋ด์ ์นผ๋ก๋ฆฌ ํฉ๊ณ ๊ณ์ฐ
int calories = menu.stream()
.map(Dish::getCalories)
.reduce(0, Integer::sum);
์ด ์ฝ๋์๋ ๋ฐ์ฑ ๋น์ฉ์ด ์จ์ด์๋ค. ๋ด๋ถ์ ์ผ๋ก ํฉ๊ณ๋ฅผ ๊ณ์ฐํ๊ธฐ ์ ์ Integer๋ฅผ ๊ธฐ๋ณธํ์ผ๋ก ์ธ๋ฐ์ฑํด์ผ ํ๋ค.
1
2
3
4
// !!๋ถ๊ฐ๋ฅํ ์ฝ๋
int calories = menu.stream()
.map(Dish::getCalories)
.sum();
์ ์ฝ๋์ฒ๋ผ sum ๋ฉ์๋๋ฅผ ์ง์ ํธ์ถํ ์ ์๋ค. map ๋ฉ์๋๊ฐ Stream
๋คํํ๋ ์คํธ๋ฆผ์ ์คํธ๋ฆผ API ์ซ์ ์คํธ๋ฆผ์ ํจ์จ์ ์ผ๋ก ์ฒ๋ฆฌํ ์ ์๋๋ก ๊ธฐ๋ณธํ ํนํ ์คํธ๋ฆผ์ ์ ๊ณตํ๋ค.
5.7.1 ๊ธฐ๋ณธํ ํนํ ์คํธ๋ฆผ
์๋ฐ 8์์๋ ์ธ ๊ฐ์ง ๊ธฐ๋ณธํ ํนํ ์คํธ๋ฆผ์ ์ ๊ณตํ๋ค.
- int ์์์ ํนํ๋ IntStream
- double ์์์ ํนํ๋ DoubleStream
- long ์์์ ํนํ๋ LongStream
๊ฐ๊ฐ์ ์ธํฐํ์ด์ค๋ ์ซ์ ์คํธ๋ฆผ์ ํฉ๊ณ๋ฅผ ๊ณ์ฐํ๋ sum, ์ต๋๊ฐ ์์๋ฅผ ๊ฒ์ํ๋ max ๊ฐ์ด ์์ฃผ ์ฌ์ฉํ๋ ์ซ์ ๊ด๋ จ ๋ฆฌ๋์ฑ ์ฐ์ฐ ์ํ ๋ฉ์๋๋ฅผ ์ ๊ณตํ๋ค.
ํนํ ์คํธ๋ฆผ์ ์ค์ง ๋ฐ์ฑ ๊ณผ์ ์์ ์ผ์ด๋๋ ํจ์จ์ฑ๊ณผ ๊ด๋ จ ์์ผ๋ฉฐ ์คํธ๋ฆผ์ ์ถ๊ฐ ๊ธฐ๋ฅ์ ์ ๊ณตํ์ง ์๋๋ค.
์ซ์ ์คํธ๋ฆผ์ผ๋ก ๋งคํ
์คํธ๋ฆผ์ ํนํ ์คํธ๋ฆผ์ผ๋ก ๋ณํํ ๋๋ mapToInt, mapToDouble, mapToLong ์ธ ๊ฐ์ง ๋ฉ์๋๋ฅผ ๊ฐ์ฅ ๋ง์ด ์ฌ์ฉํ๋ค. ์ด๋ค ๋ฉ์๋๋ map๊ณผ ์ ํํ ๊ฐ์ ๊ธฐ๋ฅ์ ์ํํ์ง๋ง, Stream
1
2
3
int calories = menu.stream() // Stream<Dish> ๋ฐํ
.mapToInt(Dish::getCalories) // IntStream ๋ฐํ
.sum();
mapToInt ๋ฉ์๋๋ ๊ฐ ์๋ฆฌ์์ ๋ชจ๋ ์นผ๋ก๋ฆฌ(Integer ํ์)๋ฅผ ์ถ์ถํ ๋ค์์ IntStream์(**Stream
์คํธ๋ฆผ์ด ๋น์ด์์ผ๋ฉด sum์ ๊ธฐ๋ณธ๊ฐ 0์ ๋ฐํํ๋ค.
IntStream์ max, min, average ๋ฑ ๋ค์ํ ์ ํธ๋ฆฌํฐ ๋ฉ์๋๋ ์ง์ํ๋ค.
๊ฐ์ฒด ์คํธ๋ฆผ์ผ๋ก ๋ณต์ํ๊ธฐ(boxed)
์ซ์ ์คํธ๋ฆผ โ ํนํ๋์ง ์์ ์คํธ๋ฆผ
ex) IntStream์ ๊ธฐ๋ณธํ์ ์ ์๋ง ๋ง๋ค ์ ์๋ค.
1
2
3
// boxed ๋ฉ์๋๋ฅผ ํตํด ํนํ ์คํธ๋ฆผ์ ์ผ๋ฐ ์คํธ๋ฆผ์ผ๋ก ๋ณํ
IntStream intStream = menu.stream().mapToInt(Dish::getCalories); // ์คํธ๋ฆผ์ ์ซ์ ์คํธ๋ฆผ์ผ๋ก ๋ณํ
Stream<Integer> stream = intStream.boxed(); // ์ซ์ ์คํธ๋ฆผ์ ์คํธ๋ฆผ์ผ๋ก ๋ณํ
๊ธฐ๋ณธ๊ฐ : OptionalInt
IntStream์์๋ ์ต๋๊ฐ์ ์ฐพ์ ๋ 0์ด๋ผ๋ ๊ธฐ๋ณธ๊ฐ์ด ์์ผ๋ฉด ์๋ชป๋ ๊ฒฐ๊ณผ๊ฐ ๋์ถ๋ ์ ์๋ค.
โ์คํธ๋ฆผ์ ์์๊ฐ ์๋ ์ํฉโ๊ณผ โ์ค์ ์ต๋๊ฐ์ด 0์ธ ์ํฉ์ ์ด๋ป๊ฒ ๊ตฌ๋ณํ ์ ์์๊น? Optional์ ์ฌ์ฉํ๋ค.
Optional์ Integer, String ๋ฑ์ ์ฐธ์กฐ ํ์์ผ๋ก ํ๋ผ๋ฏธํฐํํ ์ ์๋ค. ๋ํ OptionalInt, OptionalDouble, OptionalLong ์ธ ๊ฐ์ง ๊ธฐ๋ณธํ ํนํ ์คํธ๋ฆผ ๋ฒ์ ๋ ์ ๊ณตํ๋ค.
1
2
3
4
// OptionalInt๋ฅผ ์ด์ฉํด์ IntStream์ ์ต๋๊ฐ ์์๋ฅผ ์ฐพ์ ์ ์๋ค.
**OptionalInt maxCalories** = menu.stream()
.mapToInt(Dish::getCalories)
.max();
์ด์ OptionalInt๋ฅผ ์ด์ฉํด์ ์ต๋๊ฐ์ด ์๋ ์ํฉ์ ์ฌ์ฉํ ๊ธฐ๋ณธ๊ฐ์ ๋ช ์์ ์ผ๋ก ์ ์ํ ์ ์๋ค.
1
int max = maxCalories.orElse(1); // ๊ฐ์ด ์์ ๋ ๊ธฐ๋ณธ ์ต๋๊ฐ์ ๋ช
์์ ์ผ๋ก ์ค์
5.7.2 ์ซ์ ๋ฒ์
์๋ฐ 8์ IntStream๊ณผ LongStream์์๋ range์ rangeClosed๋ผ๋ ๋ ๊ฐ์ง ์ ์ ๋ฉ์๋๋ฅผ ์ ๊ณตํ๋ค. ๋ ๋ฉ์๋ ๋ชจ๋ ์ฒซ ๋ฒ์งธ ์ธ์๋ก ์์๊ฐ์, ๋ ๋ฒ์งธ ์ธ์๋ก ์ข ๋ฃ๊ฐ์ ๊ฐ๋๋ค. range ๋ฉ์๋๋ ์์๊ฐ๊ณผ ์ข ๋ฃ๊ฐ์ด ๊ฒฐ๊ณผ์ ํฌํจ๋์ง ์๋ ๋ฐ๋ฉด rangeClosed๋ ์์๊ฐ๊ณผ ์ข ๋ฃ๊ฐ์ด ๊ฒฐ๊ณผ์ ํฌํจ๋๋ค๋ ์ ์ด ๋ค๋ฅด๋ค.
1
2
3
IntStream evenNumbers = IntStream.rangeClosed(1, 100) // [1, 100] ๋ฒ์
.filter(n -> n % 2 == 0); // ์ง์ ์คํธ๋ฆผ
System.out.println(evenNumbers.count());
์ ์ฝ๋์์ filter๋ฅผ ํธ์ถํด๋ ์ค์ ๋ก๋ ์๋ฌด ๊ณ์ฐ๋ ์ด๋ฃจ์ด์ง์ง ์๋๋ค(์ค๊ฐ ์ฐ์ฐ์ด๊ณ ์ต์ข ์ฐ์ฐ์ธ count๊ฐ ์คํ๋ ๋ ์งํ๋จ!).
5.7.3 ์ซ์ ์คํธ๋ฆผ ํ์ฉ : ํผํ๊ณ ๋ผ์ค ์
์ธ ์ ํํํ๊ธฐ
ํผํ๊ณ ๋ผ์ค ์ ์คํธ๋ฆผ์ ๋ง๋ค๊ธฐ ์ํด ์ฐ์ ์ธ ์๋ฅผ ์ ์ํด์ผ ํ๋ค. ์ธ ์์๋ฅผ ๊ฐ๋ int ๋ฐฐ์ด์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์ ๊ฒ ๊ฐ๋ค. ์๋ฅผ๋ค์ด (3, 4, 5)๋ฅผ new int[]{3, 4, 5}๋ก ํํํ ์ ์๋ค.
์ข์ ํํฐ๋ง ์กฐํฉ
์ธ ์ ์ค a, b ๋ ์๋ง ์ฃผ์ด์ก๋ค๊ณ ๊ฐ์ ํ์ ๋, ๋ ์๊ฐ ํผํ๊ณ ๋ผ์ค ์์ ์ผ๋ถ๊ฐ ๋ ์ ์๋์ง ํ์ธํ ์ ์๋ ๋ฐฉ๋ฒ์ด ์์๊น?
a * a + b * b์ ์ ๊ณฑ๊ทผ์ด ์ ์์ธ์ง ํ์ธํ ์ ์๋ค.
1
Math.sqrt(a*a + b*b) % 1 == 0
์ด๋ x๊ฐ ๋ถ๋ ์์ซ์ ์๋ผ๋ฉด x % 1.0์ด๋ผ๋ ์๋ฐ ์ฝ๋๋ก ์์ซ์ ์ดํ ๋ถ๋ถ์ ์ป์ ์ ์๋ค.
์ด๊ฒ์ filter์ ๋ค์์ฒ๋ผ ํ์ฉํ ์ ์๋ค.
1
filter(b -> Math.sqrt(a*a + b*b) % 1 == 0)
์ ์ฝ๋์์ a๋ผ๋ ๊ฐ์ด ์ฃผ์ด์ง๊ณ b๋ ์คํธ๋ฆผ์ผ๋ก ์ ๊ณต๋๋ค๊ณ ๊ฐ์ ํ ๋ filter๋ก a์ ํจ๊ป ํผํ๊ณ ๋ผ์ค ์๋ฅผ ๊ตฌ์ฑํ๋ ๋ชจ๋ b๋ฅผ ํํฐ๋งํ ์ ์๋ค.
์งํฉ ์์ฑ
ํํฐ๋ฅผ ์ด์ฉํ์ฌ ์ข์ ์กฐํฉ์ ๊ฐ๋ a, b๋ฅผ ์ ํํ ์ ์๊ฒ ๋์๋ค. ์ด์ ๋ง์ง๋ง ์ธ ๋ฒ์งธ ์๋ฅผ ์ฐพ์์ผ ํ๋ค. ๋ค์์ฒ๋ผ map์ ์ด์ฉํด์ ๊ฐ ์์๋ฅผ ํผํ๊ณ ๋ผ์ค ์๋ก ๋ณํํ ์ ์๋ค.
1
2
stream.filter(b -> Math.sqrt(a*a + b*b) % 1 == 0)
.map(b -> new int[]{a, b, (int) Math.sqrt(a * a + b * b)});
b๊ฐ ์์ฑ
์ด์ b๊ฐ์ ์์ฑํด์ผ ํ๋ค. Stream.rangeClosed๋ก ์ฃผ์ด์ง ๋ฒ์์ ์๋ฅผ ๋ง๋ค ์ ์์์ ๋ฐฐ์ ๋ค.
1
2
3
4
IntStream.rangeClosed(1, 100)
.filter(b -> Math.sqrt(a*a + b*b) % 1 == 0)
.boxed()
.map(b -> new int[]{a, b, (int) Math.sqrt(a * a + b * b)});
filter ์ฐ์ฐ ๋ค์์ rangeClosed๊ฐ ๋ฐํํ IntStream์ boxed๋ฅผ ์ด์ฉํด์ Stream
๊ฐ์ฒด๊ฐ ์คํธ๋ฆผ์ ๋ฐํํ๋ IntStream์ mapToObj ๋ฉ์๋๋ฅผ ์ด์ฉํด์ ์ด ์ฝ๋๋ฅผ ์ฌ๊ตฌํํ ์ ์๋ค.
1
2
3
IntStream.rangeClosed(1, 100)
.filter(b -> Math.sqrt(a*a + b*b) % 1 == 0)
.mapToObj(b -> new int[]{a, b, (int) Math.sqrt(a * a + b * b)});
a๊ฐ ์์ฑ
๋ง์ง๋ง์ผ๋ก a๊ฐ์ ์์ฑํ๋ ์ฝ๋๋ฅผ ์ถ๊ฐํ๋ค. b์ ๋น์ทํ ๋ฐฉ๋ฒ์ผ๋ก a๊ฐ์ ์์ฑํ ์ ์๋ค.
1
2
3
4
5
6
7
Stream<int[]> pythagoreanTriples =
IntStream.rangeClosed(1, 100).boxed()
.flatMap(a ->
IntStream.rangeClosed(a, 100)
.filter(b -> Math.sqrt(a*a + b*b) % 1 == 0)
.mapToObj(b -> new int[]{a, b, (int)Math.sqrt(a*a+b*b)})
);
์ ์ฝ๋์์ ์คํธ๋ฆผ a์ ๊ฐ์ ๋งคํํ๋ฉด ์คํธ๋ฆผ์ ์คํธ๋ฆผ์ด ๋ง๋ค์ด์ง ๊ฒ์ด๋ค. ๋ฐ๋ผ์ flatMap ๋ฉ์๋๊ฐ ์์ฑ๋ ๊ฐ๊ฐ์ ์คํธ๋ฆผ์ ํ๋์ ํ์คํ๋ ์คํธ๋ฆผ์ผ๋ก ๋ง๋ค์ด์ค๋ค. ๊ฒฐ๊ณผ์ ์ผ๋ก ์ธ ์๋ก ์ด๋ฃจ์ด์ง ์คํธ๋ฆผ์ ์ป์ ์ ์๋ค.
๋ํ b์ ๋ฒ์๊ฐ 1์ด ์๋ a๋ถํฐ ์์ํจ์ ์ค๋ณต์ ๋ง๊ธฐ ์ํจ์ด๋ค.
์ฝ๋ ์คํ
limit์ ์ฌ์ฉํ์ฌ ์ผ๋ง๋ ๋ง์ ์ธ ์๋ฅผ ํฌํจํ๋ ์คํธ๋ฆผ์ ๋ง๋ค ๊ฒ์ธ์ง ๊ฒฐ์ ํ๋ฉด ๋๋ค.
1
2
3
pythagoreamTriples.limit(5)
.forEach(t ->
System.out.println(t[0] + ", " + t[1] + ", " + t[2]));
๊ฐ์ ํ ์ ?
ํ์ฌ ์ฝ๋์์๋ ์ ๊ณฑ๊ทผ์ ๋ ๋ฒ ๊ณ์ฐํ๋ค. ๋ฐ๋ผ์ (aa, bb, aa+bb) ํ์์ ๋ง์กฑํ๋ ์ธ ์๋ฅผ ๋ง๋ ๋ค์์ ์ฐ๋ฆฌ๊ฐ ์ํ๋ ์กฐ๊ฑด์ ๋ง๋ ๊ฒฐ๊ณผ๋ง ํํฐ๋งํ๋ ๊ฒ์ด ๋ ์ต์ ํ๋ ๋ฐฉ๋ฒ์ด๋ค.
1
2
3
4
5
6
Stream<double[]> pythagoreanTriples2 =
IntStream.rangeClosed(1, 100).boxed()
.flatMap(a -> IntStream.rangeClosed(a, 100))
.mapToObj(
b -> new double[]{a, b, Math.sqrt(a*a + b*b)}) // ๋ง๋ค์ด์ง ์ธ ์
.filter(t -> t[2] % 1 == 0); // ์ธ ์์ ์ธ ๋ฒ์งธ ์์๋ ๋ฐ๋์ ์ ์์ฌ์ผ ํ๋ค.
5.8 ์คํธ๋ฆผ ๋ง๋ค๊ธฐ
์ผ๋ จ์ ๊ฐ, ๋ฐฐ์ด, ํ์ผ, ํจ์๋ฅผ ์ด์ฉํ ๋ฌดํ ์คํธ๋ฆผ ๋ง๋ค๊ธฐ ๋ฑ ๋ค์ํ ๋ฐฉ์์ผ๋ก ์คํธ๋ฆผ์ ๋ง๋๋ ๋ฐฉ๋ฒ
5.8.1 ๊ฐ์ผ๋ก ์คํธ๋ฆผ ๋ง๋ค๊ธฐ
Stream.of
์์์ ์๋ฅผ ์ธ์๋ก ๋ฐ๋ ์ ์ ๋ฉ์๋ Stream.of๋ฅผ ์ด์ฉํด์ ์คํธ๋ฆผ์ ๋ง๋ค ์ ์๋ค.
1
2
3
4
// ๋ฌธ์์ด ์คํธ๋ฆผ ๋ง๋ค๊ธฐ
Stream<String> stream = Stream.of("Modern ", "Java ", "In ", "Action");
// ์คํธ๋ฆผ์ ๋ชจ๋ ๋ฌธ์์ด์ ๋๋ฌธ์๋ก ๋ณํํ ํ ๋ฌธ์์ด์ ํ๋์ฉ ์ถ๋ ฅ
stream.map(String::toUpperCase).forEach(System.out::println);
๋ค์์ฒ๋ผ empty ๋ฉ์๋๋ฅผ ์ด์ฉํด์ ์คํธ๋ฆผ์ ๋น์ธ ์ ์๋ค.
1
Stream<String> emptyStream = Stream.empty();
๋ค์์ฒ๋ผ empty ๋ฉ์๋๋ฅผ ์ด์ฉํด์ ์คํธ๋ฆผ์ ๋น์ธ ์ ์๋ค.
5.8.2 null์ด ๋ ์ ์๋ ๊ฐ์ฒด๋ก ์คํธ๋ฆผ ๋ง๋ค๊ธฐ
์๋ฐ 9์์๋ null์ด ๋ ์ ์๋ ๊ฐ์ฒด๋ฅผ ์คํธ๋ฆผ์ผ๋ก ๋ง๋ค ์ ์๋ ์๋ก์ด ๋ฉ์๋๊ฐ ์ถ๊ฐ๋์๋ค.
Stream.ofNullable
์๋ฅผ ๋ค์ด System.getProperty๋ ์ ๊ณต๋ ํค์ ๋์ํ๋ ์์ฑ์ด ์์ผ๋ฉด null์ ๋ฐํํ๋ค. ์ด๋ฐ ๋ฉ์๋๋ฅผ ์คํธ๋ฆผ์ ํ์ฉํ๋ ค๋ฉด ๋ค์์ฒ๋ผ null์ ๋ช ์์ ์ผ๋ก ํ์ธํด์ผ ํ๋ค.
1
2
3
String homeValue = System.getProperty("home");
Stream<String> homeValueStream
= homeValue == null ? Stream.empty() : Stream.of(value);
Stream.ofNullable์ ์ด์ฉํด ๋ค์์ฒ๋ผ ์ฝ๋๋ฅผ ๊ตฌํํ ์ ์๋ค.
1
2
Stream<String> homeValueStream
= Stream.ofNullable(System.getProperty("home"));
null์ด ๋ ์ ์๋ ๊ฐ์ฒด๋ฅผ ํฌํจํ๋ ์คํธ๋ฆผ๊ฐ์ flatMap๊ณผ ํจ๊ป ์ฌ์ฉํ๋ ์ํฉ์์๋ ์ด ํจํด์ ๋ ์ ์ฉํ๊ฒ ์ฌ์ฉํ ์ ์๋ค.
1
2
3
Stream<String> values =
Stream.of("config", "home", "user")
.flatMap(key -> Stream.ofNullable(System.getProperty(key)));
5.8.3 ๋ฐฐ์ด๋ก ์คํธ๋ฆผ ๋ง๋ค๊ธฐ
Arrays.stream
๋ฐฐ์ด์ ์ธ์๋ก ๋ฐ๋ ์ ์ ๋ฉ์๋ Arrays.stream์ ์ด์ฉํด์ ์คํธ๋ฆผ์ ๋ง๋ค ์ ์๋ค. ์๋ฅผ ๋ค์ด ๋ค์์ฒ๋ผ ๊ธฐ๋ณธํ int๋ก ์ด๋ฃจ์ด์ง ๋ฐฐ์ด์ IntStream์ผ๋ก ๋ณํํ ์ ์๋ค.
1
2
int[] numbers = {2, 3, 5, 7, 11, 13};
int sum = Arrays.stream(numbers).sum(); // ํฉ๊ณ๋ 41
5.8.4 ํ์ผ๋ก ์คํธ๋ฆผ ๋ง๋ค๊ธฐ
ํ์ผ์ ์ฒ๋ฆฌํ๋ ๋ฑ์ I/O ์ฐ์ฐ์ ์ฌ์ฉํ๋ ์๋ฐ์ NIO API(๋น๋ธ๋ก I/O)๋ ์คํธ๋ฆผ API๋ฅผ ํ์ฉํ ์ ์๋๋ก ์ ๋ฐ์ดํธ๋์๋ค. java.nio.file.Files์ ๋ง์ ์ ์ ๋ฉ์๋๊ฐ ์คํธ๋ฆผ์ ๋ฐํํ๋ค.
์๋ฅผ ๋ค์ด Files.lines๋ ์ฃผ์ด์ง ํ์ผ์ ํ ์คํธ๋ฆผ์ ๋ฌธ์์ด๋ก ๋ฐํํ๋ค. ์ง๊ธ๊น์ง ๋ฐฐ์ด ์คํธ๋ฆผ ์ฐ์ฐ์ ํ์ฉํ๋ฉด ๋ค์ ์ฝ๋์ฒ๋ผ ํ์ผ์์ ๊ณ ์ ํ ๋จ์ด ์๋ฅผ ์ฐพ๋ ํ๋ก๊ทธ๋จ์ ๋ง๋ค ์ ์๋ค.
1
2
3
4
5
6
7
8
9
10
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) {
// ํ์ผ์ ์ด๋ค๊ฐ ์์ธ๊ฐ ๋ฐ์ํ๋ฉด ์ฒ๋ฆฌํ๋ค.
}
Files.lines๋ก ํ์ผ์ ๊ฐ ํ ์์๋ฅผ ๋ฐํํ๋ ์คํธ๋ฆผ์ ์ป์ ์ ์๋ค. ์คํธ๋ฆผ์ ์์ค๊ฐ I/O ์์์ด๋ฏ๋ก ์ด ๋ฉ์๋๋ฅผ try/catch ๋ธ๋ก์ผ๋ก ๊ฐ์๊ณ ๋ฉ๋ชจ๋ฆฌ ๋์๋ฅผ ๋ง์ผ๋ ค๋ฉด ์์์ ๋ซ์์ผ ํ๋ค.
๊ธฐ์กด์๋ finally ๋ธ๋ก์์ ์์์ ๋ซ์๋ค. Stream ์ธํฐํ์ด์ค๋ AutoCloseable ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๋ค. ๋ฐ๋ผ์ try ๋ธ๋ก ๋ด์ ์์์ ์๋์ผ๋ก ๊ด๋ฆฌ๋๋ค.
5.8.5 ํจ์๋ก ๋ฌดํ ์คํธ๋ฆผ ๋ง๋ค๊ธฐ
์คํธ๋ฆผ API๋ ํจ์์์ ์คํธ๋ฆผ์ ๋ง๋ค ์ ์๋ ๋ ์ ์ ๋ฉ์๋ Stream.iterate์ Stream.generate๋ฅผ ์ ๊ณตํ๋ค. ๋ ์ฐ์ฐ์ ์ด์ฉํด์ ๋ฌดํ ์คํธ๋ฆผ, ์ฆ ํฌ๊ธฐ๊ฐ ๊ณ ์ ๋์ง ์์ ์คํธ๋ฆผ์ ๋ง๋ค ์ ์๋ค.
iterate์ generate์์ ๋ง๋ ์คํธ๋ฆผ์ ์์ฒญํ ๋๋ง๋ค ์ฃผ์ด์ง ํจ์๋ฅผ ์ด์ฉํด์ ๊ฐ์ ๋ง๋ค๊ธฐ ๋๋ฌธ์ ๋ฌด์ ํ์ผ๋ก ๊ฐ์ ๊ณ์ฐํ ์ ์๋ค. ๋ฌดํํ ๊ฐ์ ์ถ๋ ฅํ์ง ์๋๋ก ๋ณดํต limit(n) ํจ์๋ฅผ ํจ๊ป ์ฌ์ฉํ๋ค.
iterate ๋ฉ์๋
1
2
3
Stream.iterate(0, n -> n + 2)
.limit(10)
.forEach(System.out::println);
- (์ด๊น๊ฐ 0, ๋๋ค) ์ธ์
2์ฉ ์ปค์ง๋ ์ฐ์ฐ์ ์งํํ๊ณ ์์ฒญํ ๋๋ง๋ค ๊ฐ์ ์์ฐํ ์ ์์ผ๋ฉฐ ๋์ด ์์ผ๋ฏ๋ก ๋ฌดํ ์คํธ๋ฆผ์ ๋ง๋ ๋ค. โ ์ธ๋ฐ์ด๋ ์คํธ๋ฆผ
์ผ๋ฐ์ ์ผ๋ก ์ฐ์๋ ์ผ๋ จ์ ๊ฐ์ ๋ง๋ค ๋ iterate๋ฅผ ์ฌ์ฉํ๋ค.
Quiz. ํผ๋ณด๋์น์์ด ์งํฉ
ํผ๋ณด๋์น์์ด์ ์ ๋ช ํ ๊ณ ์ ํ๋ก๊ทธ๋๋ฐ ๋ฌธ์ ๋ค. ํผ๋ณด๋์น์์ด์ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 โฆ ๊ฐ์ ์์ผ๋ก ๊ตฌ์ฑ๋๋ค. ์์ด์ 0, 1๋ก ์์ํ๋ฉฐ ์ดํ์ ์ซ์๋ ์ด์ ๋ ์ซ์๋ฅผ ๋ํ ๊ฐ์ด๋ค. ํผ๋ณด๋์น์์ด์ ์งํฉ๋ ๋น์ทํ ํํ๋ก ๊ตฌ์ฑ๋๋ค. ์ฆ, (0, 1), (1, 1), (1, 2), (2, 3), (3, 5), (5, 8), (8, 13), (13, 21)โฆ ์ฒ๋ผ ์ฐ์์ ์ธ ์ซ์๋ก ์ด๋ฃจ์ด์ง๋ค.
์ฐ๋ฆฌ๊ฐ ํด์ผํ ์ผ์ iterate ๋ฉ์๋๋ฅผ ์ด์ฉํด์ ํผ๋ณด๋์น์์ด์ ์งํฉ์ 20๊ฐ ๋ง๋๋ ๊ฒ์ด๋ค.
์ฐ์ UnaryOperator
๋ฅผ ์ธ์๋ก ๋ฐ๋ iterate ๋ฉ์๋๋ฅผ ์ด์ฉํด์ (0, 1) ๊ฐ์ ์งํฉ์ ๋ง๋ค์ด์ผ ํ๋ค. ์ฐ์ ๋ ์์๋ฅผ ํฌํจํ๋ ๋ฐฐ์ด๋ก ๋์ถฉ ์งํฉ์ ๋ง๋ค์ด ๋ณผ ์ ์๋ค. ์ฆ, new int[]{0, 1}๋ก ํผ๋ณด๋์น์์ด ์งํฉ (0, 1)์ ํํํ ์ ์๋ค. ์ด๋ฅผ iterate ๋ฉ์๋์ ์ด๊น๊ฐ์ผ๋ก ์ฌ์ฉํ๋ค. 1 2 3
Stream.iterate(new int[]{0, 1}, ???) .limit(20) .forEach(t -> System.out.println("(" + t[0] + "," + t[1] + ")"));
์ ์ฝ๋์์ ???๋ก ํ์๋ ๋ถ๋ถ์ ํด๊ฒฐํด์ผ ํ๋ค. iterate๋ ???์๋ฆฌ์ ์ฃผ์ด์ง๋ ๋๋ค๋ฅผ ์ฐ์์ ์ผ๋ก ์ ์ฉํ๋ ํจ์๋ผ๋ ์ฌ์ค์ ๊ธฐ์ตํ๋ฉฐ ํด์ฆ๋ฅผ ํ์ด๋ณด์.
์ ๋ต
1 2 3
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 ๋ฉ์๋๋ ํ๋ ๋์ผ์ดํธ๋ฅผ ์ง์ํ๋ค.
1
2
3
// 0์์ ์์ํด์ 100๋ณด๋ค ํฌ๋ฉด ์ซ์ ์์ฑ ์ค๋จ
IntStream.iterate(0, n -> n < 100, n -> n + 4)
.forEach(System.out::println)
iterate ๋ฉ์๋๋ ๋ ๋ฒ์งธ ์ธ์๋ก ํ๋ ๋์ผ์ดํธ๋ฅผ ๋ฐ์ ์ธ์ ๊น์ง ์์ ์ ์ํํ ๊ฒ์ธ์ง์ ๊ธฐ์ค์ผ๋ก ์ฌ์ฉํ๋ค. filter ๋์์ผ๋ก๋ ๊ฐ์ ๊ฒฐ๊ณผ๋ฅผ ์ป์ ์ ์์ ๊ฒ์ด๋ผ ์๊ฐํ์ ์๋ ์์ ๊ฒ์ด๋ค.
1
2
3
IntStream.iterate(0, n -> n + 4)
.filter(n -> n < 100)
.forEach(System.out::println);
์ํ๊น๊ฒ๋ ์ด์ ๊ฐ์ ๋ฐฉ๋ฒ์ผ๋ก๋ ๊ฐ์ ๊ฒฐ๊ณผ๋ฅผ ์ป์ ์ ์๋ค. ์ค์ ๋ก ์ ์ฝ๋๋ ์ข ๋ฃ๋์ง ์๋๋ค. filter ๋ฉ์๋๋ ์ธ์ ์ด ์์ ์ ์ค๋จํด์ผ ํ๋์ง๋ฅผ ์ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค. ์คํธ๋ฆผ ์ผํธ์ํท์ ์ง์ํ๋ takeWhile์ ์ด์ฉํ๋ ๊ฒ์ด ํด๋ฒ์ด๋ค.
1
2
3
IntStream.iterate(0, n -> n + 4)
.takeWhile(n -> n < 100)
.forEach(System.out::println);
generate ๋ฉ์๋
iterate์ ๋น์ทํ๊ฒ generate๋ ์๊ตฌํ ๋ ๊ฐ์ ๊ณ์ฐํ๋ ๋ฌดํ ์คํธ๋ฆผ์ ๋ง๋ค ์ ์๋ค. ํ์ง๋ง iterate์ ๋ฌ๋ฆฌ generate๋ ์์ฐ๋ ๊ฐ ๊ฐ์ ์ฐ์์ ์ผ๋ก ๊ณ์ฐํ์ง ์๋๋ค. generate๋ Supplier
1
2
3
Stream.generate(Math::random)
.limit(5)
.forEach(System.out::println);
์ด ์ฝ๋๋ 0๊ณผ 1 ์ฌ์ด์์ ์์์ ๋๋ธ ์ซ์ ๋ค์ฏ ๊ฐ๋ฅผ ๋ง๋ ๋ค. ๋ค์์ ์คํ ๊ฒฐ๊ณผ๋ค.
1
2
3
4
5
0.9410810294106129
0.6586270755634592
0.9592859117266873
0.13743396659487006
0.3942776037651241
Math.random์ ์์์ ์๋ก์ด ๊ฐ์ ์์ฑํ๋ ์ ์ ๋ฉ์๋๋ค. limit๊ฐ ์๋ค๋ฉด ์คํธ๋ฆผ์ ์ธ๋ฐ์ด๋ ์ํ๊ฐ ๋๋ค.
generate๋ ์ํ๊ฐ ์๋ ๋ฉ์๋, ์ฆ ๋์ค์ ๊ณ์ฐ์ ์ฌ์ฉํ ์ด๋ค ๊ฐ๋ ์ ์ฅํด๋์ง ์๋ ๋ฉ์๋๋ฅผ ํ์ฉํ๋ค.
์ ์์ ์์ IntStream์ ์ด์ฉํ๋ฉด ๋ฐ์ฑ ์ฐ์ฐ ๋ฌธ์ ๋ฅผ ํผํ ์ ์๋ค. IntStream์ generate ๋ฉ์๋๋ Supplier
1
IntStream ones = IntStream.generate(() -> 1);
IntSupplier ์ธํฐํ์ด์ค์ ์ ์๋ getAsInt๋ฅผ ๊ตฌํํ๋ ๊ฐ์ฒด๋ฅผ ๋ช ์์ ์ผ๋ก ์ ๋ฌํ ์๋ ์๋ค.
1
2
3
4
5
IntStream twos = IntStream.generate(new IntSupplier(){
public int getAsInt() {
return 2;
}
});
์ต๋ช ํด๋์ค์ ๋๋ค๋ ๋น์ทํ๋ค. ํ์ง๋ง ์ต๋ช ํด๋์ค๋ ๋ฉ์๋์ ์ฐ์ฐ์ ์ปค์คํฐ๋ง์ด์ฆํ ์ ์๋ ์ํ ํ๋๋ฅผ ์ ์ํ ์ ์๋ค๋ ์ ์ด ๋ค๋ฅด๋ค.
โ ๋ถ์์ฉ์ด ์๊ธธ ์ ์๋ ์์ ์ด๋ค.
๋๋ค๋ฅผ ์ฌ์ฉํ์ ๋๋ ๋ถ์์ฉ์ด ์์๋ค.(์ํ๋ฅผ ๋ฐ๊พธ์ง ์๊ธฐ ๋๋ฌธ)
1
2
3
4
5
6
7
8
9
10
11
12
13
// ํผ๋ณด๋์น ์์๋ฅผ ๋ฐํํ๋๋ก IntSupplier ๊ตฌํ
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);
IntSupplier ๊ฐ์ฒด๋ ๊ธฐ์กด ํผ๋ณด๋์น ์์์ ๋ ์ธ์คํด์ค ๋ณ์์ ์ด๋ค ํผ๋ณด๋์น ์์๊ฐ ๋ค์ด์๋์ง ์ถ์ ํ๋ฏ๋ก ๊ฐ๋ณ ์ํ ๊ฐ์ฒด๋ค. getAsInt๋ฅผ ํธ์ถํ๋ฉด ๊ฐ์ฒด ์ํ๊ฐ ๋ฐ๋๋ฉฐ ์๋ก์ด ๊ฐ์ ์์ฑํ๋ค.
iterate๋ฅผ ์ฌ์ฉํ์ ๋๋ ๊ฐ ๊ณผ์ ์์ ์๋ก์ด ๊ฐ์ ์์ฑํ๋ฉด์๋ ๊ธฐ์กด ์ํ๋ฅผ ๋ฐ๊พธ์ง ์๋ ์์ํ ๋ถ๋ณ ์ํ๋ฅผ ์ ์งํ๋ค.