Post

[JAVA]자바 Stream 사용방법 2편(중간연산)

스트림 관련글

JAVA[자바] Stream 사용방법 1편(생성하기)
JAVA[자바] Stream 사용방법 3편(최종연산)

스트림 중간연산

스트림 중간연산은 생성한 스트림을 어떻게 가공할지 정하는 과정이다. 참고로 중간연산은 여러번 사용할 수 있다!

1
List<String> names = Arrays.asList("Eric", "Elena", "Java");

위의 List 를 기준으로 stream 중간연산 함수에 대해 설명하겠다.

Filtering

1
Stream<T> filter(Predicate<? super T> predicate);

Predicateboolean 형을 리턴해주는 함수형 인터페이스이다. 간단히 말해서 필터링을 위해 어떤함수를 넣을때, 그 함수는 boolean 값을 리턴해줘야 한다.

1
2
3
String
Stream<String> stream = names.stream()
        .filter(name -> name.contains("a")) // Elena, Java;

이름에 “a” 가 포함되는 모든 문자열을 필터링한다. 만약 더 복잡한 조건을 원한다면 직접 함수를 커스텀해서 넣어줄 수 있다.

1
2
3
4
5
6
7
Stream<String> stream = names.stream()
                .filter(name -> {
                    if (name.contains("E") && !name.contains("r")) {
                        return true;
                    }
                    return false;
                }); // Elena

이름에 “E” 를 포함하고, “r” 를 포함하지 않는 이름으로 필터링 했다. 해당 부분을 외부함수로 빼내면 좀 더 객체지향적으로 만들 수 있다.

Mapping

1
<R> Stream<R> map(Function<? super T, ? extends R> mapper);

Function<? super T, ? extends R> 는 매개변수를 받아서 값을 리턴해주는 함수를 뜻한다. 즉 원하는 데이터형식으로 바꿔주는 함수를 넣어주면 해당 형식의 스트림으로 반환을 해준다.

1
2
3
4
Stream<String> stream = 
  names.stream()
  .map(String::toUpperCase);
// [ERIC, ELENA, JAVA]

mapString::toUpperCase 함수를 넣어주어 각각의 이름을 대문자로 변환시켜서 반환해준다. 이렇게 다른 형태로 값을 변경할때 사용한다.

그리고 좀 더 복잡한 flatmap 도 있습니다.

1
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

mapper 에 값을 보시면 Function<? super T, ? extends Stream<? extends R>> 으로 기존 map 과 다르게 ? extends Stream<> 으로 한번 더 감싸주었다. 그런데 리턴값은 map 과 똑같이 Stream<> 이다. 결과적으로 중첩된 구조를 한층 벗겨주는 역할한다.

1
2
3
4
5
6
List<List<String>> list =
        Arrays.asList(Arrays.asList("a", "1"), Arrays.asList("b", "2")); // [[a, 1], [b, 2]]

List<String> flatList = list.stream() // Stream<List<String>>
        .flatMap(s -> s.stream()) // Stream<String>
        .collect(Collectors.toList()); // [a, 1, b, 2] 

예제를 보면 이중리스트를 flatMap 을 이용해 하나의 리스트로 바꿔준것을 볼 수 있다. 이중리스트 구조를 단일 리스트로 구조를 한층 벗겨냈다.

Sorting

1
2
Stream<T> sorted();
Stream<T> sorted(Comparator<? super T> comparator);

기본 sorted 함수는 오름차순으로 정렬해준다. 이 외의 다른 방식의 정렬은 Comparator 를 이용해서 정렬할 수 있다. 정렬 기준에대해 자세히 알고싶다면 JAVA[자바] Comparable 과 Comparator 참고하자.

1
2
3
4
names.stream()
  .sorted((s1, s2) -> s2.length() - s1.length())
  .collect(Collectors.toList());
//Elena, Eric, Java

Comparator 를 이용할 경우 정렬 기준을 직접 작성할 수 있다. 물론 기본으로 Comparator 에서 기본으로 제공하는 함수도 있다. 예를 들어 Comparator.reverseOrder() 를 사용하면 내림차순으로 정렬이 가능하다.

Boxed

기본스트림을 다른 타입으로 변경해줍니다.

1
2
3
4
5
IntStream.of(14, 11, 20, 39, 23) // IntStream
  .sorted() // IntStream
  .boxed() // Stream<Integer>
  .collect(Collectors.toList());
// [11, 14, 20, 23, 39]

IntStream 을 정렬을 한뒤 리스트 형태로 반환할려고 했다. ()collect(Collectors.toList()) 최종연산으로 값을 리스트로 반환해준다)그런데 IntStream 에는 collect 함수를 지원하지 않기때문에 boxed 를 이용해 Stream<Integer> 로 바꿔준뒤 사용해야한다. 이런 IntSream, DoubleStream, LongStream 형식 으로 지원하지 않는 함수가 있으면 boxed 를 사용해 타입을 바꾸면 사용할 수 있다.

Peek

stream 의 각각의 연산을 대상으로 연산 한다. 결과값에 영향을 끼치지 않아 중간에 값을 확인할때 사용한다.

하지만 결과값에 영향을 끼치지 않을뿐, 해당 함수를 이용해 다른 리스트에 값을 추가하는 등 연산을 할 수 있다. 물론 권장 하는 방식은 아니다.

1
2
3
int sum = IntStream.of(1, 3, 5, 7, 9)
  .peek(System.out::println)
  .sum();

Distinct

요소내 중복값을 제거해준다. 참고로 객체간의 비교를 하면 equals() 로 비교를하기 때문에 원하는 결과를 얻을려면 해당 클래스에 equals() 오버라이딩이 필요하다.

1
2
3
4
5
List<String> stringNumbers = Arrays.asList("1", "2", "3", "1");

List<String> distinctNumbers = stringNumbers.stream()
        .distinct()
        .collect(Collectors.toList()); // 1, 2, 3

Limit

결과값의 개수를 제한한다.

1
2
Stream<String> generatedStream =
        Stream.generate(() -> "g").limit(3); // [g, g, g]

Skip

첫번째 요소부터 설정값까지 건너뛴다.

1
2
Stream<String> stream = names.stream()
        .skip(1); // "Elena", "Java"

Reference

https://futurecreator.github.io/2018/08/26/java-8-streams/

This post is licensed under CC BY 4.0 by the author.