[Java] Stream과 iterable 의 forEach

2022. 2. 6. 01:40☕️ Java

우선 자바의 forEach() 메서드는 2가지로 Stream 과 iterable 인터페이스에 각각 정의돼 있습니다.

stream 의 forEach() 메서드는 아래 코드처럼 사용할 수 있습니다. for 문을 사용하는 것에 비해 간결하고 가독성있는 코드를 작성할 수 있습니다. 

 

Stream 의 forEach()

public void streamExample() {
    String[] numberOfStatus = new String[]{"0", "1", "2", "3"};
    Arrays.stream(numberOfStatus).forEach(item -> System.out.println(item));
}

다만 위 처럼 단순 반복을 위해 Stream 의 forEach() 를 사용하면 Stream 생성 비용이 낭비됩니다. Stream 을 생성할 땐 가독성과 생성 비용 사이의 간극을 생각해야 합니다.

 

iterable 의 forEach()

위처럼 단순 반복문일 경우엔, Stream.forEach() 대신 iterable 인터페이스에 디폴트 메서드로 정의된 forEach() 메서드를 사용할 수 있습니다.

public void streamExample() {
    String[] numberOfStatus = new String[]{"0", "1", "2", "3"};
    for (String status : numberOfStatus) {
        System.out.println(status);
    }
}

 

 

Stream 의 forEach() 에 대해 좀 더 알아보겠습니다. 아래 내용은 해당 포스트를 일정 부분 참고하였습니다.

 

Stream 의 forEach 의 용도는 최종 연산.

public void IntStreamExample() {
    IntStream.range(1, 10).forEach(number -> {
        if (number > 5) {
          return;
        }
        System.out.println(number);
    });
}

위 코드는 1에서 10 까지 정수를 순회하며, 이때 5 보다 작은 숫자만 출력하는 조건을 가집니다. number 가 5 보다 큰 순간부터 더 이상 forEach() 가 호출될 필요가 없지만, Stream 은 강제적으로 종료되지 않습니다.

 

forEach() 내부보다 filter() 내부에 필터 로직을 삽입하는 것이 바람직합니다. Stream 의 지연 연산 특성 때문에 10 번 모두 순회하지만, forEach() 는 최종 연산으로만 사용하는 것이 옳습니다.

public void IntStreamExample() {
    IntStream.range(1, 10)
        .filter(number -> number <= 5)
        .forEach(System.out::println);
}

이펙티브 자바 아이템 46에 따르면, Stream 의 forEach() 는 본래 스트림의 종료를 위한 연산으로, 스트림 계산 결과를 보고하는 (출력) 용도가 적합하다고 합니다. 위 처럼 로직을 처리하는 것은 본 의도에 맞지 않다고 볼 수 있습니다. 

 

결론

iterable 의 forEach 나 향상된 for 문을 우선 사용하되, Stream 의 forEach 를 사용하고자 한다면 그 의도를 명확히 파악하고 적절한 곳에 사용하는 것이 옳다고 생각합니다.