[03] Stream

MIIINยท2023๋…„ 3์›” 12์ผ
0

Modern Java

๋ชฉ๋ก ๋ณด๊ธฐ
3/8
post-thumbnail

๐ŸŒŠ Stream

Stream์ด๋ž€ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ์—ฐ์‚ฐ์„ ์ง€์›ํ•˜๋„๋ก ์†Œ์Šค์—์„œ ์ถ”์ถœ๋œ ์—ฐ์†๋œ ์š”์†Œ๋กœ ์ •์˜ํ•œ๋‹ค.

  • ์—ฐ์†๋œ ์š”์†Œ
    - Collection์ฒ˜๋Ÿผ ํŠน์ • ์š”์†Œ ํ˜•์‹์œผ๋กœ ์ด๋ฃจ์–ด์ง„ ์—ฐ์†๋œ ๊ฐ’ ์ง‘ํ•ฉ์˜ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณตํ•œ๋‹ค.
  • ์†Œ์Šค
    - Collection, Array, I/O Resource ๋“ฑ์˜ ๋ฐ์ดํ„ฐ ์ œ๊ณต ์†Œ์Šค๋กœ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ์†Œ๋น„ํ•œ๋‹ค.
  • ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ์—ฐ์‚ฐ
    - ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด์—์„œ ์ผ๋ฐ˜์ ์œผ๋กœ ์ง€์›ํ•˜๋Š” ์—ฐ์‚ฐ๊ณผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ๋น„์Šทํ•œ ์—ฐ์‚ฐ์„ ์ง€์›ํ•œ๋‹ค.

์ŠคํŠธ๋ฆผ์˜ 2๊ฐ€์ง€ ์ค‘์š” ํŠน์ง•

  1. Pipelining
    • ๋Œ€๋ถ€๋ถ„์˜ Stream ์—ฐ์‚ฐ์€ Stream ์—ฐ์‚ฐ๋ผ๋ฆฌ ์—ฐ๊ฒฐํ•ด์„œ ์ปค๋‹ค๋ž€ Pipeline์„ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๋„๋ก Stream ์ž์‹ ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
    • Jaziness, short-circuiting ์ตœ์ ํ™”๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.
  2. ๋‚ด๋ถ€ ๋ฐ˜๋ณต
    • ๋ฐ˜๋ณต์ž(Iterator)๋ฅผ ์‚ฌ์šฉํ•˜๋Š” Collection๊ณผ ๋‹ฌ๋ฆฌ ๋‚ด๋ถ€ ๋ฐ˜๋ณต์„ ์ง€์›ํ•œ๋‹ค.
-- ์นผ๋กœ๋ฆฌ๊ฐ€ ๊ฐ€์žฅ ๋†’์€ ์ˆœ์œผ๋กœ 3๊ฐ€์ง€ ์š”๋ฆฌ
List<String> threeHighCaloricDishNames =
				menu.stream()
						.sorted(Comparator.comparingInt(Dish::getCalories).reversed())
						.map(Dish::getName)
						.limit(3)
						.collect(Collectors.toList());

๐Ÿพ Stream vs Collection

Collection๊ณผ Stream ๋ชจ๋‘ ์—ฐ์†๋œ ์š”์†Œ ํ˜•์‹์˜ ๊ฐ’์„ ์ €์žฅํ•˜๋Š” ์ž๋ฃŒ๊ตฌ์กฐ์˜ Interface๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

๋ฐ์ดํ„ฐ๋ฅผ ์–ธ์ œ ๊ณ„์‚ฐํ•˜๋Š”๊ฐ€?

  • Collection์€ ํ˜„์žฌ ์ž๋ฃŒ๊ตฌ์กฐ๊ฐ€ ํฌํ•จํ•˜๋Š” ๋ชจ๋“  ๊ฐ’์„ ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅํ•˜๋Š” ์ž๋ฃŒ๊ตฌ์กฐ์ด๋‹ค.
    ๋”ฐ๋ผ์„œ, Collection์˜ ๋ชจ๋“  ์š”์†Œ๋Š” Collection์— ์ถ”๊ฐ€ํ•˜๊ธฐ ์ „์— ๊ณ„์‚ฐ(์—ฐ์‚ฐ)๋˜์–ด์•ผ ํ•œ๋‹ค.
  • Stream์€ ์š”์ฒญํ•˜๋Š” ๊ฐ’(๋ฐ์ดํ„ฐ)๋งŒ ๊ณ„์‚ฐํ•˜๋Š” ๊ณ ์ •๋œ ์ž๋ฃŒ๊ตฌ์กฐ์ด๋‹ค.

