본문 바로가기

CS/Java

Java FP 3. Streams

Stream!

겁나 쉬운줄 알았는데 역시나 object 개념들어가니까 바로 적용 못하고~

개념같은 건 구글링하면 나오니까 헷갈리는 것만 오답노트 시작!

 

맨 처음에 바보같이 filtermap을 헷갈려했는데,

- filter는 stream에서 조건에 맞는 것들만 살려두는 것

- map은 stream에서 모든 element들에 대해 명령을 실행하는 것

완전 다른 거라 헷갈릴 이유가 없다!

 

Use Stream.range() or Stream.rangeClosed(). Method rangeClosed() includes an upper bound.

LongStream rangedStream = LongStream.rangeClosed(100_000, 1_000_000);

 

Mapreduce는 용법이 다양하고 자주쓰이는데 그만큼 헷갈리기도 한다.

flatMap은 stream[ stream, stream] 일 때, stream 안에 있는 stream들의 element를 밖으로 빼주는,, 그니까 2차원에서 1차원으로 펼쳐주는 느낌...? python의 flatten느낌이당~ 근데 거기서 map의 기능이 추가됨.

The flatMap operation is used to transform each element from a stream into another stream and concatenates all streams into one. For instance, to obtain elements of an internal collection in a class.

Let's imagine there is a collection of java books exists. Each book has a title, a year of publishing and a list of authors (only names). It is necessary to obtain a collection of all authors from the list of java books.

// the collection of java books
final List<Book> javaBooks = Stream.of(
        new Book("Java EE 7 Essentials", 2013, Arrays.asList("Arun Gupta")),
        new Book("Algorithms", 2011, Arrays.asList("Robert Sedgewick", "Kevin Wayne")),
        new Book("Clean code", 2014, Arrays.asList("Robert Martin"))
).collect(Collectors.toList());

// list of authors
final List<String> authors = javaBooks.stream()
        .flatMap(book -> book.getAuthors().stream())
        .distinct()
        .collect(Collectors.toList());

여기서 book이라는 객체 안에는 authors라는 배열이 있는데, 이 배열을 가져와서 stream으로 만든다음, flatMap을 사용해 1차원의, 하나의 stream을 만들어 준다.

 

 

 

reduce의 용법은 다음과 같다.

int sum = numbers.stream().reduce(0, (acc, elem) -> acc + elem);

0은 초기값, acc은 누적, elem은 다음 elements를 의미한다.

 

 

 

내가 진짜 헷갈렸던 예제는 다음과 같다.

 

You have two classes:

  • Transaction: uuid: String,  state: State (CANCELED, FINISHED, PROCESSING), sum: Long, created: Date

  • Account: number: String, balance: Long, transactions: List<Transaction>

Both classes have getters for all fields with the corresponding names (getState(), getSum(), getTransactions() and so on).

Write a method using Stream API that calculates the total sum of canceled transactions for all non-empty accounts (balance > 0). Perhaps, flatMap method can help you to implement it.

 

Classes TransactionAccount and enum State will be available during testing. You can define your own classes for local testing.

Important. Use the given template for your method. Pay attention to type of an argument and the returned value. Please, use only Stream API, no cycles.

Examples: there are 2 accounts (in JSON notation) below. The result is 10 000.

[
  {
    "number": "1001",
    "balance": 0,
    "transactions": [
      {
        "uuid": "774cedda-9cde-4f53-8bc2-5b4d4859772a",
        "state": "CANCELED",
        "sum": 1000,
        "created": "2016.12.12 18:30:05"
      }
    ]
  },
  {
    "number": "1002",
    "balance": 8000,
    "transactions": [
      {
        "uuid": "337868a7-f469-43c0-9042-3422ce37ee26a",
        "state": "FINISHED",
        "sum": 8000,
        "created": "2016.12.12 17:30:55"
      },
      {
        "uuid": "f8047f86-89e7-4226-8b75-74c55a4d7e31",
        "state": "CANCELED",
        "sum": 10000,
        "created": "2016.12.12 18:10:18"
      }
    ]
  }
]

그리고 아래의 코드들이 여러 답중 하나

/**
 * Calculates the general sum of canceled transactions for all non empty accounts in the list
 */
public static long calcSumOfCanceledTransOnNonEmptyAccounts(List<Account> accounts) {
    long canceledTrx = accounts.stream().filter(account -> account.getBalance() > 0)
        .flatMap(account -> account.getTransactions().stream())
        .filter(trx -> trx.getState() == State.CANCELED)
        .mapToLong(trx -> trx.getSum()).sum();
    return canceledTrx;
    // write your code here
}
/**
 * Calculates the general sum of canceled transactions for all non empty accounts in the list
 */
public static long calcSumOfCanceledTransOnNonEmptyAccounts(List<Account> accounts) {
    long canceledTrx = accounts.stream().filter(account -> account.getBalance() > 0)
        .flatMap(account -> account.getTransactions().stream())
        .filter(trx -> trx.getState() == State.CANCELED)
        .map(trx -> trx.getSum()).reduce(0L, (acc, next)->acc+next);
    return canceledTrx;
    // write your code here
}

State::CANCELED인 transaction들까지 filtering했는데, 그 trx들의 total sum을 구하는 데서 버벅거렸다.

여기서 마지막에 map만들고, 거기서 summaryStatistics()를 사용하려 했는데 계속 컴파일 에러가 났다. 근데 그건 IntStream의 API라서 그런 것이었다~~ 하하하 멍충이

 

 

오늘도 역시, 자바 API 몰라서 버벅거린 게 많다...

collection이 뭔지도 몰랐고, Array, ArrayList, List가 다 다른건지도 몰랐다. ArrayList는 뭐지? Array는 C++의 배열, List는 C++의 벡터 정도로 생각하면 될 것 같다. 

아 그리고 좀 좋은거! String.startsWith("text")하면 text가 어두로 쓰였는지 boolean으로 알 수 있다.

'CS > Java' 카테고리의 다른 글

Java FP 5. Monads  (7) 2020.04.07
Java FP 4. Currying  (5) 2020.04.07
Java FP 2. Functions are objects  (13) 2020.03.26
Java FP 1. Lambda expressions and method references  (14) 2020.03.26