Search

Item 45. 스트림은 주의해서 사용하라

생성일
2023/07/31 23:47
챕터
7장 - 람다와 스트림

스트림의 한계

스트림 API는 여러 기능들을 제공하여 사용자가 사용하길 원하는 많은 계산들을 하게 만들 수 있다. 하지만 스트림을 잘못 하용하면 가독성이 저하되고 유지보수가 힘들어지기 때문에 주의해서 사용하자.
Java
복사
이와 같은 코드를 스트림을 적용하면
try (Stream<String> words = Files.lines(dictionary)) { words.collect( groupingBy(word -> word.chars().sorted() // 애너그램을 수집 .collect(StringBuilder::new, (sb, c) -> sb.append((char) c), StringBuilder::append).toString())) .values().stream() // 맵의 값들을 스트림으로 변환 .filter(group -> group.size() >= minGroupSize) // 충분히 큰 그룹만 필터링 .map(group -> group.size() + ": " + group) // 결과를 문자열로 변환 .forEach(System.out::println); // 결과 출력 }
Java
복사
이처럼 바꿀 수 있다. 다만 보면 알 수 있듯 스트림을 과하게 적용하여 가독성이 떨어진다.
try (Stream<String> words = Files.lines(dictionary)) { words.collect(groupingBy(Anagrams::alphabetize)) // 애너그램을 수집 .values().stream() // 맵의 값들을 스트림으로 변환 .filter(group -> group.size() >= minGroupSize) // 충분히 큰 그룹만 필터링 .forEach(group -> System.out.println(group.size() + ": " + group)); // 결과 출력 } /* alphabetize */ private static String alphabetize(String s) { char[] a = s.toCharArray(); java.util.Arrays.sort(a); return new String(a); }
Java
복사
이처럼 적정 수준의 스트림을 사용하면 깔끔하게 표현될 수 있다.
기존 코드를 스트림으로 리팩터링을 할 때는 리팩터링 후 코드가 더 깔끔해질 때만 적용하자.

스트림의 람다식 vs 반복문의 코드블록

스트림 람다식의 함수 객체에서는 할 수 없지만, 반복문 내의 코드블록에서는 가능한 것들이 있다.
람다식에서는 final이나 사실상 final인 변수들만 읽을 수 있고 지역변수를 읽는 게 불가능하지만, 코드 블록에서는 스코프 내의 지역변수를 읽고 수정할 수 있다.
코드 블록에서는 return이나 continue, break를 통해 반복문을 종료하거나 건너뛰는게 가능하지만, 람다식에서는 불가능하다.
코드 블록에서는 메서드 선언에 명시된 검사 예외를 던질 수 있지만, 람다식은 할 수 없다.
위 사항들의 로직을 수행해야한다면, 해당 코드는 스트림으로 수정하는게 올바르지 못한 선택일 가능성이 높다.
또한 스트림에서는 파이프라인을 통과하면서 데이터를 따로 저장하는게 아니라 해당 단계가 넘어가면 이전의 결과는 잃어버리기 때문에, 여러 단계의 연산 결과 데이터에 접근해야하는 상황이 있다면 스트림을 적용해선 안된다.
반면 스트림에 적용하기 적합한 코드들의 특징들은 다음과 같다.
원소들의 시퀀스를 일관되게 변환한다.
원소들의 시퀀스를 필터링한다.
원소들의 시퀀스를 하나의 연산으로 결합한다.
원소들의 시퀀스를 공통된 속성을 기준으로 묶어 컬렉션으로 저장한다.
원소들의 시퀀스에서 특정 조건을 만족하는 원소를 찾는다.

결론

스트림과 반복문 중 더 나은 선택을 해서 적용하자.
어느 쪽이 더 나은지 모르겠다면, 둘 다 구현해보고 선택하자.