๋‹จ 1๋ฒˆ๋งŒ ํƒ์ƒ‰ํ•  ์ˆ˜ ์žˆ๋‹ค

  • Collection์˜ Iterator(๋ฐ˜๋ณต์ž)์ฒ˜๋Ÿผ Stream๋„ 1๋ฒˆ๋งŒ ํƒ์ƒ‰ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์ด๋ฏธ 1๋ฒˆ ํƒ์ƒ‰ํ•œ ์š”์†Œ๋ฅผ ๋‹ค์‹œ ํƒ์ƒ‰ํ•˜๋ ค๋ฉด ๋ฐ์ดํ„ฐ ์†Œ์Šค์—์„œ ์ƒˆ๋กœ์šด Stream์„ ๋งŒ๋“ค์–ด์•ผ ํ•œ๋‹ค.

์™ธ๋ถ€ ๋ฐ˜๋ณต๊ณผ ๋‚ด๋ถ€ ๋ฐ˜๋ณต

-- 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 ์—ฐ์‚ฐ์„ ์ค‘๊ฐ„ ์—ฐ์‚ฐ์ด๋ผ๊ณ  ํ•˜๋ฉฐ, Stream์„ ๋‹ซ๋Š” ์—ฐ์‚ฐ์„ ์ตœ์ข… ์—ฐ์‚ฐ์ด๋ผ๊ณ  ํ•œ๋‹ค.

์ค‘๊ฐ„ ์—ฐ์‚ฐ

OperatorReturn TypeFunctional InterfaceFunction Descriptor
filterStream<T>Predicate<T>T -> boolean
distinctStream<T>
takeWhileStream<T>Predicate<T>T -> boolean
dropWhileStream<T>Predicate<T>T -> boolean
skipStream<T>long
limitStream<T>long
mapStream<R>Predicate<T, R>T -> R
flatMapStream<R>Predicate<T, Stream<R>>T -> Stream<R>
sortedStream<T>Comparator<T>(T, T) -> int

์ตœ์ข… ์—ฐ์‚ฐ

OperatorReturn TypeFunctional InterfaceFunction Descriptor
anyMatchbooleanPredicate<T>T -> boolean
noneMatchbooleanPredicate<T>T -> boolean
allMatchbooleanPredicate<T>T -> boolean
findAnyOptional<T>
findFirstOptional<T>
forEachvoidConsumer<T>T -> void
collectRCollector<T, A, R>
reduceOptional<T>BinaryOperator<T>(T, T) -> T
countlong

๐Ÿ‡ Filtering

1. filter

Predicate๋ฅผ ์ธ์ˆ˜๋กœ ๋ฐ›์•„์„œ ์ผ์น˜ํ•˜๋Š” ๋ชจ๋“  ์š”์†Œ๋ฅผ ํฌํ•จํ•˜๋Š” Stream์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

List<Dish> vegetarianMenu = menu.stream().filter(Dish::isVegetarian).collect(toList());

2. distinct

๊ณ ์œ  ์š”์†Œ(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);

โšก Stream Slicing

1. takeWhile, dropWhile

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());

2. limit

์ฃผ์–ด์ง„ ๊ฐ’(n) ์ดํ•˜์˜ ํฌ๊ธฐ๋ฅผ ๊ฐ–๋Š” ์ƒˆ๋กœ์šด Stream์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

List<Dish> dishes = specialMenu.stream().filter(dish -> dish.getCalories() > 300).limit(3).collect(toList());

3. skip

์ฒ˜์Œ n๊ฐœ ์š”์†Œ๋ฅผ ์ œ์™ธํ•œ Stream์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

List<Dish> dishes = menu.stream().filter(d -> d.getCalories() > 300).skip(2).collect(toList());

๐Ÿธ Mapping

1. map

Function์„ ์ธ์ˆ˜๋กœ ๋ฐ›์œผ๋ฉฐ, Function์€ ๊ฐ ์š”์†Œ์— ์ ์šฉ๋˜์–ด ๊ทธ ๊ฒฐ๊ณผ๊ฐ€ ์ƒˆ๋กœ์šด ์š”์†Œ๋กœ ๋งคํ•‘๋œ๋‹ค.

List<Integer> dishNameLengths = menu.stream().map(Dish::getName).map(String::length).collect(toList());

2. flatMap

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());

๐ŸŽก Searching & Matching

1. anyMatch

Predicate๋ฅผ ์ธ์ˆ˜๋กœ ๋ฐ›์œผ๋ฉฐ, ์ ์–ด๋„ ํ•œ ์š”์†Œ์™€ ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์ธํ•˜์—ฌ boolean์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

boolean isAnyMatch = menu.stream().anyMatch(Dish::isVegetarian);

2. allMatch, noneMatch

Predicate๋ฅผ ์ธ์ˆ˜๋กœ ๋ฐ›์œผ๋ฉฐ, ๋ชจ๋“  ์š”์†Œ์™€ ์ผ์น˜/๋ถˆ์ผ์น˜ ํ•˜๋Š”์ง€ ํ™•์ธํ•˜์—ฌ boolean์„ ๋ฐ˜ํ™˜ํ•˜๋‹ค.

-- ๋ชจ๋‘ ์ผ์น˜
boolean isAllMatch = menu.stream().allMatch(Dish::isVegetarian);

