자바 8의 출시와 함께 다양한 새로운 기능들이 추가되었는데, 그 중 하나가 바로 forEach 메서드입니다. 이번 글에서는 forEach가 무엇인지, 어떻게 사용하는지, 그리고 주의사항 및 한계에 대해 알아보겠습니다.
forEach란?
`forEach`는 자바 8에서 추가된 `Iterable` 인터페이스의 디폴트 메서드로, 컬렉션의 모든 요소를 순회하며 지정된 동작을 수행합니다. 기존의 `for` 또는 `iterator`를 사용하는 방법보다 코드가 간결해집니다.
기본 사용법
List<String> list = Arrays.asList("apple", "banana", "cherry");
list.forEach(fruit -> System.out.println(fruit));
람다 표현식과의 연계
forEach는 함수형 인터페이스인 Consumer를 인수로 받기 때문에 람다 표현식과 함께 사용하면 매우 편리합니다.
list.forEach(item -> {
if(item.startsWith("a")) {
System.out.println(item);
}
});
실용적인 예제
맵(Map)에서의 사용
Map<String, Integer> map = new HashMap<>();
map.put("apple", 1);
map.put("banana", 2);
map.forEach((key, value) -> System.out.println(key + ": " + value));
주의사항 및 한계
순차 처리
forEach는 기본적으로 순차적으로 처리되며, 병렬 처리를 원한다면 `parallelStream()`과 함께 사용해야 합니다.
예외 처리
람다 내부에서 체크 예외를 처리하려면 예외 처리가 필요합니다.
종단 연산
forEach는 스트림에서 사용될 때 종단 연산(terminal operation)으로 작동합니다. 이 말은 스트림의 요소들을 소비하면서 어떤 동작을 수행하지만, 그 결과를 새로운 스트림으로 반환하지 않는다는 의미입니다.
업데이트 작업에서의 주의사항
forEach를 사용하여 컬렉션의 요소를 업데이트하는 것은 권장되지 않습니다. 그 이유는 다음과 같습니다
- 불변성 유지의 어려움: 스트림은 함수형 프로그래밍 스타일로 설계됐습니다. 함수형 프로그래밍의 철학은 데이터의 불변성을 유지하는 것입니다. forEach 내에서 요소를 변경하면 이러한 불변성이 깨질 수 있습니다.
- 병렬 스트림에서의 문제: forEach를 병렬 스트림과 함께 사용할 때 상태를 변경하는 작업을 하면 예측하지 못한 결과가 발생할 수 있습니다.
- 의도 전달의 부적절성: forEach는 주로 요소를 소비하는 데 사용되며, 데이터를 변경하거나 업데이트하는 작업에는 적합하지 않습니다. 요소를 변경하는 것은 map 등의 중간 연산을 사용하는 것이 더 적절합니다.
적절한 대안
업데이트 작업이 필요하다면 `for`문,`for-each`문을 사용하거나 , `map`을 사용하는 것이 더 적절합니다.
List<Integer> numbers = Arrays.asList(1, 2, 3);
List<Integer> updatedNumbers = numbers.stream()
.map(n -> n * 2)
.toList();
map을 사용하면 각 요소를 변환한 새로운 스트림을 얻을 수 있습니다.
📚 결론
forEach는 요소를 소비하는 데 최적화되어 있으며, 상태를 변경하는 작업에는 적합하지 않습니다. 업데이트나 변환이 필요하다면 `map` 등의 중간 연산이나 기존의 반복문을 사용하는 것이 좋습니다. 이를 통해 코드의 가독성과 안전성을 높일 수 있습니다.
reference
- https://braindisk.tistory.com/156
'JAVA > 개념' 카테고리의 다른 글
언제 equals를 재정의 해야할까? (0) | 2024.11.22 |
---|---|
Collection.forEach vs Stream.forEach (1) | 2024.11.20 |
스트림의 중간 연산, 종단 연산 (0) | 2024.11.18 |
자바 정적 팩토리 메서드 쉽게 이해하기 - 왜 사용하고 어떻게 활용할까? (0) | 2024.10.24 |