최근 한 프로젝트에서 로직이 복잡한 배치 프로그램에서 에러가 났다.
디버깅하려고 보니 Stream을 이용한 코딩이라 디버깅이 되지 않았다.
일반 for문으로 코드를 수정한 뒤에 디버깅을 해 오류를 해결했다.
Stream과 일반 for문의 차이를 메모하고자 작성하는 포스팅.
| for문 사용 | Stream 사용 | |
| 장점 | 직관적이고 디버깅 쉬움break, continue 사용 가능 | 선언적이고 깔끔한 코드가독성 좋고 체이닝 쉬움 |
| 단점 | 가독성 떨어질 수 있음 (특히 중첩되면) | 디버깅이 어렵고, 예외 처리 불편할 수 있음너무 복잡하게 쓰면 가독성 저하 |
| 디버깅 | 쉬움 | 어려움 |
| 추천 사용법 | 복잡한 조건 or 상태 변경 | 단순 필터/맵핑 |
예제로 비교한다.
공통 샘플 데이터
List<Map<String, Object>> people = new ArrayList<>();
people.add(Map.of("name", "철수", "age", 28, "team", "A", "score", 85));
people.add(Map.of("name", "영희", "age", 32, "team", "B", "score", 90));
people.add(Map.of("name", "민수", "age", 40, "team", "A", "score", 70));
people.add(Map.of("name", "지영", "age", 29, "team", "B", "score", 95));
people.add(Map.of("name", "수지", "age", 35, "team", "C", "score", 88));
- if문 조건에 따른 로직
//--# for문
for (Map<String, Object> person : people) {
Integer age = (Integer) person.get("age");
if (age >= 30) {
System.out.println(person.get("name"));
}
}
//--# Stream
people.stream()
.filter(p -> (Integer) p.get("age") >= 30)
.map(p -> (String) p.get("name"))
.forEach(System.out::println);
2. 값 변경 없는 정렬 (score 기준 내림차순)
//--# for문
people.sort((p1, p2) -> ((Integer) p2.get("score")) - ((Integer) p1.get("score")));
//--# Stream
List<Map<String, Object>> sorted =
people.stream()
.sorted((p1, p2) -> ((Integer) p2.get("score")) - ((Integer) p1.get("score")))
.collect(Collectors.toList());
3. 값 변경 있는 정렬 (grade 추가 후 score 기준 내림차순)
//--# for문 : 원본 리스트 변경함
for (Map<String, Object> person : people) {
int score = (Integer) person.get("score");
String grade;
if (score >= 90) {
grade = "A";
} else if (score >= 80) {
grade = "B";
} else {
grade = "C";
}
person.put("grade", grade);
}
// 정렬 (score 기준 내림차순)
people.sort((p1, p2) -> ((Integer) p2.get("score")) - ((Integer) p1.get("score")));
//--# Stream : 새 리스트 생성 (collect)
List<Map<String, Object>> updatedAndSorted = people.stream()
.peek(person -> {
int score = (Integer) person.get("score");
String grade;
if (score >= 90) grade = "A";
else if (score >= 80) grade = "B";
else grade = "C";
person.put("grade", grade); // 값 변경
})
.sorted((p1, p2) -> ((Integer) p2.get("score")) - ((Integer) p1.get("score")))
.collect(Collectors.toList());
✅ stream의 peek()의 원래 목적은 값을 바꾸거나 외부에 영향을 주는 용도가 아니다.
peek()은 중간 연산자로, Stream 안에서 “디버깅용 또는 가볍게 훑어보는 용도”로 만들어졌다.
그렇지만 실무에서는 편해서 자주 쓴다고 한다.
stream
.peek(e -> e.put("grade", "A")) // Map에 새로운 값 추가
.collect(Collectors.toList());
4. 그룹핑 (team별 그룹)
//--# for문
Map<String, List<Map<String, Object>>> grouped = new HashMap<>();
for (Map<String, Object> person : people) {
String team = (String) person.get("team");
grouped.computeIfAbsent(team, k -> new ArrayList<>()).add(person);
}
//--# Stream
Map<String, List<Map<String, Object>>> grouped =
people.stream()
.collect(Collectors.groupingBy(p -> (String) p.get("team")));
5. 집계 (전체 score 합계 & 평균)
//--# for문
int sum = 0;
for (Map<String, Object> person : people) {
sum += (Integer) person.get("score");
}
double avg = (double) sum / people.size();
//--# Stream
int sum = people.stream()
.mapToInt(p -> (Integer) p.get("score"))
.sum();
double avg = people.stream()
.mapToInt(p -> (Integer) p.get("score"))
.average()
.orElse(0.0);