-- ๋ชจ๋‘ ๋ถˆ์ผ์น˜
boolean isNoneMatch = menu.stream().noneMatch(Dish::isVegetarian);

3. findAny

์ž„์˜์˜ ์š”์†Œ(Optional ๊ฐ์ฒด)๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. Paralle Stream์€ ์ฒซ ๋ฒˆ์žฌ ์š”์†Œ๋ฅผ ์ฐพ๊ธฐ ํž˜๋“ค๊ธฐ์— findAny๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

Optional<Dish> dish = menu.stream().filter(Dish::isVegetarian).findAny();

4. findFirst

์ฒซ ๋ฒˆ์งธ ์š”์†Œ(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();

๐Ÿ Reducing

๋ชจ๋“  Stream ์š”์†Œ๋ฅผ ์ฒ˜๋ฆฌํ•ด์„œ ๊ฐ’์œผ๋กœ ๋„์ถœํ•˜๋Š” ๊ฒƒ์„ Reducing ์—ฐ์‚ฐ์ด๋ผ๊ณ  ํ•œ๋‹ค.

reduce

๋‘ ๊ฐœ์˜ ์ธ์ˆ˜๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋งŒ์•ฝ ์ฒซ๋ฒˆ์งธ ์ธ์ˆ˜(์ดˆ๊นƒ๊ฐ’)์ด ์—†์œผ๋ฉด Optional ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

  • ์ดˆ๊นƒ๊ฐ’
  • BinaryOperator<T>
-- ๋ชจ๋“  ๊ฐ’์„ ๋”ํ•˜๋Š” ์˜ˆ์ œ
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

Stream API๋Š” (Auto) Boxing์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•œ 3๊ฐ€์ง• ๊ธฐ๋ณธํ˜• ํŠนํ™” Stream (IntStream, DoubleStream, LongStream)์„ ์ œ๊ณตํ•œ๋‹ค. ๊ฐ๊ฐ์˜ ์ธํ„ฐํŽ˜์ด์Šค๋Š” ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ์ˆซ์ž ๊ด€๋ จ Reducing ์—ฐ์‚ฐ ์ˆ˜ํ–‰ ๋ฉ”์„œ๋“œ(sum, max ๋“ฑ)๋„ ์ œ๊ณตํ•œ๋‹ค.

mapToInt, mapToDouble, mapToLong

Stream<T>์„ ๊ธฐ๋ณธํ˜• ํŠนํ™” Stream (IntStream, DoubleStream, LongStream)์œผ๋กœ ๋ณ€ํ™˜ํ•œ๋‹ค.

-- mapToInt (IntStream์œผ๋กœ ๋ณ€ํ™˜)
int calories = menu.stream().mapToInt(Dish::getCalories).sum();

boxed

๊ธฐ๋ณธํ˜• ํŠนํ™” Stream์„ ๋‹ค์‹œ Stream<T>์œผ๋กœ ๋ณ€ํ™˜ํ•œ๋‹ค.

IntStream intStream = menu.stream().mapToInt(Dish::getCalories);
Stream<Integer> stream = intStream.boxed();

Reducing ์—ฐ์‚ฐ

-- 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());

Making Stream

1. Stream.of

์ •์  ๋ฉ”์„œ๋“œ๋กœ์จ ์ž„์˜์˜ ์ˆ˜๋ฅผ ์ธ์ˆ˜๋กœ ๋ฐ›์•„ 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();

2. Stream.ofNullable

Java SE 9์˜ ์ถ”๊ฐ€๋œ ์ •์  ๋ฉ”์„œ๋“œ๋กœ์จ null์ด ๋  ์ˆ˜ ์žˆ๋Š” ๊ฐœ์ฒด๋ฅผ ๋ฐ›์•„ Stream์„ ๋งŒ๋“ ๋‹ค.

Stream<String> values = 
		Stream.of("config", "home", "user")
				.flatMap(key -> Stream.ofNullable(System.getProperty(key)));

3. Arrays.stream

์ •์  ๋ฉ”์„œ๋“œ๋กœ์จ ๋ฐฐ์—ด์„ ์ธ์ˆ˜๋กœ ๋ฐ›์•„ 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();

4. NIO API

ํŒŒ์ผ 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) {

}

5. Stream.iterate

์ •์  ๋ฉ”์„œ๋“œ๋กœ์จ ์ดˆ๊ธฐ๊ฐ’๊ณผ 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);

6. Stream.generate

์ •์  ๋ฉ”์„œ๋“œ๋กœ์จ Supplier<T>๋ฅผ ์ธ์ˆ˜๋กœ ๋ฐ›์•„์„œ ๋ฌดํ•œ Stream์„ ๋งŒ๋“ ๋‹ค.

Stream.generate(Math::random).limit(5).forEach(System.out::println);

profile
๋ฐฑ์—”๋“œ๊ฐœ๋ฐœ์ž

0๊ฐœ์˜ ๋Œ“๊ธ€