Stream์ด๋ ๋ฐ์ดํฐ ์ฒ๋ฆฌ ์ฐ์ฐ์ ์ง์ํ๋๋ก ์์ค์์ ์ถ์ถ๋ ์ฐ์๋ ์์๋ก ์ ์ํ๋ค.
-- ์นผ๋ก๋ฆฌ๊ฐ ๊ฐ์ฅ ๋์ ์์ผ๋ก 3๊ฐ์ง ์๋ฆฌ
List<String> threeHighCaloricDishNames =
menu.stream()
.sorted(Comparator.comparingInt(Dish::getCalories).reversed())
.map(Dish::getName)
.limit(3)
.collect(Collectors.toList());
Collection๊ณผ Stream ๋ชจ๋ ์ฐ์๋ ์์ ํ์์ ๊ฐ์ ์ ์ฅํ๋ ์๋ฃ๊ตฌ์กฐ์ Interface๋ฅผ ์ ๊ณตํ๋ค.
-- Collection : for-each ์ธ๋ถ ๋ฐ๋ณต
List<String> names = new ArrayList<>();
for (Dish dish : menu) {
names.add(dish.getName());
}
-- Collection : Iterator ์ธ๋ถ ๋ฐ๋ณต
List<String> names = new ArrayList<>();
Iterator<String> iterator = menu.iterator();
while (iterator.hasNext()) {
Dish dish = iterator.next();
names.add(dish.getName());
}
-- Stream : ๋ด๋ถ ๋ฐ๋ณต
List<String> names = menu.stream().map(Dish::getName).collect(toList());
Stream์ ์ฐ์ฐ์ ๋ ๊ทธ๋ถ์ผ๋ก ๊ตฌ๋ถํ๋ค. ์ฐ๊ฒฐํ ์ ์๋ Stream ์ฐ์ฐ์ ์ค๊ฐ ์ฐ์ฐ์ด๋ผ๊ณ ํ๋ฉฐ, Stream์ ๋ซ๋ ์ฐ์ฐ์ ์ต์ข ์ฐ์ฐ์ด๋ผ๊ณ ํ๋ค.
Operator | Return Type | Functional Interface | Function Descriptor |
---|---|---|---|
filter | Stream<T> | Predicate<T> | T -> boolean |
distinct | Stream<T> | ||
takeWhile | Stream<T> | Predicate<T> | T -> boolean |
dropWhile | Stream<T> | Predicate<T> | T -> boolean |
skip | Stream<T> | long | |
limit | Stream<T> | long | |
map | Stream<R> | Predicate<T, R> | T -> R |
flatMap | Stream<R> | Predicate<T, Stream<R>> | T -> Stream<R> |
sorted | Stream<T> | Comparator<T> | (T, T) -> int |
Operator | Return Type | Functional Interface | Function Descriptor |
---|---|---|---|
anyMatch | boolean | Predicate<T> | T -> boolean |
noneMatch | boolean | Predicate<T> | T -> boolean |
allMatch | boolean | Predicate<T> | T -> boolean |
findAny | Optional<T> | ||
findFirst | Optional<T> | ||
forEach | void | Consumer<T> | T -> void |
collect | R | Collector<T, A, R> | |
reduce | Optional<T> | BinaryOperator<T> | (T, T) -> T |
count | long |
Predicate๋ฅผ ์ธ์๋ก ๋ฐ์์ ์ผ์นํ๋ ๋ชจ๋ ์์๋ฅผ ํฌํจํ๋ Stream์ ๋ฐํํ๋ค.
List<Dish> vegetarianMenu = menu.stream().filter(Dish::isVegetarian).collect(toList());
๊ณ ์ ์์(hasCode, euqals)๋ก ์ด๋ฃจ์ด์ง Stream์ ๋ฐํํ๋ค.
List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
numbers.stream().filter(i -> i % 2 == 0).distinct().forEach(system.out::println);
Java SE 9์ ์ถ๊ฐ๋ ์ฐ์ฐ์ด๋ฉฐ, ์คํธ๋ฆผ์ ์์๋ฅผ ํจ๊ณผ์ ์ผ๋ก ์ ํํ๋ค.
์์๋ค์ด ํํฐ๋ง ์กฐ๊ฑด์ผ๋ก ์ ๋ ฌ์ด ๋์ด์๋ ๊ฒฝ์ฐ์๋ Predicate๋ฅผ ๋ง์กฑํ๋ ์์ญ or Predicate๋ฅผ ๋ง์กฑํ์ง ์๋ ์์ญ์ผ๋ก ๋๋ ์ ์๋ค.
-- ์นผ๋ก๋ฆฌ๋ก ์ ๋ ฌ ๋์ด ์๋ List
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)
);
-- filter ์ฌ์ฉ ( ์ ์ฒด ์์์ Predicate ์ ์ฉ ํ ๋ฐํ)
List<Dish> filteredMenu = specialMenu.stream().filter(dish -> dish.getCalories() < 320).collect(toList());
-- takeWhile ์ฌ์ฉ ( Predicate๋ฅผ ๋ง์กฑํ์ง ์๋ ์์ ์ฒซ๋ฒ์งธ ๊น์ง ์ํ ํ ๋ฐํ)
List<Dish> slicedMenu1 = specialMenu.stream().takeWhile(dish -> dis.getCalories() < 320).collect(toList());
-- dropWhile ์ฌ์ฉ
List<Dish> slicedMenu2 = specialMenu.stream().dropWhile(dish -> dis.getCalories() < 320).collect(toList());
์ฃผ์ด์ง ๊ฐ(n) ์ดํ์ ํฌ๊ธฐ๋ฅผ ๊ฐ๋ ์๋ก์ด Stream์ ๋ฐํํ๋ค.
List<Dish> dishes = specialMenu.stream().filter(dish -> dish.getCalories() > 300).limit(3).collect(toList());
์ฒ์ n๊ฐ ์์๋ฅผ ์ ์ธํ Stream์ ๋ฐํํ๋ค.
List<Dish> dishes = menu.stream().filter(d -> d.getCalories() > 300).skip(2).collect(toList());
Function์ ์ธ์๋ก ๋ฐ์ผ๋ฉฐ, Function์ ๊ฐ ์์์ ์ ์ฉ๋์ด ๊ทธ ๊ฒฐ๊ณผ๊ฐ ์๋ก์ด ์์๋ก ๋งคํ๋๋ค.
List<Integer> dishNameLengths = menu.stream().map(Dish::getName).map(String::length).collect(toList());
Function์ ์ธ์๋ก ๋ฐ์ผ๋ฉฐ, ๋ฐฐ์ด์ด๋ ๊ฐ์ฒด๋ก ๋ฌถ์ธ Stream์ ๋จ์ผ ์์ Stream์ผ๋ก ๋ฐํํ๋ค.
String[] words = {"Hello", "world"};
-- map ( ๊ฒฐ๊ณผ List<Stream<String>> : [("H","e","l","l","o"), ("w","o","r","l","d")])
words.stream()
.map(word -> word.split(""))
.map(Array::stream)
.collect(toList());
-- flatMap ( ๊ฒฐ๊ณผ List<String> : ["H","e","l","l","o","w","o","r","l","d"]
words.stream()
.map(word -> word.split(""))
.flatMap(Array::stream)
.collect(toList());
Predicate๋ฅผ ์ธ์๋ก ๋ฐ์ผ๋ฉฐ, ์ ์ด๋ ํ ์์์ ์ผ์นํ๋์ง ํ์ธํ์ฌ boolean์ ๋ฐํํ๋ค.
boolean isAnyMatch = menu.stream().anyMatch(Dish::isVegetarian);
Predicate๋ฅผ ์ธ์๋ก ๋ฐ์ผ๋ฉฐ, ๋ชจ๋ ์์์ ์ผ์น/๋ถ์ผ์น ํ๋์ง ํ์ธํ์ฌ boolean์ ๋ฐํํ๋ค.
-- ๋ชจ๋ ์ผ์น
boolean isAllMatch = menu.stream().allMatch(Dish::isVegetarian);
-- ๋ชจ๋ ๋ถ์ผ์น
boolean isNoneMatch = menu.stream().noneMatch(Dish::isVegetarian);
์์์ ์์(Optional ๊ฐ์ฒด)๋ฅผ ๋ฐํํ๋ค. Paralle Stream์ ์ฒซ ๋ฒ์ฌ ์์๋ฅผ ์ฐพ๊ธฐ ํ๋ค๊ธฐ์ findAny๋ฅผ ์ฌ์ฉํ๋ค.
Optional<Dish> dish = menu.stream().filter(Dish::isVegetarian).findAny();
์ฒซ ๋ฒ์งธ ์์(Optional ๊ฐ์ฒด)๋ฅผ ๋ฐํํ๋ค.
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();
๋ชจ๋ Stream ์์๋ฅผ ์ฒ๋ฆฌํด์ ๊ฐ์ผ๋ก ๋์ถํ๋ ๊ฒ์ Reducing ์ฐ์ฐ์ด๋ผ๊ณ ํ๋ค.
๋ ๊ฐ์ ์ธ์๋ฅผ ๊ฐ์ง ์ ์์ผ๋ฉฐ, ๋ง์ฝ ์ฒซ๋ฒ์งธ ์ธ์(์ด๊น๊ฐ)์ด ์์ผ๋ฉด Optional ๊ฐ์ฒด๋ฅผ ๋ฐํํ๋ค.
-- ๋ชจ๋ ๊ฐ์ ๋ํ๋ ์์
int sum = 0;
for (int x : numbers) {
sum += x;
}
-- reduce(์ด๊น๊ฐ, BinaryOperator<T\>)
int sum = numbers.stream().reduce(0, (sum, num) -> sum + num);
-- reduce(BinaryOperator<T\>)
Optional<Integer> sum = numbers.stream().reduce((sum, num) -> sum + num);
-- ์ต๋๊ฐ
Optional<Integer> max = numbers.stream().reduce(Integer::max);
-- ์ต์๊ฐ
Optional<Integer> min = numbers.stream().reduce(Integer::min);
Stream API๋ (Auto) Boxing์ ํผํ๊ธฐ ์ํ 3๊ฐ์ง ๊ธฐ๋ณธํ ํนํ Stream (IntStream, DoubleStream, LongStream)์ ์ ๊ณตํ๋ค. ๊ฐ๊ฐ์ ์ธํฐํ์ด์ค๋ ์์ฃผ ์ฌ์ฉํ๋ ์ซ์ ๊ด๋ จ Reducing ์ฐ์ฐ ์ํ ๋ฉ์๋(sum, max ๋ฑ)๋ ์ ๊ณตํ๋ค.
Stream<T>์ ๊ธฐ๋ณธํ ํนํ Stream (IntStream, DoubleStream, LongStream)์ผ๋ก ๋ณํํ๋ค.
-- mapToInt (IntStream์ผ๋ก ๋ณํ)
int calories = menu.stream().mapToInt(Dish::getCalories).sum();
๊ธฐ๋ณธํ ํนํ Stream์ ๋ค์ Stream<T>์ผ๋ก ๋ณํํ๋ค.
IntStream intStream = menu.stream().mapToInt(Dish::getCalories);
Stream<Integer> stream = intStream.boxed();
-- sum
int sum = menu.stream().mapToInt(Dish::getCalories).sum();
-- min (OptionalInt, OptionalDouble, OptionalLong ๋ฐํ)
int min = menu.stream().mapToInt(Dish::getCalories).min().getAsInt();
-- max (OptionalInt, OptionalDouble, OptionalLong ๋ฐํ)
int max = menu.stream().mapToInt(Dish::getCalories).max().getAsInt();
-- average (OptionalDouble ๋ฐํ)
double average = menu.stream().mapToInt(Dish::getCalories).average().getAsDouble();
-- range (์ข
๋ฃ๊ฐ ํฌํจ ํ์ง ์์ : 49๊ฐ)
IntStream evenNumbers1 = IntStream.range(1, 100).filter(n -> n % 2 == 0);
System.out.println(evenNumbers1.count());
-- rangeClosed (์ข
๋ฃ๊ฐ ํฌํจ : 50๊ฐ)
IntStream evenNumbers2 = IntStream.rangeClosed(1, 100).filter(n -> n % 2 == 0);
System.out.println(evenNumbers2.count());
์ ์ ๋ฉ์๋๋ก์จ ์์์ ์๋ฅผ ์ธ์๋ก ๋ฐ์ Stream์ ๋ง๋ ๋ค.
Stream<String> stream = Stream.of("Hello", "world");
stream.map(s -> s.toUpperCase().split("")).flatMap(Arrays::stream).forEach(System.out::print);
-- ๋น Stream ๋ง๋ค๊ธฐ
Stream<String> emptyStream = Stream.empty();
Java SE 9์ ์ถ๊ฐ๋ ์ ์ ๋ฉ์๋๋ก์จ null์ด ๋ ์ ์๋ ๊ฐ์ฒด๋ฅผ ๋ฐ์ Stream์ ๋ง๋ ๋ค.
Stream<String> values =
Stream.of("config", "home", "user")
.flatMap(key -> Stream.ofNullable(System.getProperty(key)));
์ ์ ๋ฉ์๋๋ก์จ ๋ฐฐ์ด์ ์ธ์๋ก ๋ฐ์ Stream์ ๋ง๋ ๋ค.
-- ๋ฐฐ์ด์ ํ์
์ ๋ฐ๋ผ IntStream, DoubleStream, LongStream์ ๋ฐํํ๋ค.
int[] numbers = {2, 3, 5, 7, 11, 13};
int sum = Arrays.stream(numbers).sum();
double[] doubles = {0.1, 0.31, 4.7, 6.5, 10.0};
double sum2 = Arrays.stream(doubles).sum();
ํ์ผ I/O ์ฐ์ฐ์ ์ฌ์ฉํ๋ NIO API๋ Stream API๋ฅผ ํ์ฉํ ์ ์์ผ๋ฉฐ, java.nio.file.Files์ ๋ง์ ์ ์ ๋ฉ์๋๊ฐ Stream์ ๋ฐํํ๋ค.
Stream ์ธํฐํ์ด์ค๋ AutoCloseable ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๋ฏ๋ก, Try-with-resources ๊ตฌ๋ฌธ์ผ๋ก ์์์ ์๋์ผ๋ก ๊ด๋ฆฌํ ์ ์๋ค.
-- Files.lines : ํ์ผ์ ๊ฐ ํ ์์๋ฅผ ๋ฐํํ๋ค.
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) {
}
์ ์ ๋ฉ์๋๋ก์จ ์ด๊ธฐ๊ฐ๊ณผ UnaryOperator<T>๋ฅผ ์ธ์๋ก ๋ฐ์์ ๋ฌดํ Stream (ํฌ๊ธฐ๊ฐ ๊ณ ์ ๋์ง ์์ Stream)์ ๋ง๋ ๋ค.
-- iterate : UnaryOperator<T\>
Stream.iterate(new int[]{0, 1}, a -> new int[]{a[1], a[0] + a[1]})
.limit(20)
.forEach(t -> System.out.println("(" + t[0] + "," + t[1] + ")"));
Java SE 9์์๋ Predicate<T>๋ ์ธ์๋ก ๋ฐ์ ์ธ์ ๊น์ง ์ํํ ๊ฒ์ธ๊ฐ์ ๋ํ ๊ธฐ์ค์ ์ค ์ ์๋ค.
-- iterate : Predicate<T\>, UnaryOperator<T\>
Stream.iterate(0, n -> n < 100, n -> n + 2)
.forEach(System.out::println);
์ ์ ๋ฉ์๋๋ก์จ Supplier<T>๋ฅผ ์ธ์๋ก ๋ฐ์์ ๋ฌดํ Stream์ ๋ง๋ ๋ค.
Stream.generate(Math::random).limit(5).forEach(System.out::println);