ํ๋ ๋์ผ์ดํธ ํํฐ๋ง ๋ฐฉ๋ฒ๊ณผ๊ณ ์ ์์๋ง ํํฐ๋งํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์ด๋ค.
filter ๋ฉ์๋๋ ํ๋ ๋์ผ์ดํธ(๋ถ๋ฆฌ์ธ์ ๋ฐํํ๋ ํจ์)๋ฅผ ์ธ์๋ก ๋ฐ์์ ํ๋ ๋์ผ์ดํธ์ ์ผ์นํ๋ ๋ชจ๋ ์์๋ฅผ ํฌํจํ๋ ์คํธ๋ฆผ์ ๋ฐํํ๋ค.
// ์ฑ์์๋ฆฌ๋ฅผ ํํฐ๋งํด์ ๋ฉ๋ด ๋ง๋ค๊ธฐ
List<Dish> vegetarianMenu = menu.stream()
.filter(Dish::lsVegetarian)
.collect(toList())๏ผ
// ๋ฆฌ์คํธ์ ๋ชจ๋ ์ง์๋ฅผ ์ ํํ๊ณ ์ค๋ณต์ ํํฐ๋ง
List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
numbers.stream()
.filter(i -> i % 2 == 0)
.distinct()
.forEach(System.out::println);
์คํธ๋ฆผ์ ์์๋ฅผ ์ ํํ๊ฑฐ๋ ์คํตํ๋ ๋ค์ํ ๋ฐฉ๋ฒ์ ์ค๋ช ํ๋ค.
๋ค์๊ณผ ๊ฐ์ ํน๋ณํ ์๋ฆฌ ๋ชฉ๋ก์ ๊ฐ๊ณ ์๋ค๊ณ ๊ฐ์ ํ์.
List<Dish> specialMenu = A rrays.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๋ฅผ ๋ค์์ฒ๋ผ ์ด์ฉํ ์ ์๋ค๋ ์๊ฐ์ด ๋ค ๊ฒ์ด๋ค.
List<Dish> filteredMenu
= specialMenu.stream()
.filter(dish -> dish.getCalories() < 320)
.collect(toList());
๋ฆฌ์คํธ๊ฐ ์ด๋ฏธ ์นผ๋ก๋ฆฌ ์์ผ๋ก ์ ๋ ฌ๋์ ์๋ค๋ ์ฌ์ค์ ์ฃผ๋ชฉํ์.
filter๋ฅผ ์ฌ์ฉํ๋ฉด ๋ชจ๋ ์์์ ์ ์ฉํ ๊ฒ์ด๋ค.
์ ๋ ฌ๋์ด ์๋ค๋ฉด 320์นผ๋ก๋ฆฌ ๋ณด๋ค ํฌ๊ฑฐ๋ ๊ฐ์ ์๋ฆฌ๊ฐ ๋์์ ๋ ๋ฐ๋ณต์ ๋ฉ์ถ๋ ๊ฒ์ด ํจ์จ์ ์ด๋ค.
takeWhile์ ์ฌ์ฉํ๋ฉด ์ด๋ฅผ ๊ฐ๋จํ ์ฒ๋ฆฌํ ์ ์๋ค.
List<Dish> slicedMenul
= specialMenu.stream()
.takeWhile(dish -> dish.getCalories() < 320)
.collect(toList());
320์นผ๋ก๋ฆฌ๋ณด๋ค ํฐ ์์๋ ์ด๋ป๊ฒ ํ์ํ ๊น?
dropWhile์ ์ด์ฉํด ์ด์์
์ ์๋ฃํ ์ ์๋ค.
List<Dish> slicedMenu2
= specialMenu.stream()
.dropWhile(dish -> dish.getCalories() < 320)
.collect(toList())
ํ๋ ๋์ผ์ดํธ๊ฐ ๊ฑฐ์ง์ด ๋๋ฉด ๊ทธ ์ง์ ์์ ์์ ์ ์ค๋จํ๊ณ ๋จ์ ๋ชจ๋ ์์๋ฅผ ๋ฐํํ๋ค. dropWhile์ ๋ฌดํํ ๋จ์ ์์๋ฅผ ๊ฐ์ง ๋ฌดํ ์คํธ๋ฆผ์์๋ ๋์ํ๋ค.
์คํธ๋ฆผ์ ์ฃผ์ด์ง ๊ฐ ์ดํ์ ํฌ๊ธฐ๋ฅผ ๊ฐ๋ ์๋ก์ด ์คํธ๋ฆผ์ ๋ฐํํ๋ limit(n) ๋ฉ์๋๋ฅผ ์ง์ํ๋ค. ์คํธ๋ฆผ์ด ์ ๋ ฌ๋์ด ์์ผ๋ฉด ์ต๋ ์์ n๊ฐ๋ฅผ ๋ฐํํ ์ ์๋ค.
List<Dish> dishes = specialMenu.stream()
.filter(dish -> dish.getCalories() > 300)
.limit(3)
.collect(toList());
์ ๋ ฌ๋์ง ์์ ์คํธ๋ฆผ(์๋ฅผ ๋ค๋ฉด ์์ค๊ฐ Set)์๋ limit๋ฅผ ์ฌ์ฉํ ์ ์๋ค. ์์ค๊ฐ ์ ๋ ฌ๋์ด์์ง ์์๋ค๋ฉด limit์ ๊ฒฐ๊ณผ๋ ์ ๋ ฌ๋์ง ์์์ํ๋ก ๋ฐํ๋๋ค.
์คํธ๋ฆผ์ ์ฒ์ n๊ฐ ์์๋ฅผ ์ ์ธํ ์คํธ๋ฆผ์ ๋ฐํํ๋ skip(n) ๋ฉ์๋๋ฅผ ์ง์ํ๋ค.
List<Dish> dishes = menu.stream()
.filter(d -> d.getCalories() > 300)
.skip(2)
.collect(toList());
// ์คํธ๋ฆผ์ ์ด์ฉํด์ ์ฒ์ ๋ฑ์ฅํ๋ ๋ ๊ณ ๊ธฐ ์๋ฆฌ๋ฅผ ํํฐ๋งํ์์ค.
List<Dish> dishes =
menu.stream()
.filter(d -> d.getType() == Dish.Type.MEAT)
.limit(2)
.collect(toList());
์คํธ๋ฆผ์ ํจ์๋ฅผ ์ธ์๋ก ๋ฐ๋ map ๋ฉ์๋๋ฅผ ์ง์ํ๋ค. ์ธ์๋ก ์ ๊ณต๋ ํจ์๋ ๊ฐ ์์์ ์ ์ฉ๋๋ฉฐ ํจ์๋ฅผ ์ ์ฉํ ๊ฒฐ๊ณผ๊ฐ ์๋ก์ด ์์๋ก ๋งคํ๋๋ค.
// ์๋ฆฌ๋ช
์ ์ถ์ถํ๋ ์ฝ๋
List<String> dishNames = menu.stream()
.map(Dish::getName)
.collect(toList());
getName์ ๋ฌธ์์ด์๋ฐํํ๋ฏ๋ก map ๋ฉ์๋์ ์ถ๋ ฅ ์คํธ๋ฆผ์ Stream<String> ํ์์ ๊ฐ๋๋ค.
// ๊ฐ ์๋ฆฌ๋ช
์ ๊ธธ์ด๋ฅผ ์๊ณ ์ถ๋ค๋ฉด ์ด๋ป๊ฒ ํด์ผ ํ ๊น?
List<Integer> dishNameLengths = menu.stream()
.map(Dish::getName)
.map(String::length)
.collect(toList());
["Hello", "World"] ๋ฆฌ์คํธ๊ฐ ์๋ค๋ฉด ๊ฒฐ๊ณผ๋ก ["H", "e", "ใ ฃ", V , "W", "r", d']๋ฅผํฌํจํ๋ ๋ฆฌ์คํธ๊ฐ ๋ฐํ๋์ด์ผ ํ๋ค๊ณ ๊ฐ์ ํ์.
words.stream()
.map(word -> word.split(""))
.distinct()
.collect(toList());
๋ค์๊ณผ ๊ฐ์ด ์๊ฐํ์ ์ ์์ง๋ง map์ผ๋ก ์ ๋ฌํ ๋๋ค๋ ๊ฐ ๋จ์ด์ String[](๋ฌธ์์ด ๋ฐฐ์ด)์ ๋ฐํํ๋ค๋ ์ ์ด ๋ฌธ์ ๋ค. ๋ฐ๋ผ์ map ๋ฉ์๋๊ฐ ๋ฐํํ ์คํธ๋ฆผ์ ํ์์ Stream<String[]>์ด๋ค. ์ฐ๋ฆฌ๊ฐ ์ํ๋ ๊ฒ์ ๋ฌธ์์ด์ ์คํธ๋ฆผ์ ํํํ Stream<String>์ด๋ค.
flatMap์ด๋ผ๋ ๋ฉ์๋๋ฅผ ์ด์ฉํด์ ์ด๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์๋ค.
๋ฌธ์์ด ๋ฐฐ์ด์ ๋ฐ์ ๋ฌธ์์ด ์คํธ๋ฆผ์ผ๋ก ๋ณํํ๋ ๋ฐฉ๋ฒ์ ์์์ผ ํ๋ค.
๊ทธ ์ญํ ์ Arrays.stream๊ฐ ํด์ค๋ค.
String[] arrayOfWords = {"Goodbye", "World"};
Stream<String> streamOfwords = Arrays.stream(arrayOfWords);
์ ์์ ์ ํ์ดํ๋ผ์ธ์ Arrays.stream() ๋ฉ์๋๋ฅผ ์ ์ฉํด๋ณด์.
words.stream()
.map(word -> word.split("")) // ๊ฐ ๋จ์ด๋ฅผ ๊ฐ๋ณ ๋ฌธ์์ด ๋ฐฐ์ด๋ก ๋ณํ
.map(Arrays::stream) // ๊ฐ ๋ฐฐ์ด์ ๋ณ๋์ ์คํธ๋ฆผ์ผ๋ก ์์ฑ
.distinct()
.collect(toList());
List<Stream<String>>๊ฐ ๋ง๋ค์ด์ง๋ฉด์ ๋ฌธ์ ๊ฐ ํด๊ฒฐ๋์ง ์๋๋ค.
๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ค๋ฉด ๋จผ์ ๊ฐ ๋จ์ด๋ฅผ ๊ฐ๋ณ ๋ฌธ์์ด๋ก ์ด๋ฃจ์ด์ง ๋ฐฐ์ด๋ก ๋ง๋ ๋ค์์ ๊ฐ ๋ฐฐ
์ด์ ๋ณ๋์ ์คํธ๋ฆผ์ผ๋ก ๋ง๋ค์ด์ผ ํ๋ค.
List<String> uniqueCharacters =
words.stream()
.map(word -> word.split("")) // ๊ฐ ๋จ์ด๋ฅผ ๊ฐ๋ณ ๋ฌธ์๋ฅผ ํฌํจํ๋ ๋ฐฐ์ด๋ก ๋ณํ
.flatMap(Arrays::rstream) // ์์ฑ๋ ์คํธ๋ฆผ์ ํ๋์ ์คํธ๋ฆผ์ผ๋ก ํ๋ฉดํ
.distinct()
.collect(toList());
map(Arrays::stream)๊ณผ ๋ฌ๋ฆฌ flatMap์ ํ๋์ ํ๋ฉดํ๋ ์คํธ๋ฆผ์ ๋ฐํํ๋ค.
์ ๋ต
List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> collect = nums.stream()
.map(num -> num * num)
.collect(Collectors.toList());
์ ๋ต
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(Collectors.toList());
1) ์ฑ
๋ฐฉ๋ฒ
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(Collectors.toList());
2) ๋ด ๋ฐฉ๋ฒ
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}))
.filter(i -> Arrays.stream(i).sum() % 3 == 0)
.collect(Collectors.toList());
ํ๋ ๋์ผ์ดํธ๊ฐ ์ฃผ์ด์ง ์คํธ๋ฆผ์์ ์ ์ด๋ ํ ์์์ ์ผ์นํ๋์ง ํ์ธํ ๋ anyMatch ๋ฉ์๋๋ฅผ ์ด์ฉํ๋ค. ์๋ฅผ ๋ค์ด ๋ค์ ์ฝ๋๋ menu์ ์ฑ์์๋ฆฌ๊ฐ ์๋์ง ํ์ธํ๋ ์์ ๋ค.
if(menu.stream().anyMatch(Dish::isVegetarian)) {
System.out.println("The menu is (somewhat) vegetarian friendly!!");
}
์คํธ๋ฆผ์ ๋ชจ๋ ์์๊ฐ ์ฃผ์ด์ง ํ๋ ๋์ผ์ดํธ์ ์ผ์นํ๋์ง ๊ฒ์ฌํ๋ค. ์๋ฅผ ๋ค์ด ๋ฉ๋ด๊ฐ ๊ฑด๊ฐ์(๋ชจ๋ ์๋ฆฌ๊ฐ 1000์นผ๋ก๋ฆฌ ์ดํ๋ฉด ๊ฑด๊ฐ์์ผ๋ก ๊ฐ์ฃผ) ์ธ์ง ํ์ธํ ์ ์๋ค.
boolean isHealthy = menu.stream()
.allMatch(dish -> dish.getCalories() < 1000);
noneMatch๋ allMatch์ ๋ฐ๋ ์ฐ์ฐ์ ์ํํ๋ค. ์ฆ, noneMatch๋ ์ฃผ์ด์ง ํ๋ ๋์ผ์ดํธ์ ์ผ์นํ๋์์๊ฐ ์๋์ง ํ์ธํ๋ค.
boolean isHealthy = menu.stream()
.noneMatch(d -> d.getCalories() >= 1000);
anyMatch, allMatch, noneMatch ์ธ ๋ฉ์๋๋ ์คํธ๋ฆผ ์ผํธ์ํท ๊ธฐ๋ฒ๏ผ์ฆ ์๋ฐ์ &&๏ผ||์ ๊ฐ์ ์ฐ์ฐ์ ํ์ฉํ๋ค.
์๋ฅผ ๋ค์ด if(false && true && true) ๋ผ๋ฉด ์ฒซ๋ฒ์งธ false๊ฐ ๋์ค๋ฉด ๋ฐ๋ก if๋ฌธ ๊ฒ์ฌ๋ฅผ ๋๋ธ๋ค.(๋ค์ true๋ ๋ณด์ง๋ ์์)
findAny ๋ฉ์๋๋ ํ์ฌ ์คํธ๋ฆผ์์ ์์์ ์์๋ฅผ ๋ฐํํ๋ค. ์๋ฅผ ๋ค์ด ๋ค์ ์ฝ๋์ฒ๋ผ filter์ findAny๋ฅผ ์ด์ฉํด์ ์ฑ์์๋ฆฌ๋ฅผ ์ ํํ ์ ์๋ค.
Optional<Dish> dish =
menu.stream()
.filter(Dish::isVegetarian)
.findAny();
์คํธ๋ฆผ ํ์ดํ๋ผ์ธ์ ๋ด๋ถ์ ์ผ๋ก ๋จ์ผ ๊ณผ์ ์ผ๋ก ์คํํ ์ ์๋๋ก ์ต์ ํ๋๋ค. ์ฆ, ์ผํธ์ํท์ ์ด์ฉํด์ ๊ฒฐ๊ณผ๋ฅผ ์ฐพ๋ ์ฆ์ ์คํ์ ์ข ๋ฃํ๋ค.
Optional<T> ํด๋์ค(java.util.Optional)๋ ๊ฐ์ ์กด์ฌ๋ ๋ถ์ฌ ์ฌ๋ถ๋ฅผ ํํํ๋ ์ปจํ ์ด๋ ํด๋์ค๋ค. ์ด์ ์์ ์์ findAny๋ ์๋ฌด ์์๋ ๋ฐํํ์ง ์์ ์ ์๋ค. null์ ์ฝ๊ฒ ์๋ฌ๋ฅผ ์ผ์ผํฌ ์ ์์ผ๋ฏ๋ก ์๋ฐ 8 ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค๊ณ์๋Optional<T>๋ฅผ ๋ง๋ค์๋ค. ์์ธํ๊ฑด 10์ฅ์์ ์์๋ณด์.
๋ค์ ์ฌ์ค๋ง ์์๋์!
menu.stream()
.filter(Dish::isVegetarian)
.findAny() // Optional<Dish> ๋ฐํ
.ifPresent(dish -> System.out.println(dish.getName());
// ๊ฐ์ด ์์ผ๋ฉด ์ถ๋ ฅ๋๊ณ ์์ผ๋ฉด ์๋ฌด ์ผ๋ ์ผ์ด๋์ง ์๋๋ค.
์คํธ๋ฆผ์์ ์ฒซ ๋ฒ์งธ ์์๋ฅผ ์ฐพ์ผ๋ ค๋ฉด ์ด๋ป๊ฒ ํด์ผ ํ ๊น?
List<Integer> someNumbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> firstSquareDivisibleByThree =
someNumbers.stream()
.map(n -> n * n)
.filter(n -> n % 3 == 0)
.findFirst(); // 9
findFirst VS findAny
๋ณ๋ ฌ ์คํ์์๋ ์ฒซ ๋ฒ์งธ ์์๋ฅผ ์ฐพ๊ธฐ ์ด๋ ต๋ค. ๋ฐ๋ผ์ ์์์ ๋ฐํ ์์๊ฐ ์๊ด์๋ค๋ฉด ๋ณ๋ ฌ ์คํธ๋ฆผ์์๋ ์ ์ฝ์ด ์ ์ findAny๋ฅผ ์ฌ์ฉํ๋ค.
๋ฆฌ๋์ฑ ์ฐ์ฐ : ๋ชจ๋ ์คํธ๋ฆผ ์์๋ฅผ ์ฒ๋ฆฌํด์ ๊ฐ์ผ๋ก ๋์ถ
ํจ์ํ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด ์ฉ์ด๋ก๋ ์ด ๊ณผ์ ์ด ๋ง์น ์ข
์ด(์ฐ๋ฆฌ์ ์คํธ๋ฆผ)๋ฅผ ์์ ์กฐ๊ฐ์ด ๋ ๋๊น์ง ๋ฐ๋ณตํด์ ์ ๋ ๊ฒ๊ณผ ๋น์ทํ๋ค๋ ์๋ฏธ๋ก ํด๋(fold)๋ผ๊ณ ๋ถ๋ฅธ๋ค.
int sum = 0;
for (int x : numbers) {
sum += x;
}
// ์คํธ๋ฆผ์์ ๋ค์๊ณผ ๊ฐ์ด ์ฌ์ฉํ ์ ์๋ค.
int sum = numbers.stream().reduce(0, (a, b) -> a + b);
์ฒซ๋ฒ์งธ ์์ : ์ด๊น๊ฐ 0
๋๋ฒ์งธ ์์ : BinaryOperator<T>
Optional<Integer> sum = numbers.stream().reduce((a, b) -> (a + b));
์ด๊น๊ฐ์ด ์์ผ๋ฉด ์ Optional์ ๋ฐํํ ๊น?
์ด๊น๊ฐ๋ ์๊ณ ์คํธ๋ฆผ์ ์๋ฌด ์์๋ ์๋ค๋ฉด ํฉ๊ณ๊ฐ ์์์ ๊ฐ๋ฆฌํฌ ์ ์๋๋ก Optional ๊ฐ์ฒด๋ก ๊ฐ์ผ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํด์ผ ํ๋ค.
// ์ต๋๊ฐ
Optional<Integer> max = numbers.stream().reduce(Integer::max);
// ์ต์๊ฐ
Optional<Integer> min = numbers.stream().reduce(Integer::min);
map๊ณผ reduce ๋ฉ์๋๋ฅผ ์ด์ฉํด์ ์คํธ๋ฆผ์ ์๋ฆฌ ๊ฐ์๋ฅผ ๊ณ์ฐํ์์ค.
int count = menu.stream()
.map(d -> 1)
.reduce(0, (a, b) -> a + b);
๋ฐ๋ณต์ ์ธ ํฉ๊ณ์์๋ sum ๋ณ์๋ฅผ ๊ณต์ ํด์ผ ํ๋ฏ๋ก ์ฝ๊ฒ ๋ณ๋ ฌํํ๊ธฐ ์ด๋ ต๋ค. ๊ฐ์ ์ ์ผ๋ก ๋๊ธฐํ์ํค๋๋ผ๋ ๊ฒฐ๊ตญ ๋ณ๋ ฌํ๋ก ์ป์ด์ผ ํ ์ด๋์ด ์ค๋ ๋ ๊ฐ์ ์๋ชจ์ ์ธ ๊ฒฝ์ ๋๋ฌธ์ ์์๋์ด ๋ฒ๋ฆฐ๋ค. ์ฆ ์ด ์์
์ ๋ณ๋ ฌํํ๋ ค๋ฉด ์
๋ ฅ์ ๋ถํ ํ๊ณ , ๋ถํ ๋ ์
๋ ฅ์ ๋ํ ๋ค์์, ๋ํ ๊ฐ์ ๊ณ ์ณ์ผ ํ๋ค.
ํ์ง๋ง ์คํธ๋ฆผ์ ์ด์ฉํ๋ฉด ์ฝ๊ฒ ๋ณ๋ ฌํ ํ ์ ์๋ค.
int sum = numbers.parallelStream(),reduce(0, Integer::sum);
์ ์ฝ๋๋ฅผ ๋ณ๋ ฌ๋ก ์คํํ๋ ค๋ฉด ๋๊ฐ๋ฅผ ์ง๋ถํด์ผ ํ๋ค. ์ฆ, reduce์ ๋๊ฒจ์ค ๋๋ค์ ์ํ(์ธ์คํด์ค ๋ณ์๊ฐ์)๊ฐ ๋ฐ๋์ง ๋ง์์ผ ํ๋ฉฐ, ์ฐ์ฐ์ด ์ด๋ค์์๋ก ์คํ๋๋๋ผ๋ ๊ฒฐ๊ณผ๊ฐ ๋ฐ๋์ง ์๋ ๊ตฌ์กฐ์ฌ์ผ ํ๋ค.
map, filter ๋ฑ์ ์ ๋ ฅ ์คํธ๋ฆผ์์ ๊ฐ ์์๋ฅผ ๋ฐ์ 0 ๋๋ ๊ฒฐ๊ณผ๋ฅผ ์ถ๋ ฅ ์คํธ๋ฆผ์ผ๋ก๋ณด๋ธ๋ค. ๋ฐ๋ผ์ (์ฌ์ฉ์๊ฐ ์ ๊ณตํ ๋๋ค๋ ๋ฉ์๋ ์ฐธ์กฐ๊ฐ ๋ด๋ถ์ ์ธ ๊ฐ๋ณ ์ํ๋ฅผ ๊ฐ์ง ์๋๋ค๋ ๊ฐ์ ํ์) ์ด๋ค์ ๋ณดํต ์ํ๊ฐ ์๋, ์ฆ ๋ด๋ถ ์ํ๋ฅผ ๊ฐ์ง ์๋ ์ฐ์ฐ(stateless operation)์ด๋ค.
ํ์ง๋ง reduce, sum, max ๊ฐ์ ์ฐ์ฐ์ ๊ฒฐ๊ณผ๋ฅผ ๋์ ํ ๋ด๋ถ ์ํ๊ฐ ํ์ํ๋ค. ์์ ์ ๋ด๋ถ ์ํ๋ ์์๊ฐ์ด๋ค. ์ฐ๋ฆฌ ์์ ์์๋ int ๋๋ double์ ๋ด๋ถ ์ํ๋ก ์ฌ์ฉํ๋ค. ์คํธ๋ฆผ์์ ์ฒ๋ฆฌํ๋ ์์์์ ๊ด๊ณ์์ด ๋ด๋ถ ์ํ์ ํฌ๊ธฐ๋ ํ์ ๋์ด ์๋ค.
๋ฐ๋ฉด sorted๋ distinct ๊ฐ์ ์ฐ์ฐ์ filter๋ map์ฒ๋ผ ์คํธ๋ฆผ์ ์ ๋ ฅ์ผ๋ก ๋ฐ์ ๋ค๋ฅธ ์คํธ๋ฆผ์ ์ถ๋ ฅํ๋ ๊ฒ์ฒ๋ผ ๋ณด์ผ์ ์๋ค. ํ์ง๋ง sorted๋ distinct๋ filter๋map๊ณผ๋ ๋ค๋ฅด๋ค. ์คํธ๋ฆผ์ ์์๋ฅผ ์ ๋ ฌํ๊ฑฐ๋ ์ค๋ณต์ ์ ๊ฑฐํ๋ ค๋ฉด ๊ณผ๊ฑฐ์ ์ด๋ ฅ์ ์๊ณ ์์ด์ผ ํ๋ค. ์๋ฅผ ๋ค์ด ์ด๋ค์์๋ฅผ ์ถ๋ ฅ ์คํธ๋ฆผ์ผ๋ก ์ถ๊ฐํ๋ ค๋ฉด ๋ชจ๋ ์์๊ฐ ๋ฒํผ์ ์ถ๊ฐ๋์ด ์์ด์ผ ํ๋ค. ์ฐ์ฐ์ ์ํํ๋ ๋ฐ ํ์ํ ์ ์ฅ์ ํฌ๊ธฐ๋ ์ ํด์ ธ์์ง ์๋ค. ๋ฐ๋ผ์ ๋ฐ์ดํฐ ์คํธ๋ฆผ์ ํฌ๊ธฐ๊ฐ ํฌ๊ฑฐ๋ ๋ฌดํ์ด๋ผ๋ฉด ๋ฌธ์ ๊ฐ ์๊ธธ์ ์๋ค. ์ด๋ฌํ ์ฐ์ฐ์ ๋ด๋ถ์ํ๋ฅผ ๊ฐ๋ ์ฐ์ฐ์ด๋ผ ํ๋ค.
์ ๋ฆฌํ์๋ฉด map, filter์ฒ๋ผ ๋ด๋ถ ์ํ๋ฅผ ๊ฐ์ง ์๋ ๋ฉ์๋์ reduce, sum, max, sorted, distinct์ฒ๋ผ ๋ด๋ถ ์ํ๋ฅผ ๊ฐ์ง๊ณ ์์ด์ผ ํ๋ ๋ฉ์๋๊ฐ ์กด์ฌํ๋ค.
๋ค์๊ณผ ๊ฐ์ด ๋ฉ๋ด์ ์นผ๋ฆฌ๋ก ํฉ๊ณ๋ฅผ ๊ณ์ฐํ ์ ์๋ค.
int calories = menu.stream()
.map(Dish::getCalories)
.reduce(0, Integer::sum);
์ฌ์ค ์ ์ฝ๋์๋ ๋ฐ์ฑ ๋น์ฉ์ด ์จ์ด์๋ค. ๋ด๋ถ์ ์ผ๋ก ํฉ๊ณ๋ฅผ ๊ณ์ฐํ๊ธฐ ์ ์ Integer๋ฅผ ๊ธฐ๋ณธํ์ผ๋ก ์ธ๋ฐ์ฑ ํด์ผํ๋ค.
๋ค์ ์ฝ๋์ฒ๋ผ ์ง์ sum๋ฉ์๋๋ฅผ ํธ์ถํ ์ ์๋ค๋ฉด ์ข์ง ์์๊น?
int calories = menu.stream()
.map(Dish::getCalories)
.sum();
ํ์ง๋ง ์ด ์ฝ๋๋ sum()๋ฉ์๋๋ฅผ ์ง์ ํธ์ถํ ์ ์๋ค. map์ด Stream<T>๋ฅผ ์์ฑํ๊ธฐ ๋๋ฌธ์ด๋ค. ์คํธ๋ฆผ์ ์์ ํ์์ Integer์ง๋ง ์ธํฐํ์ด์ค์๋ sum ๋ฉ์๋๊ฐ ์๋ค.
์ sum ๋ฉ์๋๊ฐ ์์๊น? ์๋ฅผ๋ค์ด menu์ฒ๋ผ Stream<Dish> ํ์์ ์์๋ง ์๋ค๋ฉด sum์ด๋ผ๋ ์ฐ์ฐ์ ์ํํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
๊ทธ๋ผ ๊ณ์ ๋ฐ์ฑ ๋น์ฉ์ ๊ฐ์ํ๊ณ reduce๋ฅผ ์ฌ์ฉํด์ผ ํ ๊น?
๋คํํ ์คํ๋ฆผ API์์๋ ๊ธฐ๋ณธํ ํนํ ์คํธ๋ฆผ์ ์ ๊ณตํ๋ค.
IntStream, DoubleStream, LongStream์ด ์กด์ฌํ๋ค.
mapToInt, mapToDouble, mapToLong ์ธ ๊ฐ์ง ๋ฉ์๋๋ฅผ ๊ฐ์ฅ ๋ง์ด ์ฌ์ฉํ๋ค.
map๊ณผ ์ ํํ ๊ฐ์ ๊ธฐ๋ฅ์ ์ํํ์ง๋ง๏ผStream<T> ๋์ ํนํ๋ ์คํธ๋ฆผ์ ๋ฐํํ๋ค.(IntStream)
int calories = menu.stream() // Stream<Dish> ๋ฐํ
.mapToInt(Dish::getCalories) // IntStram๋ฐํ
.sum();
์ซ์ ์คํธ๋ฆผ์ ์์ํ์ธ ํนํ๋์ง ์์ ์คํธ๋ฆผ์ผ๋ก ๋ณต์ํ ์ ์์๊น? boxed
IntStrea mintStream = menu.stream().mapToInt(Dish::getCalories);
Stream<Integer> stream = intStream.boxed(); // ์ซ์ ์คํธ๋ฆผ์ ์คํธ๋ฆผ์ผ๋ก ๋ณํ
์คํธ๋ฆผ์ ์์๊ฐ ์์ ๊ฒฝ์ฐ ์ต๋, ์ต์๋ ์์ ๊ฒ์ด๋ค. ์ด๋ป๊ฒ ํด์ผํ ๊น?
Optionallnt maxCalories = menu.stream()
.mapToInt(Dish::getCalories)
.max();
Optionallnt๋ก ๋ฐํํ๋ฉด ๋๋ค. ์ต๋๊ฐ์ด ์๋ ๊ฒฝ์ฐ ๊ธฐ๋ณธ๊ฐ์ ๋ช ์์ ์ผ๋ก ์ ์ํ ์ ์๋ค.
int max = maxCalories.orElse(1);
IntStream evenNumbers = IntStream.rangeClosed(1, 100) // [1,100] ํฌํจ
.filter(n -> n % 2 == 0);
System.out.println(evenNumbers.count()); // 50๊ฐ
range(1,100) // 49๊ฐ, (1,100) ๋ฏธํฌํจ
์๋ตํ๊ฒ ๋ธ.. ๋ฐฐ๋ณด๋ค ๋ฐฐ๊ผฝ์ด ๋ ์ปค์ง๋๊ฑฐ ๊ฐ์์ ๊ฐ์ธ์ ์ผ๋ก ๊ณต๋ถ!
์ด ์ ์์๋ ์ผ๋ จ์ ๊ฐ, ๋ฐฐ์ด, ํ์ผ, ์ฌ์ง์ด ํจ์๋ฅผ ์ด์ฉํ ๋ฌดํ ์คํธ๋ฆผ ๋ง๋ค๊ธฐ ๋ฑ ๋ค์ํ ๋ฐฉ์์ผ๋ก ์คํธ๋ฆผ์ ๋ง๋๋ ๋ฐฉ๋ฒ์ ์ค๋ช ํ๋ค.
์์์ ์๋ฅผ ์ธ์๋ก ๋ฐ๋ ์ ์ ๋ฉ์๋ Stream.of๋ฅผ ์ด์ฉํด์ ์คํธ๋ฆผ์ ๋ง๋ค ์ ์๋ค.
Stream<String> stream = Stream.of("Modern", "Java", "In", "Action");
stream.map(String::toUpperCase).forEach(System.out::println);
// empty ๋ฉ์๋๋ฅผ ์ด์ฉํด์ ์คํธ๋ฆผ์ ๋น์ธ์ ์๋ค.
Stream<String> emptyStream = Stream.empty();
์๋ฐ 9 ์ด์ ์๋ ๋ค์๊ณผ ๊ฐ์ด null์ ํ์ธํด์ค์ผ ํ๋ค.
String homeValue = System.getProperty("home");
Stream<String> homeValueStream = homeValue == null ? Stream.empty() : Stream.of(value);
getProperty
: ์๋ฐ๋ฅผ ์คํํ ๋, ์คํ๋๋ ๊ณณ์ ์ ๋ณด๋ฅผ ์ป์ด์ค๊ฑฐ๋ ์ด์์ฒด์ ์ ์ ๋ณด๊ฐ ํ์ํ ๋๊ฐ ์๋ค. ์คํ ์์น์ ์๋ ํ์ผ์ ์ฝ์ด๋๋ ค์ผ ํ๋๋ฐ, ํ์ฌ ์์น๋ฅผ ์ ์ ์๋ ๋ฐฉ๋ฒ ๋ฑ ์์คํ
์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ฌ ๋ System.getProperty(key) ๋ฅผ ์ฌ์ฉํ๋ค.
key๋ java ์์คํ
์์ฑ๋ชฉ๋ก ๊ฒ์ํ๋ฉด ๋์จ๋ค.
ํ์ง๋ง ์๋ฐ 9์ดํ์๋ ๋ค์๊ณผ ๊ฐ์ด ์ฝ๋๋ฅผ ๊ตฌํํ ์ ์๋ค.
Stream<String> homeValueStream
= Stream.ofNullable(System.getProperty("home"));
null์ด ๋ ์ ์๋ ๊ฐ์ฒด๋ฅผ ํฌํจํ๋ ์คํธ๋ฆผ๊ฐ์ flatMap๊ณผ ํจ๊ป ์ฌ์ฉํ๋ ์ํฉ์์๋ ์ด ํจํด์ ๋ ์ ์ฉํ๊ฒ ์ฌ์ฉํ ์ ์๋ค.
Stream<String> values =
Stream.of("config", "home", "user")
.flatMap(key -> Stream.ofNullable(System.getProperty(key)));
Arrays.stream์ ์ด์ฉํ๊ธฐ
int[] numbers = {2, 3, 5, 7, 11, 13};
int sum = Arrays.stream(numbers).sum();
long uniqueWords = 0;
// AutoCloseable์ด๋ฏ๋ก tryโfinally๊ฐ ํ์์๋ค.
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) {
// ์์ธ์ฒ๋ฆฌ
}
Paths.get("data.txt") : "data.txt"์ ํด๋นํ๋ Path ๊ฐ์ฒด ๋ฐํ
Files.lines : ํด๋น ๊ฒฝ๋ก์ ์๋ ํ์ผ ์ฝ์ด์ ์คํธ๋ฆผ์ผ๋ก ๋ฐํ
์ ์ ๋ฉ์๋ Stream.iterate์ Stream, generate๋ฅผ ์ ๊ณตํ๋ค. ๋ ์ฐ์ฐ์ ์ด์ฉํด์ ๋ฌดํ ์คํธ๋ฆผ, ์ฆ ๊ณ ์ ๋ ์ปฌ๋ ์
์์ ๊ณ ์ ๋ ํฌ๊ธฐ๋ก ์คํธ๋ฆผ์ ๋ง๋ค์๋ ๊ฒ๊ณผ๋ ๋ฌ๋ฆฌ ํฌ๊ธฐ๊ฐ ๊ณ ์ ๋์ง ์์ ์คํธ๋ฆผ์ ๋ง๋ค ์ ์๋ค.
iterate์ generate์์ ๋ง๋ ์คํธ๋ฆผ์ ์์ฒญํ ๋๋ง๋ค ์ฃผ์ด์ง ํจ์๋ฅผ ์ด์ฉํด์ ๊ฐ์ ๋ง๋ ๋ค. ๋ฐ๋ผ์ ๋ฌด์ ํ์ผ๋ก ๊ฐ์ ๊ณ์ฐํ ์ ์๋ค. ํ์ง๋ง ๋ณดํต ๋ฌดํํ ๊ฐ์ ์ถ๋ ฅํ์ง ์๋๋ก limit(n) ํจ์๋ฅผ ํจ๊ป ์ฐ๊ฒฐํด์ ์ฌ์ฉํ๋ค.
Stream.iterate(0, n -> n + 2)
.limit(10)
.forEach(System.out::println);
์ด๊ธฐ๊ฐ 0, ๋๋ค๋ฅผ ์ด์ฉํด์ ๋ฌดํํ ์ง์ ์คํธ๋ฆผ์ ๋ง๋ค ์ ์๋ค.
์ด๋ฌํ ๋ฌดํ ์คํธ๋ฆผ์ ์ธ๋ฐ์ด๋ ์คํธ๋ฆผ์ด๋ผ๊ณ ๋ถ๋ฅธ๋ค.
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55... ๊ฐ์ ์์ผ๋ก ๊ตฌ์ฑ๋๋ค. ๋ฌดํ ์คํธ๋ฆผ์ผ๋ก ์ด๋ป๊ฒ ๊ตฌํํด์ผ ํ ๊น?
Stream.iterate(new int[]{0, 1}, t -> new int[]{t[1], t[0] + t[1]})
.limit(10)
.map(t -> t[0])
.forEach(System.out::println);
์๋ฐ 9์ iterate ๋ฉ์๋๋ ํ๋ ๋์ผ์ดํธ๋ฅผ ์ง์ํ๋ค. ์๋ฅผ ๋ค์ด 0์์ ์์ํด์ 100๋ณด๋ค ํฌ๋ฉด์ซ์ ์์ฑ์ ์ค๋จํ๋ ์ฝ๋๋ฅผ ๋ค์์ฒ๋ผ ๊ตฌํํ ์ ์๋ค.
IntStream.iterate(0, n -> n < 100, n -> n + 4)
.forEach(System.out::println);
๋ค์๊ณผ ๊ฐ์ด filter์ ์ด์ฉํด๋ ๋์ง ์์๊น?
IntStream.iterate(0, n -> n + 4)
.filter(n -> n < 100)
.forEach(System.out::println);
์์ฝ๊ฒ๋ ์ด ์ฝ๋๋ ๋ฌดํ์คํธ๋ฆผ์ ๋ง๋ ๋ค. filter ์ ์ฅ์์๋ ์ธ์ ์ด ์์ ์ ์ค๋จํด์ผ ํ๋์ง ์ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
์คํธ๋ฆผ ์ผํธ์ํท์ ์ง์ํ๋ takeWhile์ ์ด์ฉํ๋ ๊ฒ์ด ํด๋ฒ์ด๋ค.
IntStream.iterate(0 , n - > n + 4)
.takeWhile(n -> n < 100)
.forEach(System.out::println);
iterate์ ๋ฌ๋ฆฌ generate๋ ์์ฐ๋ ๊ฐ ๊ฐ์ ์ฐ์์ ์ผ๋ก ๊ณ์ฐํ์ง ์๋๋ค. generate๋ supplier<T>๋ฅผ ์ธ์๋ก ๋ฐ์์ ์๋ก์ด ๊ฐ์ ์์ฐํ๋ค.
Stream.generate(Math::random)
.limit(5)
.forEach(System.out::println);
generate๋ ์ฐ๋ฆฌ๊ฐ ์ฌ์ฉํ ๋ฐํ์(supplier : ๋ฉ์๋ ์ฐธ์กฐ Math.random)๋ ์ํ๊ฐ ์๋ ๋ฉ์๋, ์ฆ ๋์ค์ ๊ณ์ฐ์ ์ฌ์ฉํ ์ด๋ค ๊ฐ๋ ์ ์ฅํด๋์ง ์๋๋ค.
ํ์ง๋ง ๋ฐํ์(supplier)๊ฐ ๋ฐ๋์ ์ํ๊ฐ ์์ด์ผ ํ๋ ๊ฒ์ ์๋๋ค. ๋ค์ ๋ถ๋ถ์์ ์ค๋ช ํ๊ฒ ์ง๋ง ์ํ๊ฐ ๊ฐ๋ฅํ๊ฒ ์ฝ๋๋ฅผ ์์ฑํ ์ ์๋ค. ๊ทธ๋ฌ๋ ๋ณ๋ ฌ์ฒ๋ฆฌ(๋์์ฑ)์์ ๋ฌธ์ ๊ฐ ๋๋ค๋ ๊ฒ์ ์๊ณ ์์ด์ผ ํ๋ค.
iterate์์ ์ค๋ช ํ ํผ๋ณด๋์น ์์ด ์์ ๋ฅผ ๊ฐ์ ธ์ฌ ๊ฒ์ด๋ค. ์ถ๊ฐ์ ์ผ๋ก ๋ฐ์ฑ์ฐ์ฐ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด IntStream์ ์ฌ์ฉํ ๊ฒ์ด๋ฉฐ IntStream์ Supplier<T> ๋์ IntSupplier๋ฅผ ์ธ์๋กค ๋ฐ๋๋ค๋ ๊ฒ์ ๊ธฐ์ตํ์.
IntSupplier์ ์ต๋ช ๊ฐ์ฒด๋ฅผ ์ด์ฉํ์ฌ ๋ค์๊ณผ ๊ฐ์ด ์ฌ์ฉํ ์ ์๋ค.
IntStreamtwos = IntStream.generate(new IntSupplier() {
public int getAsInt() {
return 2;
}
});
์ด๋ฐ์์ผ๋ก IntSupplier์ ์ปค์คํฐ๋ง์ด์งํ์ฌ ํผ๋ณด๋์น ์์ด์ ๊ตฌํํด๋ณด์!
IntSupplierfib = 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 ๊ฐ์ฒด์ ์ํ๋ ๊ฐ๋ณ์ํ์ด๋ค. ์ธ์คํด์ค ํ๋์ ๊ฐ์ด ๊ณ์ ๋ณํ๊ธฐ ๋๋ฌธ์ด๋ค. ์์์ ๋งํ๋ฏ์ด ์ฌ๋ฌ ์ฐ๋ ๋๊ฐ ์ ๊ทผํ์์ ๋ ๋์์ฑ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ๊ฒ์ด๋ค.
๋ฐ๋๋ก iterate๋ ๋ถ๋ณ์ํ๋ฅผ ์ ์งํ๋ค. ๋ฐ๋ผ์ ์คํธ๋ฆผ์ ๋ณ๋ ฌ๋ก ์ฒ๋ฆฌํ๋ฉด์ ์ฌ๋ฐ๋ฅธ ๊ฒฐ๊ณผ๋ฅผ ์ป์ผ๋ ค๋ฉด ๋ถ๋ณ ์ํ ๊ธฐ๋ฒ์ ์ ์งํด์ผ ํ๋ค. ์ด๋ 7์ฅ์์ ๋ฐฐ์ฐ๋๋ก ํ์!
๋ฌดํ์คํธ๋ฆผ์์๋ limit์ด์ฉํด ๋ช ์์ ์ผ๋ก ์คํธ๋ฆผ ํฌ๊ธฐ๋ฅผ ์ ํ ํด์ผํ๋ฉฐ ๋ฌดํ ์คํธ๋ฆผ์ ์์๋ ๋ฌดํ์ ์ผ๋ก ๊ณ์ฐ์ด ๋ฐ๋ณต๋๋ฏ๋ก ์ ๋ ฌํ๊ฑฐ๋ ๋ฆฌ๋์คํ ์ ์๋ค.
์ฐธ๊ณ ์๋ฃ)
๋ณธ ํฌ์คํ
์ ๋ชจ๋ ์๋ฐ ์ธ ์ก์
์ ์ฐธ๊ณ ํ์ฌ ์์ฑํ์์ต๋๋ค.