글을 작성하는 이유
Java로 개발을 하다 보면 컬렉션이나 배열의 요소를 처리할 때 for문 대신 Stream을 사용하는 코드를 자주 마주하게 됩니다. 더 빠르다는 말에 이끌려 저 또한 Stream을 사용했습니다. 하지만 최근 들어 단순히 빠르다는 이유만으로 기술을 선택하는 것은 안일하다는 생각이 들었습니다. '왜 Stream을 사용해야 할까?'라는 근본적인 질문에 대한 답을 찾고 싶어졌습니다. 그리고 좋은건 더 공부해서 적극적으로 사용해야 한다고 생각했습니다.
for문과 Stream, 어떤 차이가 있을까?
특징 | for문 | Stream |
---|---|---|
개념 | 명시적인 반복 | 함수형 스타일의 데이터 처리 |
구조 | 순차적인 실행 | 파이프라인 구성 |
장점 | 직관적, 간단한 로직 | 가독성, 유지보수성, 병렬 처리 가능 |
단점 | 복잡한 로직에서 가독성 저하, 병렬 처리 어려움 | 학습 곡선, 성능 오버헤드 (일부 경우) |
Stream 쓰는 이유
1. 필요한 요소만 처리
- 지연 처리는 중간 연산을 누적하지 않고 최종 연산이 필요로 하는 만큼만 수행합니다.
- 지연 처리는 최종 연산에서 필요하지 않은 중간 연산이 있다면 이를 수행하지 않습니다.
2. 연산 합성
- 지연 처리를 통해 중간 연산들이 합쳐져서 한 번에 처리됩니다.
- 예를 들어, filter와 map이 연속으로 적용될 때, 각 요소에 대해 한 번의 순회로 filter와 map을 동시에 수행합니다.
여러 번 순회할 필요가 없어집니다!
stream의 프로세스
1. 생성
- Stream은 컬렉션(List, Set 등), 배열, 또는 I/O 채널 등에서 데이터를 가져와 생성할 수 있습니다.
예를 들어, List<String> list = Arrays.asList("a", "b", "c"); 에서 list.stream()을 호출하면 Stream<String>이 생성됩니다.
2. 중간 연산
- 중간 연산은 스트림의 데이터를 가공하는 과정입니다. filter(), map(), sorted() 등이 있습니다. 각 연산은 스트림의 요소를 처리하지만 즉시 실행하지는 않습니다.
- 중간 연산은 지연 처리 방식으로 동작하므로 최종 연산이 호출될 때까지 수행되지 않습니다.
- 3. 최종 연산
- 최종 연산은 스트림을 닫고 결과를 생성합니다. forEach(), collect(), reduce() 등이 있습니다.
- 최종 연산이 호출되면 중간 연산들이 실제로 실행됩니다.
메서드 정리
filter
filter는 스트림의 각 요소에 대해 주어진 조건을 검사하여 조건을 만족하는 요소만으로 구성된 새로운 스트림을 생성합니다.
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0) // 짝수만 필터링
.collect(Collectors.toList());
System.out.println(evenNumbers); // [2, 4]
map
map은 스트림의 각 요소를 주어진 함수를 사용하여 변환하여, 변환된 요소들로 구성된 새로운 스트림을 생성합니다.
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
List<Integer> nameLengths = names.stream()
.map(String::length) // 각 이름의 길이로 변환
.collect(Collectors.toList());
System.out.println(nameLengths); // [5, 3, 7]
forEach
forEach는 스트림의 각 요소에 대해 주어진 동작을 수행합니다. 최종 연산으로 스트림의 각 요소를 처리하고 결과를 출력하거나 저장할 때 사용됩니다.
List<String> items = Arrays.asList("apple", "banana", "cherry");
items.stream()
.forEach(System.out::println); // 각 요소 출력
collect
collect는 스트림의 요소들을 수집하여 특정 결과를 생성하는 최종 연산입니다. 주로 Collectors를 사용하여 리스트, 집합, 맵 등으로 결과를 수집할 수 있습니다.
List<String> fruits = Arrays.asList("apple", "banana", "cherry");
List<String> upperFruits = fruits.stream()
.map(String::toUpperCase)
.collect(Collectors.toList()); // 대문자로 변환하여 리스트로 수집
System.out.println(upperFruits); // [APPLE, BANANA, CHERRY]
참고: https://futurecreator.github.io/2018/08/26/java-8-streams-advanced/
'Dev' 카테고리의 다른 글
Index를 생성해서 약 10배 성능 개선 (0) | 2024.12.04 |
---|---|
N+1 문제 해결해서 성능 개선하기 (0) | 2024.12.04 |
일단 단위 테스트부터 테스트 코드 작성해보기 (0) | 2024.12.04 |
상품 주문하기 동시성 문제 해결하기 (0) | 2024.12.04 |
과도한 트래픽에 대한 방어하기 (2) | 2024.12.04 |