Open yh20studio opened 2 years ago
Stream.iterate()
Stream.generate()
차이점은 iterate
는 UnaryOperator
로 초기값과 조건을 이용하여 연산을 하면서 스트림을 만들어 나간다. 하지만 generate
는 Supplier
를 통해서 주어진 값을 활용해서 스트림을 만든다.
그렇다면 책의 예제처럼 피보나치 수열을 만드는 과정을 각각의 메서드를 활용해보자
Supplier<Integer> fib = new Supplier<>() {
private int previous = 0;
private int current = 1;
@Override
public Integer get() {
int oldPrevious = this.previous;
int nextValue = this.previous + this.current;
this.previous = this.current;
this.current = nextValue;
return oldPrevious;
}
};
@Test
void test() {
List<Integer> iterateList = Stream.iterate(new int[]{0, 1}, t -> t[0] <= 514229,
t -> new int[]{t[1], t[0] + t[1]})
.map(t -> t[0])
.sorted()
.collect(Collectors.toList());
List<Integer> generateList = Stream.generate(fib)
.takeWhile(t -> t <= 514229)
.sorted()
.collect(Collectors.toList());
assertThat(iterateList.equals(generateList)).isTrue();
}
// iterate()
// [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229]
// generate()
// [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229]
@Test
void parallelTest() {
List<Integer> iterateList = Stream.iterate(new int[]{0, 1}, t -> t[0] <= 514229,
t -> new int[]{t[1], t[0] + t[1]})
.parallel()
.map(t -> t[0])
.sorted()
.collect(Collectors.toList());
List<Integer> generateList = Stream.generate(fib)
.parallel()
.takeWhile(t -> t <= 514229)
.sorted()
.collect(Collectors.toList());
assertThat(iterateList.equals(generateList)).isTrue();
}
// 정상적인 피보나치 수열 출력
// [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229]
// iterate()
// [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229]
// generate() 실행할 때마다 다르게 나온다.
// [0, 1, 1, 1, 2, 3, 5, 8, 8, 13, 21, 21, 34, 55, 89, 144, 233, 233, 377, 610, 987, 1597, 2584, 2584, 4181, 6765, 10946, 17711, 28657, 28657, 46368, 75025, 75025, 121393, 196418, 196418, 317811, 514229, 514229]
// [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 46368, 75025, 121393, 196418, 317811, 514229]
iterate()
는 초기값과 연산을 지정할 수 있기에 람다로 내부에서 객체를 불변 상태로 유지했다. 따라서 병렬로 스트림을 처리해도 같은 값이 나왔다. 하지만 generate()
는 연산을 지정할 수 없기 때문에 익명 클래스로 외부에서 값을 변경하면서 수열을 만들기에 병렬로 스트림을 처리했을 때 부작용이 일어난 것이다.
물론 피보나치 수열을 만드는데 generate()
를 사용하는 것이 좋지 못한 사용법이다. 하지만 이로 간접적으로 병렬 처리의 부작용을 막기 위해 스트림에서는 익명 클래스보다는 상태를 바꾸지 않는 람다를 사용하는 것을 권장하는 것을 알 수 있다.
문제
무한 스트림을 생성하는 방법에는 iterate 와 generate가 있다. 이 둘의 차이점은 무엇이며, 책의 후반부에 나오는 피보나치 수열을 만들 때 가변 상태 객체와 불변 상태 객체를 활용하는 차이점에 대해서 알아보자
선정 배경
무한 스트림을 생성하는 두가지 방법의 차이점을 알아보고, 책의 앞장 부터 나왔던 병렬성과 불변 상태 객체에 대해서 좀 더 이해해 보기 위해서
관련 챕터
[5장] 스트림 활용 190~195p