그림에서 이해하는 목록 처리 - java8 stream/javaslang -

「java8 로 stream 해 보았지만 익숙해지-」라든지 「조금 어려운 일을 하려고 하면 할 수 없어」라고 하는 것을 잘 듣기 때문에 정리해 보겠습니다.

형마저 파악하면 8할방 알 수 있었던 것 같은 것이므로, 그것을 그림으로 해 볼게.

덧붙여서, 기사는 길지만 최초의 [本編 - 라고 표제의 개소까지 읽으면 충분하다!

역시 냉정하게 생각하면 너무 길기 때문에 분할했다.

[본편 - java8 stream]: filter, map, reduce



기본의 3손을 누르자.
reduce 에는 친숙하지 않을지도 모르지만, Google 가 제창했다 MapReduce 모델이므로 map 와는 함께 말할 수 있어. -> 위키

아래의 예제를 사용해 봐 갈게.
/*
 * 以下の様なログがある
 * ラベル もしくは ラベル:millisec の形式で1行ずつ出力される
 *
 * millisec の総和を求めよ
 */
List<String> lines = Arrays.asList(
    "application-boot", "access-dispatch:10", "dispatched", "authentication:30", "database-access:45", "response-creation:15", "access-logging"
);

중요한 것은 어쨌든 형이므로, Generics 에 비비지 않고 거기만은 확실히 확인해 갈거야.

그리고 이런 그림이 대량으로 나올거야.


A -> B f 라고 써 있는 것은 java 의 코드로 하면 B f(a); 라고 하는 것으로, A 이나 B 에는 String 이나 Optional<Integer> 등의 구체형이 들어갑니다. 즉 Generics 입니다.

filter


filter의 정의는 다음과 같다.
Stream<T> filter(Predicate<? super T> predicate);
Predicate<T> 는 「인수가 T 로 반환값이 bool? super T 의 형표기를 Predicate<T> 라고 잡고, T を bool にする 라고 써 보자.
그런 다음 Generics도 단순화됩니다.
Stream<T> filter((T -> bool) f);
(T -> bool) 를 사용하여 filterlines 의 한 행에만 만드는 것은 이렇게 쓸 수 있다.
lines.stream()
        .filter(line -> line.contains(":"))                  // ["access-dispatch:10", "authentication:30", "database-access:45", "response-creation:15"]

정의를 : 라고 써 보면, 구현의 T -> bool 가 같게 보인다.
(이 주제에서는 line -> line.contains(":") 는 구체적으로는 T 라는 것)

그림에 그리면 이런 느낌일까.


String 는 형은 변하지 않고, 수가 바뀌는 변환이라는 것이다.


filter의 정의는 다음과 같다.
<R> Stream<R> map(Function<? super T, ? extends R> mapper);

이쪽도 똑같이 간략화하면 이렇다.
Stream<R> map((T -> R) f);
map 를 사용하여 map 의 행을 ラベル:millisec 로 하는 것은, Integer 의 계속에 이렇게 쓸 수 있다.
lines.stream()
        .filter(line -> line.contains(":"))                  // ["access-dispatch:10", "authentication:30", "database-access:45", "response-creation:15"]
        .map(line -> Integer.valueOf(line.split(":")[1]))    // [10, 30, 45, 15]

여기도 정의의 filter 와 구현이 같은 형태를 하고 있다.
(이 주제에서는, T -> R 는 구체적으로는 R 라는 것이니까, IntegerT -> R 로 보이고 있으면 좋은 느낌)

그림에 그리면 이런 느낌일까.


String -> Integer 는 수는 변하지 않고, 형이 바뀌는 변환이라는 것이다.

reduce


map 의 정의는 java8 stream 에는 3개 있습니다만, 지금부터 사용하는 것은 이렇다.
T reduce(T identity, BinaryOperator<T> accumulator);
reduce 는 조금 낯설지만, 「인수가 BinaryOperator<T> 를 2개로 반환값도 T 」라고 하는 것이다.
이것도 비비하지 않고 간략화해 보자.
T reduce(T t, ((T, T) -> T) f);
T 는 일반적으로 「컨벌루션」이라고 말해져 있어 리스트의 선두로부터 순서대로 결과를 축적해 가는 처리이다.
reduce 를 사용하여 reduce 의 줄을 Integer 의 합계로 만드는 것은 Integer 의 다음에 이렇게 쓸 수 있다.
lines.stream()
        .filter(line -> line.contains(":"))                  // ["access-dispatch:10", "authentication:30", "database-access:45", "response-creation:15"]
        .map(line -> Integer.valueOf(line.split(":")[1]))    // [10, 30, 45, 15]
        .reduce(0, (acc, n) -> acc + n)                      // 100

정의에 조금 괄호가 많지만, mapT, (T, T) -> T 가 같은 형태를 하고 있기 때문에 조금은 알기 쉬워야 한다.

그림에 그리면 이런 느낌일까.


0, (acc, n) -> ... 는 리스트의 요소가 단일의 결과가 되는 처리이다.

요약



이것으로 클리어!
 * millisec の総和を求めよ


// ( 再掲 )

lines.stream()
        .filter(line -> line.contains(":"))                  // ["access-dispatch:10", "authentication:30", "database-access:45", "response-creation:15"]
        .map(line -> Integer.valueOf(line.split(":")[1]))    // [10, 30, 45, 15]
        .reduce(0, (acc, n) -> acc + n)                      // 100

에서 풀었다.

일단 전체 그림을 올려 놓는다.





여기에서 앞의 모든 덤은 아래의 기사로 분리했다.

그림에서 이해하는 목록 처리 - java8 stream / javaslang - 덤

깨끗이.

좋은 웹페이지 즐겨찾기