Java 스트림(Stream) 사용 시 주의사항
최근에 Java의 Stream API를 활용하면서 경험했던 오류들에 대한 내용을 정리한다.
1. 스트림의 재사용
말 그대로 한 번 사용했던 스트림을 다시 한번 사용할 경우 발생하는 오류이다. 흔하게 접할 수 있는 실수이므로 주의하자. Stream은 컬렉션이 아니기 때문에 재사용할 수 없다.
예제1)
// 문자열 스트림 생성
Stream<String> langNames = Stream.of("Java", "C++", "Python", "Ruby");
// 스트림 내 모든 요소 출력
langNames.forEach(System.out::println);
// "Java" 만 제외한 스트림을 다시 생성... Exception이 발생한다.
Stream<String> filtered = langNames.filter(lang -> !lang.equals("Java"));
filtered.forEach(System.out::println);
예제2)
IntStream stream = IntStream.of(1, 2);
stream.forEach(x -> System.out::println(x));
// 재사용시 Exception 발생
stream.forEach(x -> System.out::println(x));
java.lang.IllegalStateException: stream has already been operated upon or closed
스트림은 오직 한 번만 소비될 수 있다.
2. 지역변수 접근
스트림을 이용하면서 람다(lambda) 또는 메소드 참조(method references)를 사용하는 경우 지역 변수(local variables)에 접근할 수 없다.
int sumForLambda = 0;
for (int i = 0; i < 5; i++) {
// it works!
sumForLambda += i;
}
int sumForloop = 0;
IntStream.range(0, 5).forEach(i -> {
// compile error
sumForloop += i;
});
그리고 스트림의 파이프라인에서 연결된 각 단계의 값들에 접근할 수 없다. peek 메서드로 연산 사이의 결과를 확인하고 싶지만 불가능하다.
Arrays.stream(array)
.filter(first -> first % 2 == 0)
.filter(second -> second > 3)
.peek(value -> {
// compile error, can't access second filter's variable
int printValue = value + second;
System.out.println(printValue);
})
.sum();
참고로 peek 메서드의 경우 스트림의 결과를 구하는 단말 연산(Terminal Operations)이 실행되지 않으면 메서드 자체가 실행되지 않는다. 위 예제에서는 sum 메서드가 단말 연산으로 실행되었다.
3. 무한 스트림 생성 문제
// 1-1. 무한이 돌아감
IntStream.iterate(0, i -> i + 1)
.forEach(System.out::println);
// 1-2. 더 나은 코드 (0~9)
IntStream.iterate(0, i -> i + 1)
.limit(10)
.forEach(System.out::println);
/* --------------------------------------------------------------- */
// 2-1. 의도치 않게 생성된 무한 스트림
IntStream.iterate(0, i -> ( i + 1 ) % 2)
.distinct()
.limit(10)
.forEach(System.out::println);
System.out.println("complete");
// 2-2. 연산자 순서를 변경해 정상적으로 작동하게 함
IntStream.iterate(0, i -> ( i + 1 ) % 2)
.limit(10)
.distinct()
.forEach(System.out::println);
System.out.println("complete");
3번은 limit(10)을 사용했는데도 무한 스트림이 될 수 있다.
여기서 문제는 distinct 연산자는 iterate 함수가 오직 0과 1이라는 값만 생성할 것이라는 것을 알지 못해 결국 스트림으로부터 무한히 값을 받아들여 사용할 것이다. limit(10)에는 결코 다다르지 못하기 때문에 무한 스트림이 된다.
이 외에도 여러가지 주의점이 있지만 가장 많이 접할 수 있는 오류들로 정리하였다.
참고(출처)
https://madplay.github.io/post/mistakes-when-using-java-streams
'Programming > Java' 카테고리의 다른 글
[Java] String to Date - ParseException 해결 방법 (0) | 2023.06.14 |
---|---|
[Java] static 개념 및 사용법 (1) | 2023.05.08 |
[Java] 약수의 개수 구하는 최적의 방법(알고리즘) (0) | 2022.11.20 |
[Java] Map이란? (개념, 활용, 예제 등) (0) | 2022.10.02 |
[Java] List 중복제거 - Set, Stream 활용 (0) | 2022.09.10 |