스트림은 그저 또 하나의 API가 아닌, 함수형 프로그래밍에 기초한 패러다임이기 대무이다. 스트림이 제공하는 표현력, 속도를 얻으려면 API와 이 패러다임까지 함께 받아 드려야 한다.
스트림 패러다임의 핵심은 계산을 일련의 변환으로 재구성하는 부분이다. 이때 각 변환 단계는 가능한 한 이전 단계의 결과를 받아 처리하는 순수 함수여야 한다.
순수함수란 오직 입력만이 결과에 영향을 주는 함수를 말한다. 다른 가변 상태를 참조하지 않고, 함수 스스로도 다른 상태를 변경하지 않는다. 이렇게 하려면 스트림 연산에 건네는 함수 객체는 모두 부작용이 없어야 한다.
순수하지 않은 함수
public class Main {
private static int factor = 2; // 외부 상태
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 순수 함수가 아닌 예: 외부 상태에 의존
List<Integer> multipliedNumbers = numbers.stream()
.map(n -> n * factor) // factor에 의존
.collect(Collectors.toList());
System.out.println(multipliedNumbers); // [2, 4, 6, 8, 10]
factor = 3; // 외부 상태 변경
multipliedNumbers = numbers.stream()
.map(n -> n * factor) // 변경된 factor에 의존
.collect(Collectors.toList());
System.out.println(multipliedNumbers); // [3, 6, 9, 12, 15]
}
}
위 함수는 순수하지 않다. 외부 상태에 의존하기 때문이다. 외부 상태가 변경되면 결과가 바뀐다는 불확실성을 만든다. 순수하지 않은 함수의 단점은 아래와 같다.
순수함수
public class PureFunctionExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 순수 함수 사용
List<Integer> squaredNumbers = numbers.stream()
.map(n -> n * n) // map() 메서드에 전달된 람다식은 순수 함수
.collect(Collectors.toList());
System.out.println(squaredNumbers); // [1, 4, 9, 16, 25]
}
}
위 함수는 순수하다. 결과에 영향을 미치는 값은 입력 값이 유일하다. 순수함수의 장점은 아래와 같다.