Java 8 Stream API 살펴보기 -2- Stream 가공하기 / 결과 만들기
Updated:
- Java 8 Stream API 살펴보기 -1- Stream 생성하기
- Java 8 Stream API 살펴보기 -2- Stream 가공하기 / 결과 만들기
- Java 8 Stream API 살펴보기 -3- findAny() vs findFirst()
- Java 8 Stream API 살펴보기 -4- Collector 살펴보기
1. Intro
본 포스트에서는 Stream 에서 데이터를 가공하고, 결과를 반환하는 메소드에 대해 알아보겠습니다.
함수가 다양하고 많기 때문에 간단하게 설명하면서 진행하겠습니다.
- 가공하기
- Filtering
- Mapping
- Sorting
- Iterating
- 결과 만들기
- Calculating
- Reduction
- Collecting
- Matching
- Iterating
결과 만들기에서 findAny()
, findFirst()
함수는 다음 포스트에서 다루겠습니다.
2. 가공하기
2.1. Filtering
2.1.1. filter
필터(filter) 는 Stream 내 요소들을 하나씩 비교하여 걸러내는 메소드입니다. 인자로 받는 Predicate
는 boolean
을 리턴하는 함수형 인터페이스이며, 조건식을 표현하는 데 사용됩니다.
Stream<T> filter(Predicate<? super T> predicate);
Stream 에서 “A” 와 동일한 요소를 걸러내는 예제입니다.
List<String> list = Arrays.asList("A", "B", "C", "D");
Stream<String> stream = list.stream()
.filter(s -> s.equals("A")); // [A]
2.1.2. limit
최대 maxSize
까지 Stream 을 리턴합니다. 보통 generate()
와 같이 사용합니다.
Stream<T> limit(long maxSize);
Stream<String> stream = Stream.generate(() -> "element").limit(2);
// [element, element]
2.1.3. limit
Stream 의 상위 n
개 요소를 생략한 Stream 을 리턴합니다.
Stream<T> skip(long n);
List<Integer> list = Arrays.asList(5, 7, 3, 1, 2, 6, 1, 9, 0);
Stream<Integer> stream = list.stream()
.sorted(Comparator.reverseOrder())
.skip(4); // [3, 2, 1, 1, 0]
2.1.4. distinct
중복제거(distinct) 는 Stream 에서 중복된 요소를 제거한 Stream 을 리턴합니다.
Stream<T> distinct();
간단한 예제를 보겠습니다.
List<String> list = Arrays.asList("A", "A", "B", "B", "C", "C");
Stream<String> stream = list.stream()
.distinct(); // [A, B, C]
2.2. Mapping
2.2.1. map
맵(map) 은 Stream 내 요소들을 하나씩 특정 값으로 변환합니다. 이때 람다를 인자로 받습니다.
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
문자열을 소문자로 바꿔주는 예제입니다.
List<String> list = Arrays.asList("A", "B", "C", "D");
Stream<String> stream = list.stream()
.map(String::toLowerCase); // [a, b, c, d]
2.2.2. mapToInt, mapToLong, mapToDouble
mapToInt()
, mapToLong()
, mapToDouble()
함수들은 Stream 을 해당하는 타입으로 바꿔줍니다.
IntStream mapToInt(ToIntFunction<? super T> mapper);
LongStream mapToLong(ToLongFunction<? super T> mapper);
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);
mapToInt()
를 예시로 간단한 예제를 알아보겠습니다.
List<String> list = Arrays.asList("1", "2", "3", "4");
IntStream stream = list.stream()
.mapToInt(s -> Integer.parseInt(s)); // [1, 2, 3, 4]
2.2.3. flatMap, flatMapToInt, flatMapToLong, flatMapToDouble
flatMap()
은 조금 더 복잡합니다. 인자로 mapper
를 받고 리턴 타입이 Stream
입니다. 새로운 Stream 을 생성해서 리턴하는 람다를 넘겨야 하는데, flatMap
은 중첩 구조를 한 단계 제거하고 단일 컬렉션으로 만들어줍니다.
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
간단한 예제로 살펴보겠습니다.
List<List<String>> list = Arrays.asList(Arrays.asList("a"), Arrays.asList("b")); // [[a], [b]]
List<String> flatMap = list.stream()
.flatMap(Collection::stream)
.collect(Collectors.toList()); // [a, b]
flatMapToInt()
, flatMapToLong()
, flatMapToDouble()
는 위에서 설명한 mapToInt()
와 동일합니다.
2.3. Sorting
2.3.1. sorted
정렬도 다른 정렬과 동일하게 Comparator
를 사용합니다.
Stream<T> sorted(Comparator<? super T> comparator);
인자 없이 출력하면 디폴트 값은 오름차순입니다.
List<Integer> list = Arrays.asList(5, 7, 3, 1, 2, 6, 1, 9, 0);
Stream<Integer> stream = list.stream()
.sorted(); // [0, 1, 1, 2, 3, 4, 5, 6, 7, 9]
List<Integer> list = Arrays.asList(5, 7, 3, 1, 2, 6, 1, 9, 0);
Stream<Integer> stream = list.stream()
.sorted(Comparator.reverseOrder());
// [9, 7, 6, 5, 4, 3, 2, 1, 1, 0]
2.4. Iterating
2.4.1 peak
특정 결과를 반환하지 않는 Consumer
를 인자로 받으며, Stream 내 특정 작업을 수행할 뿐 결과에 영향을 미치지 않습니다. 아래 예제처럼 작업을 처리하는 중간에 결과를 확인할 때 사용할 수 있습니다.
Stream<T> peek(Consumer<? super T> action);
List<Integer> list = Arrays.asList(5, 7, 3, 1, 2, 6, 1, 9, 0);
Stream<Integer> stream = list.stream()
.peek(System.out::println)
.sorted(Comparator.reverseOrder());
3. 결과 만들기
3.1. Reduction
Stream 은 reduce()
라는 메소드를 이용해 결과를 만듭니다. 이 메소드는 3 가지의 파라미터를 가지고 있습니다.
accumulator
: 각 요소를 처리하는 계산 로직identity
: 계산을 위한 초깃값으로 Stream 이 비어서 계산할 내용이 없더라도 리턴한다combiner
: 병렬 Stream 처리할 때 나눠서 계산한 결과를 하나로 합치는 로직.
Optional<T> reduce(BinaryOperator<T> accumulator);
T reduce(T identity, BinaryOperator<T> accumulator);
<U> U reduce(U identity,
BiFunction<U, ? super T, U> accumulator,
BinaryOperator<U> combiner);
각각 인자별로 예제를 살펴보겠습니다.
// 45
OptionalInt reduce =
IntStream.range(1, 10)
.reduce((a, b) -> { return a + b; });
// 55
Integer reduce =
IntStream.range(1, 10)
.reduce(10, Integer::sum);
// 36
Integer reduce =
Stream.of(1, 2, 3)
.parallel()
.reduce(10, Integer::sum, (a, b) -> { return a + b; });
3.2. Collecting
필요한 요소룰 수집하여 새로운 Collection
으로 반환하는 메소드입니다. 이번 포스트에서는 간단히 다루고 추후 toList()
, joining()
과 같은 세부 메소드를 다루겠습니다.
<R> R collect(Supplier<R> supplier,
BiConsumer<R, ? super T> accumulator,
BiConsumer<R, R> combiner);
<R, A> R collect(Collector<? super T, A, R> collector);
아래 예제는 문자열 Stream 에서 “b” 가 포함된 문자를 filter
를 통하여 거르고 collect()
메소드를 이용하여 다시 List
로 만들어주는 예제입니다.
List<String> list =
Stream.of("a", "b", "c").filter(element -> element.contains("b"))
.collect(Collectors.toList());
Collector
에는 다양한 메소드가 존재하기 때문에 Java 8 Stream API 살펴보기 -4- Colector 살펴보기 위 포스에서 상세하게 다루겠습니다.
3.3. Calculating
기본형 Stream 의 통계가 있으며 T 타입 Stream 의 통계가 있습니다.
Optional<T> min(Comparator<? super T> comparator);
Optional<T> max(Comparator<? super T> comparator);
long count();
기본형 Stream 통계
count()
sum()
average()
min()
max()
3.4. Matching
3.4.1. anyMatch, allMatch, noneMatch
Stream 에서 찾고자 하는 객체가 존재하는지 탐색을 하고 boolean
타입을 리턴합니다. 메소드는 anyMatch()
, allMatch()
, noneMatch()
가 있습니다.
anyMatch()
는 하나라도 조건에 맞는 요소가 있으면true
를 리턴allMatch()
는 모든 요소가 조건에 맞아야true
를 리턴noneMatch()
는 조건에 맞는 객체가 없어야true
를 리턴
boolean anyMatch(Predicate<? super T> predicate);
boolean allMatch(Predicate<? super T> predicate);
boolean noneMatch(Predicate<? super T> predicate);
예제를 통해 한번에 알아보겠습니다.
List<String> list = Arrays.asList("A", "B", "C", "D");
boolean anyMatch = list.stream()
.anyMatch(s -> s.contains("A")); // true
boolean allMatch = list.stream()
.allMatch(s -> s.contains("A")); // false
boolean noneMatch = list.stream()
.noneMatch(s -> s.contains("A")); // false
3.5. Iterating
3.5.1. forEach
forEach()
는 요소를 돌면서 실행하는 최종 작업입니다. peek()
와의 차이는 중간 작업이냐, 최종 작업이냐의 차이가 있습니다.
void forEach(Consumer<? super T> action);
List<Integer> list = Arrays.asList(1, 2, 3);
list.stream().forEach(System.out::println); // 1 2 3
3.5.2. forEachOrdered
병렬처리에서 순서를 보장할 때 사용할 수 있습니다.
void forEachOrdered(Consumer<? super T> action);
// 651728934
IntStream.range(1, 10).parallel().forEach(System.out::print);
// 123456789
IntStream.range(1, 10).parallel().forEachOrdered(System.out::print);
참고자료
Leave a comment