Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
Tags
- entity
- font-size
- angular
- 당근마켓
- jwt
- TS
- 으
- 전역변수
- 데이터베이스 #try #이중
- 타입스크립트
- 클론코딩
- literal
- Strict
- &연산
- es6
- 10px
- github
- 1px border
- ZOOM
- Websocket
- ES5
- 서버리스 #
- 0.75px border
- 0.5px border
- Props
- 컴포넌튼
- npm
- 0.25px border
- 문서번호
- TypeScript
Archives
- Today
- Total
복잡한뇌구조마냥
[JAVA] Effective Java - 스트림은 주의해서 사용하라 (45) 본문
1. 스트림과 스트림 파이프라인
- 스트림(Stream): 데이터 원소의 유한 혹은 무한 시퀀스.
- 스트림 파이프라인(Stream Pipeline): 스트림 원소들로 수행하는 연산 단계의 묶음.
- 소스(Source): 컬렉션, 배열, I/O 채널 등.
- 중간 연산(Intermediate Operation): 변환, 필터링 등. 새로운 스트림을 반환.
- 종단 연산(Terminal Operation): 합계, 수집, 탐색 등. 최종 결과 반환.
- 지연 평가(Lazy Evaluation): 종단 연산이 호출될 때 연산이 실제 수행됨.
- 종단 연산에 쓰이지 않는 원소는 아예 계산되지 않음.
- 덕분에 무한 스트림도 다룰 수 있다.
✅ 스트림 API는 메서드 연쇄를 지원하는 플루언트 API라서 선언형 프로그래밍 스타일을 제공한다.
2. 스트림의 장점
- 간결성: 반복문을 짧고 명확하게 대체 가능.
- 의도 표현력: “무엇을 하고 싶은지”를 코드에 드러낸다.
- 조합 가능성: filter → map → reduce 체이닝.
- 병렬 처리 용이: .parallelStream()을 통한 병렬 연산 지원.
List<String> result = words.stream()
.filter(w -> w.length() > 3)
.map(String::toUpperCase)
.sorted()
.toList();
3. 스트림 사용 시 주의할 점
- 가독성 저하 위험
복잡한 파이프라인은 오히려 for문보다 읽기 힘들다. - 디버깅 어려움
중간 연산 단계에서 값을 추적하기 힘들다. - 표현력 한계
- break/continue 같은 제어 흐름은 스트림에서 난해하다.
- 지역 변수 갱신 불가(람다는 final 또는 사실상 final만 접근 가능).
- 혼란의 여지
- char를 다루면 IntStream으로 반환되어 혼란스러울 수 있음 → 명시적 형변환 필요.
- 람다 매개변수 이름을 의미 있게 정하지 않으면 가독성이 나빠짐.
4. 스트림이 빛나는 경우 ✨
- 원소들의 시퀀스를 일관되게 변환할 때
- 원소들을 조건에 따라 필터링할 때
- 원소들을 하나의 연산으로 집계할 때 (reduce)
- 원소들을 컬렉션에 모을 때 (collect)
- 원소 중 조건을 만족하는 값 탐색할 때 (findAny, findFirst)
5. 스트림이 힘든 경우 ⚠️
- 한 데이터가 여러 단계를 거치며, 각 단계의 값에 동시에 접근해야 하는 경우
- 복잡한 제어 흐름 (break, continue, 다중 분기) 이 필요한 경우
- 로직을 추적해야 하는 절차적 문제 해결 코드
이런 상황은 오히려 전통적인 for문이 더 명확하다.
// 반복 방식
private static List<Card> new Deck() {
List<Card> result = new ArrayList<>();
for (Suit suit : Suit.values())
for (Rank rank : Rank.values())
result.add(new Card(suit, rank));
return result;
}
// 스트림 방식
private static List<Card> new Deck() {
return Stream.of(Suit.values())
.flatMap(suit ->
Stream.of(Rank.values())
.map(rank -> new Card(suit, rank)))
.collect(toList());
}
6. 절충안: 스트림과 반복문은 보완재
- 스트림과 반복문은 대체재가 아니라 보완재다.
- 확고부동한 규칙은 없고, 상황에 따라 적절히 선택해야 한다.
- 같은 로직을 스트림과 반복문 양쪽으로 작성해보고 더 읽기 좋은 쪽을 택하라.
- 기존 코드를 리팩터링할 때는 새 코드가 더 짧고 명확해질 때만 스트림으로 바꾸는 것이 좋다.
- 팀 컨벤션이나 동료 개발자의 선호도도 고려할 것.
7. 결론
스트림은 데이터를 다루는 강력한 도구이지만,
남용하면 오히려 코드가 읽기 어렵고 유지보수가 힘들어진다.
데이터 변환·필터링·집계에는 스트림을,
복잡한 제어 흐름이나 상태 갱신에는 반복문을 쓰는 게 낫다.
결국 중요한 것은 코드의 가독성과 명확성이다.
LIST
'BE > JAVA' 카테고리의 다른 글
[JAVA] Effective Java - 다중정의는 신중히 사용하라 (52) (0) | 2025.08.24 |
---|---|
[JAVA] AOP (관점 지향 프로그래밍) 기초 정리 (0) | 2025.08.22 |
[JAVA] Thymeleaf 기초 정리 (0) | 2025.08.20 |
[JAVA] Spring Data JPA (0) | 2025.08.19 |
[JAVA] TDD에서 자주 쓰는 테스트 어노테이션 정리 (2) | 2025.08.18 |