자바 - Stream 최종 연산

namkun·2022년 10월 3일
0

JAVA

목록 보기
12/20

Stream의 최종 연산

Stream API 에서 중개 연산을 통해서 변환된 스트림은 마지막 최종 연산을 통해 각 요소를 소모하여 결과값을 리턴한다.

즉, 지연(lazy) 되었던 모든 중개 연산이 최종 연산 시에 다 수행되는 것이다.

이렇게 최종 연산 시에 모든 요소를 소모한 Stream은 다시 재 사용이 불가해진다.

출력

forEach()

앞선 글들에서 많이 사용했던 forEach 메서드는 스트림의 요소들을 소모해서 명시된 동작을 수행한다.

반환 타입이 void 이기에, 보통 스트림의 모든 요소를 출력하는 용도로 많이 사용된다.

Stream.of(1, 2, 3, 4)
	.forEach(System.out::println)
    
// result
1
2
3
4

소모

reduce()

앞서 스트림의 최종 연산은 각 요소를 모두 소모해서 연산을 수행한다고 하였다.

그러나 reduce 메서드는 첫 번째와 두 번째 요소를 갖고 연산을 수행한 뒤, 그 결과를 다시 세 번째 요소와 연산을 수행하게 합니다.

이런 식으로 스트림의 모든 요소를 다 소모해서 연산을 수행하고, 그 결과를 반환하게 한다.

추가로, 매개변수로 초기값을 전달하면 초기값과 해당 스트림의 첫 번째 요소로 연산을 수행한다.

Optional<String> reduceResult = Stream.of("1", "2", "3", "4")
                                      .reduce((s1, s2) -> s1 + s2);

reduceResult.ifPresent(System.out::println);

// result
1234

String reduceResult2 = Stream.of("1", "2", "3", "4")
                .reduce("start", (s1, s2) -> s1 + s2);

System.out.println(reduceResult2);

// result
start1234

위의 예제에서 이상한 점이 하나 있는데, 바로 초기값을 매개변수로 가져가면 Optional<T>가 아니라, T 타입이 리턴되는 것이다.

그 이유는 비어있는 스트림과 reduce 연산을 하는 경우, 전달 받은 초기값을 그대로 반환해야 하기 때문이다.

검색

findFirst() & findAny()

findFirst, findAny 메서드 둘 다 스트림에서 첫 번째 요소를 참조하는 Optional 객체를 반환한다.

참고로 빈 스트림의 경우 두 메서드 모두 빈 Optional 객체를 반환한다.

IntStream.of(1, 4, 2, 5, 4, 6, 8, 7, 9)
                .sorted()
                .findFirst()
                .ifPresent(System.out::println);
                
// result
1

IntStream.of(1, 4, 2, 5, 4, 6, 8, 7, 9)
                .sorted()
                .findAny()
                .ifPresent(System.out::println);

// result
1

위의 예제에서 볼 수 있듯이 결과는 모두 같게 출력된다.

그럼 왜 메서드를 두 개로 나눠놓은 것일까?

병렬 스트림을 사용할 때는 findAny 메서드를 사용해야만 정확한 연산 결과를 반환한다.

검사

anyMatch()

해당 스트림의 일부 요소가 특정 조건을 만족할 경우에 true를 반환한다.

boolean anyMatch = IntStream.of(1, 2, 3, 4).anyMatch(n -> n < 2);
System.out.println(anyMatch);

// result
true

allMatch()

해당 스트림의 모든 요소가 특정 조건을 만족할 경우 true를 반환한다.

boolean allMatch = IntStream.of(1, 2, 3, 4).allMatch(n -> n < 2);
System.out.println(allMatch);

// result
false

noneMatch()

해당 스트림의 모든 요소가 특정 조건을 만족하지 않을 경우에 true값을 반환한다.

boolean noneMatch = IntStream.of(1, 2, 3, 4).noneMatch(n -> n < 2);
System.out.println(noneMatch);

// result
false

통계

count()

해당 스트림 요소의 총 개수를 long 타입으로 반환한다.

System.out.println(IntStream.of(1,2,3).count());

// result
3

max() & min()

해당 스트림 요소중에 가장 큰 값 or 작은 값을 참조하는 Optional 객체를 반환한다.

System.out.println(IntStream.of(1, 2, 3).max().getAsInt());
System.out.println(IntStream.of(1, 2, 3).min().getAsInt());

// result
3
1

연산

IntStream이나 DoubleStream과 같은 기본 타입 스트림에는 해당 스트림의 모든 요소의 합이나 평균을 구할 수 있는 메서드가 정의되어 있다.

sum()

해당 스트림의 모든 요소의 합을 구한다.

System.out.println(IntStream.of(1, 2, 3, 4).sum());

// result
10

average()

해당 스트림의 모든 요소의 평균을 구한다.

이때 average 메서드의 반환 값은 각 기본 타입으로 래핑된 Optional 객체를 반환한다.

System.out.println(IntStream.of(1, 2, 3, 4, 5).average().getAsInt());

// result
3

수집

collect()

collect 메서드는 매개변수로 전달되는 Collectors 객체에 구현된 방법대로 스트림의 요소를 수집한다.

Collectors 클래스에는 미리 정의된 다양한 방법이 클래스 메서드로 정의되어 있으나, 사용자가 직접 Collector 인터페이스를 구현해서 직접 정의할 수 도 있다.

스트림 요소의 수집 용도 별 사용할 수 있는 Collectors 메서드는 다음과 같다.

  • Stream -> array/collection : toArray(), toCollection(), toList(), toSet(), toMap()
  • 통계 및 연산 메서드 동작을 수행 : counting(), maxBy(), minBy(), summingInt(), averagingInt()...
  • 요소의 소모 수행 : reducing(), joining()
  • 요소의 그룹화 및 분할 : groupingBy(), partitioningBy()
// stream to list
List<String> list = Stream.of("a", "b", "c")
						.collect(Collectors.toList());
                        
// array to list
String[] arr = new String[]{"a", "b", "c"};
List<String> list = Arrays.stream(arr).collect(Collectors.toList());

// partitioningBy()
Map<Boolean, List<String>) partition = Stream.of("a", "bc", "d", "ef")
										.collect(Collectors.partitioningBy(s -> (s.length() % 2) == 0));

// result
System.out.println(partition.get(false));
["a", "d"]

System.out.println(partition.get(true));
["bc", "ef"]
profile
개발하는 중국학과 사람

0개의 댓글