Java Stream forEach에서 중단하는 방법

1. 개요

Java 개발자로서 우리는 종종 일련의 요소를 반복하고 각 요소에 대해 작업을 수행하는 코드를 작성합니다. Java 8 스트림 라이브러리와 forEach 메서드를 사용하면 해당 코드를 깔끔하고 선언적인 방식으로 작성할 수 있습니다.

이것은 루프와 유사하지만 반복을 중단 하는 break 문에 해당하는 부분이 없습니다 . 스트림은 매우 길거나 잠재적으로 무한 할 수 있으며 처리를 계속할 이유가 없다면 마지막 요소를 기다리기보다는 중단하고 싶을 것입니다.

이 자습서에서는 Stream.forEach 작업 에서 break을 시뮬레이션 할 수 있는 몇 가지 메커니즘을 살펴 보겠습니다 .

2. 자바 9의 Stream.takeWhile ()

String 항목 의 스트림이 있고 길이가 홀수 인 한 요소를 처리하려고 한다고 가정 해 보겠습니다 .

Java 9 Stream.takeWhile 메소드를 사용해 보겠습니다 .

Stream.of("cat", "dog", "elephant", "fox", "rabbit", "duck") .takeWhile(n -> n.length() % 2 != 0) .forEach(System.out::println);

이것을 실행하면 출력을 얻습니다.

cat dog

for 루프와 break 사용하여 일반 Java의 동등한 코드와 비교하여 작동 방식을 살펴 보겠습니다.

List list = asList("cat", "dog", "elephant", "fox", "rabbit", "duck"); for (int i = 0; i < list.size(); i++) { String item = list.get(i); if (item.length() % 2 == 0) { break; } System.out.println(item); } 

보시다시피 takeWhile 메소드를 사용하면 필요한 것을 정확하게 얻을 수 있습니다.

하지만 아직 Java 9를 채택하지 않았다면 어떻게 될까요? Java 8을 사용하여 어떻게 비슷한 일을 할 수 있습니까?

3. 사용자 지정 분할기

Stream.spliterator 의 데코레이터로 작동 할 사용자 지정 Spliterator 를 만들어 보겠습니다 . Spliterator휴식 을 수행하도록 할 수 있습니다 .

먼저 스트림 에서 Spliterator를 가져온 다음 CustomSpliterator로 장식 하고 중단 작업 을 제어 하기위한 Predicate 를 제공합니다 . 마지막으로 CustomSpliterator 에서 새 스트림을 생성합니다 .

public static  Stream takeWhile(Stream stream, Predicate predicate) { CustomSpliterator customSpliterator = new CustomSpliterator(stream.spliterator(), predicate); return StreamSupport.stream(customSpliterator, false); }

CustomSpliterator 를 만드는 방법을 살펴 보겠습니다 .

public class CustomSpliterator extends Spliterators.AbstractSpliterator { private Spliterator splitr; private Predicate predicate; private boolean isMatched = true; public CustomSpliterator(Spliterator splitr, Predicate predicate) { super(splitr.estimateSize(), 0); this.splitr = splitr; this.predicate = predicate; } @Override public synchronized boolean tryAdvance(Consumer consumer) { boolean hadNext = splitr.tryAdvance(elem -> { if (predicate.test(elem) && isMatched) { consumer.accept(elem); } else { isMatched = false; } }); return hadNext && isMatched; } }

이제 tryAdvance 메소드를 살펴 보겠습니다 . 여기에서 커스텀 Spliterator 가 데코 레이팅 된 Spliterator 의 요소를 처리하는 것을 볼 수 있습니다 . 조건자가 일치하고 초기 스트림에 여전히 요소가있는 한 처리가 수행됩니다. 조건 중 하나가 될 때 거짓 , 우리 Spliterator "휴식" 과 스트리밍 작업 끝.

새로운 도우미 메서드를 테스트 해 보겠습니다.

@Test public void whenCustomTakeWhileIsCalled_ThenCorrectItemsAreReturned() { Stream initialStream = Stream.of("cat", "dog", "elephant", "fox", "rabbit", "duck"); List result = CustomTakeWhile.takeWhile(initialStream, x -> x.length() % 2 != 0) .collect(Collectors.toList()); assertEquals(asList("cat", "dog"), result); }

보시다시피 조건이 충족 된 후 스트림이 중지되었습니다. 테스트 목적으로 결과를 목록으로 수집했지만 forEach 호출 또는 Stream 의 다른 함수를 사용할 수도 있습니다 .

4. 맞춤형 forEach

중단 메커니즘이 포함 된 Stream 을 제공하는 것이 유용 할 수 있지만 forEach 작업 에만 집중하는 것이 더 간단 할 수 있습니다 .

데코레이터없이 직접 Stream.spliterator를 사용합시다 .

public class CustomForEach { public static class Breaker { private boolean shouldBreak = false; public void stop() { shouldBreak = true; } boolean get() { return shouldBreak; } } public static  void forEach(Stream stream, BiConsumer consumer) { Spliterator spliterator = stream.spliterator(); boolean hadNext = true; Breaker breaker = new Breaker(); while (hadNext && !breaker.get()) { hadNext = spliterator.tryAdvance(elem -> { consumer.accept(elem, breaker); }); } } }

보시다시피 새로운 사용자 정의 forEach 메서드는 BiConsumer를 호출 하여 스트림을 중지하는 데 사용할 수있는 다음 요소와 차단기 개체를 모두 코드에 제공합니다.

단위 테스트에서 이것을 시도해 봅시다.

@Test public void whenCustomForEachIsCalled_ThenCorrectItemsAreReturned() { Stream initialStream = Stream.of("cat", "dog", "elephant", "fox", "rabbit", "duck"); List result = new ArrayList(); CustomForEach.forEach(initialStream, (elem, breaker) -> { if (elem.length() % 2 == 0) { breaker.stop(); } else { result.add(elem); } }); assertEquals(asList("cat", "dog"), result); }

5. 결론

이 기사에서는 스트림에서 중단 을 호출하는 것과 동일한 기능을 제공하는 방법을 살펴 보았습니다 . Java 9의 takeWhile이 대부분의 문제를 해결하는 방법과 Java 8 용 버전을 제공하는 방법을 살펴 보았습니다 .

마지막으로 Stream에서 반복하는 동안 중단 작업 과 동일한 기능을 제공 할 수있는 유틸리티 메서드를 살펴 보았습니다 .

항상 그렇듯이 예제 코드는 GitHub에서 찾을 수 있습니다.