시뻘건 개발 도전기

STREAM 활용 본문

프로그래밍/JAVA

STREAM 활용

시뻘건볼때기 2022. 8. 23. 16:21
반응형

1. Stream을 활용한 최대값, 최소값, 개수 추출하기

/**
 * Returns the minimum element of this stream according to the provided
 * {@code Comparator}.  This is a special case of a
 * <a href="package-summary.html#Reduction">reduction</a>.
 *
 * <p>This is a <a href="package-summary.html#StreamOps">terminal operation</a>.
 *
 * @param comparator a <a href="package-summary.html#NonInterference">non-interfering</a>,
 *                   <a href="package-summary.html#Statelessness">stateless</a>
 *                   {@code Comparator} to compare elements of this stream
 * @return an {@code Optional} describing the minimum element of this stream,
 * or an empty {@code Optional} if the stream is empty
 * @throws NullPointerException if the minimum element is null
 */
Optional<T> min(Comparator<? super T> comparator);

/**
 * Returns the maximum element of this stream according to the provided
 * {@code Comparator}.  This is a special case of a
 * <a href="package-summary.html#Reduction">reduction</a>.
 *
 * <p>This is a <a href="package-summary.html#StreamOps">terminal
 * operation</a>.
 *
 * @param comparator a <a href="package-summary.html#NonInterference">non-interfering</a>,
 *                   <a href="package-summary.html#Statelessness">stateless</a>
 *                   {@code Comparator} to compare elements of this stream
 * @return an {@code Optional} describing the maximum element of this stream,
 * or an empty {@code Optional} if the stream is empty
 * @throws NullPointerException if the maximum element is null
 */
Optional<T> max(Comparator<? super T> comparator);

/**
 * Returns the count of elements in this stream.  This is a special case of
 * a <a href="package-summary.html#Reduction">reduction</a> and is
 * equivalent to:
 * <pre>{@code
 *     return mapToLong(e -> 1L).sum();
 * }</pre>
 *
 * <p>This is a <a href="package-summary.html#StreamOps">terminal operation</a>.
 *
 * @return the count of elements in this stream
 */
long count();

 

  • Stream에 담겨있는 데이터 중 최대값을 리턴하는 max (Stream이 empty면 empty인 Optional 리턴)
  • Stream에 담겨있는 데이터 중 최소값을 리턴하는 min (Stream이 empty면 empty인 Optional 리턴)
  • Stream에 담겨 있는 데이터의 수를 리턴하는 count

예제를 살펴보자.

int max = Stream.of(1, 2, 3, -5, 0).max(Integer::compareTo).get();
System.out.println(max);    // 3

User user1 = User.builder()
        .id(1)
        .name("아이언맨")
        .email("ironMan@aaa.co.kr")
        .isVerified(true)
        .build();

User user2 = User.builder()
        .id(2)
        .name("캡틴")
        .email("captain@aaa.co.kr")
        .isVerified(false)
        .build();

User user3 = User.builder()
        .id(3)
        .name("토르")
        .email("thor@aaa.co.kr")
        .isVerified(false)
        .build();

List<User> users = Arrays.asList(user1, user2, user3);

User firstUser = users.stream()
        .min((u1, u2) -> u1.getName().compareTo(u2.getName()))
        .get();

System.out.println(firstUser);  // User(id=1, name=아이언맨, email=ironMan@aaa.co.kr, isVerified=true)

long count = Stream.of(1, 2, -1, 0)
        .filter(x -> x >= 0)
        .count();

System.out.println(count);  // 3

 

2. Stream을 활용한 조건 만족 check

/**
 * Returns whether any elements of this stream match the provided
 * predicate.  May not evaluate the predicate on all elements if not
 * necessary for determining the result.  If the stream is empty then
 * {@code false} is returned and the predicate is not evaluated.
 *
 * <p>This is a <a href="package-summary.html#StreamOps">short-circuiting
 * terminal operation</a>.
 *
 * @apiNote
 * This method evaluates the <em>existential quantification</em> of the
 * predicate over the elements of the stream (for some x P(x)).
 *
 * @param predicate a <a href="package-summary.html#NonInterference">non-interfering</a>,
 *                  <a href="package-summary.html#Statelessness">stateless</a>
 *                  predicate to apply to elements of this stream
 * @return {@code true} if any elements of the stream match the provided
 * predicate, otherwise {@code false}
 */
boolean anyMatch(Predicate<? super T> predicate);

/**
 * Returns whether all elements of this stream match the provided predicate.
 * May not evaluate the predicate on all elements if not necessary for
 * determining the result.  If the stream is empty then {@code true} is
 * returned and the predicate is not evaluated.
 *
 * <p>This is a <a href="package-summary.html#StreamOps">short-circuiting
 * terminal operation</a>.
 *
 * @apiNote
 * This method evaluates the <em>universal quantification</em> of the
 * predicate over the elements of the stream (for all x P(x)).  If the
 * stream is empty, the quantification is said to be <em>vacuously
 * satisfied</em> and is always {@code true} (regardless of P(x)).
 *
 * @param predicate a <a href="package-summary.html#NonInterference">non-interfering</a>,
 *                  <a href="package-summary.html#Statelessness">stateless</a>
 *                  predicate to apply to elements of this stream
 * @return {@code true} if either all elements of the stream match the
 * provided predicate or the stream is empty, otherwise {@code false}
 */
boolean allMatch(Predicate<? super T> predicate);
  • Stream에 담겨있는 데이터가 predicate를 만족하면 true를 리턴하는 allMatch
  • Stream에 담겨있는 데이터중 하나라도 predicate를 만족하면 true를 리턴하는 anyMatch

 

예제를 살펴보자.

List<Integer> numbers = Arrays.asList(1, 2, 3, 0, -5);
boolean allPostive = numbers.stream()
        .allMatch(n -> n > 0);
System.out.println(allPostive); // false

boolean anyPostive = numbers.stream()
        .anyMatch(n -> n < 0);
System.out.println(anyPostive); // true

anyPostive = numbers.stream()
        .anyMatch(n -> n == -5);
System.out.println(anyPostive); // true

 

3. Stream을 활용한 데이터 리턴

/**
 * Returns an {@link Optional} describing the first element of this stream,
 * or an empty {@code Optional} if the stream is empty.  If the stream has
 * no encounter order, then any element may be returned.
 *
 * <p>This is a <a href="package-summary.html#StreamOps">short-circuiting
 * terminal operation</a>.
 *
 * @return an {@code Optional} describing the first element of this stream,
 * or an empty {@code Optional} if the stream is empty
 * @throws NullPointerException if the element selected is null
 */
Optional<T> findFirst();

/**
 * Returns an {@link Optional} describing some element of the stream, or an
 * empty {@code Optional} if the stream is empty.
 *
 * <p>This is a <a href="package-summary.html#StreamOps">short-circuiting
 * terminal operation</a>.
 *
 * <p>The behavior of this operation is explicitly nondeterministic; it is
 * free to select any element in the stream.  This is to allow for maximal
 * performance in parallel operations; the cost is that multiple invocations
 * on the same source may not return the same result.  (If a stable result
 * is desired, use {@link #findFirst()} instead.)
 *
 * @return an {@code Optional} describing some element of this stream, or an
 * empty {@code Optional} if the stream is empty
 * @throws NullPointerException if the element selected is null
 * @see #findFirst()
 */
Optional<T> findAny();
  • Stream에 담겨있는 데이터 중, 첫 번째 데이터 리턴하는 findFirst (Stream이 empty면 empty인 Optional 리턴)
  • Stream에 담겨있는 데이터 중, 랜덤으로 데이터 리턴하는 findAny (Stream이 empty면 empty인 Optional 리턴)

예제를 살펴보자.

List<Integer> numbers = Arrays.asList(2, 9, 3, 0, -5);
int first = numbers.stream()
        .findFirst()
        .get();
System.out.println(first);  // 2

int any = numbers.stream()
        .findAny()
        .get();
System.out.println(any);    // ?

 

4. Stream에 담긴 데이터를 하나의 값으로 계속 함수를 수행

[그림 1] reduce

 

/**
 * Performs a <a href="package-summary.html#Reduction">reduction</a> on the
 * elements of this stream, using the provided identity value and an
 * <a href="package-summary.html#Associativity">associative</a>
 * accumulation function, and returns the reduced value.  This is equivalent
 * to:
 * <pre>{@code
 *     T result = identity;
 *     for (T element : this stream)
 *         result = accumulator.apply(result, element)
 *     return result;
 * }</pre>
 *
 * but is not constrained to execute sequentially.
 *
 * <p>The {@code identity} value must be an identity for the accumulator
 * function. This means that for all {@code t},
 * {@code accumulator.apply(identity, t)} is equal to {@code t}.
 * The {@code accumulator} function must be an
 * <a href="package-summary.html#Associativity">associative</a> function.
 *
 * <p>This is a <a href="package-summary.html#StreamOps">terminal
 * operation</a>.
 *
 * @apiNote Sum, min, max, average, and string concatenation are all special
 * cases of reduction. Summing a stream of numbers can be expressed as:
 *
 * <pre>{@code
 *     Integer sum = integers.reduce(0, (a, b) -> a+b);
 * }</pre>
 *
 * or:
 *
 * <pre>{@code
 *     Integer sum = integers.reduce(0, Integer::sum);
 * }</pre>
 *
 * <p>While this may seem a more roundabout way to perform an aggregation
 * compared to simply mutating a running total in a loop, reduction
 * operations parallelize more gracefully, without needing additional
 * synchronization and with greatly reduced risk of data races.
 *
 * @param identity the identity value for the accumulating function
 * @param accumulator an <a href="package-summary.html#Associativity">associative</a>,
 *                    <a href="package-summary.html#NonInterference">non-interfering</a>,
 *                    <a href="package-summary.html#Statelessness">stateless</a>
 *                    function for combining two values
 * @return the result of the reduction
 */
T reduce(T identity, BinaryOperator<T> accumulator);

/**
 * Performs a <a href="package-summary.html#Reduction">reduction</a> on the
 * elements of this stream, using an
 * <a href="package-summary.html#Associativity">associative</a> accumulation
 * function, and returns an {@code Optional} describing the reduced value,
 * if any. This is equivalent to:
 * <pre>{@code
 *     boolean foundAny = false;
 *     T result = null;
 *     for (T element : this stream) {
 *         if (!foundAny) {
 *             foundAny = true;
 *             result = element;
 *         }
 *         else
 *             result = accumulator.apply(result, element);
 *     }
 *     return foundAny ? Optional.of(result) : Optional.empty();
 * }</pre>
 *
 * but is not constrained to execute sequentially.
 *
 * <p>The {@code accumulator} function must be an
 * <a href="package-summary.html#Associativity">associative</a> function.
 *
 * <p>This is a <a href="package-summary.html#StreamOps">terminal
 * operation</a>.
 *
 * @param accumulator an <a href="package-summary.html#Associativity">associative</a>,
 *                    <a href="package-summary.html#NonInterference">non-interfering</a>,
 *                    <a href="package-summary.html#Statelessness">stateless</a>
 *                    function for combining two values
 * @return an {@link Optional} describing the result of the reduction
 * @throws NullPointerException if the result of the reduction is null
 * @see #reduce(Object, BinaryOperator)
 * @see #min(Comparator)
 * @see #max(Comparator)
 */
Optional<T> reduce(BinaryOperator<T> accumulator);

/**
 * Performs a <a href="package-summary.html#Reduction">reduction</a> on the
 * elements of this stream, using the provided identity, accumulation and
 * combining functions.  This is equivalent to:
 * <pre>{@code
 *     U result = identity;
 *     for (T element : this stream)
 *         result = accumulator.apply(result, element)
 *     return result;
 * }</pre>
 *
 * but is not constrained to execute sequentially.
 *
 * <p>The {@code identity} value must be an identity for the combiner
 * function.  This means that for all {@code u}, {@code combiner(identity, u)}
 * is equal to {@code u}.  Additionally, the {@code combiner} function
 * must be compatible with the {@code accumulator} function; for all
 * {@code u} and {@code t}, the following must hold:
 * <pre>{@code
 *     combiner.apply(u, accumulator.apply(identity, t)) == accumulator.apply(u, t)
 * }</pre>
 *
 * <p>This is a <a href="package-summary.html#StreamOps">terminal
 * operation</a>.
 *
 * @apiNote Many reductions using this form can be represented more simply
 * by an explicit combination of {@code map} and {@code reduce} operations.
 * The {@code accumulator} function acts as a fused mapper and accumulator,
 * which can sometimes be more efficient than separate mapping and reduction,
 * such as when knowing the previously reduced value allows you to avoid
 * some computation.
 *
 * @param <U> The type of the result
 * @param identity the identity value for the combiner function
 * @param accumulator an <a href="package-summary.html#Associativity">associative</a>,
 *                    <a href="package-summary.html#NonInterference">non-interfering</a>,
 *                    <a href="package-summary.html#Statelessness">stateless</a>
 *                    function for incorporating an additional element into a result
 * @param combiner an <a href="package-summary.html#Associativity">associative</a>,
 *                    <a href="package-summary.html#NonInterference">non-interfering</a>,
 *                    <a href="package-summary.html#Statelessness">stateless</a>
 *                    function for combining two values, which must be
 *                    compatible with the accumulator function
 * @return the result of the reduction
 * @see #reduce(BinaryOperator)
 * @see #reduce(Object, BinaryOperator)
 */
<U> U reduce(U identity,
             BiFunction<U, ? super T, U> accumulator,
             BinaryOperator<U> combiner);

reduce는 3가지로 오버로딩되어있다.

  • accumulator를 이용해 데이터를 합쳐 Optional을 리턴하는 reduce
  • 초기값과 accumulator를 이용하여 함쳐 제너럴 타입을 리턴하는 reduce
  • 합치는 과정에서 타입이 바뀔 경우 사용하는 reduce

예제로 살펴보자.

List<Integer> numbers = Arrays.asList(2, 9, 3, -9);
int sum = numbers.stream()
        .reduce((x, y) -> x + y)
        .get();
System.out.println(sum);    // 5

// max, min, count도 reduce의 일종이라는 것을 알 수 있다.
int max = numbers.stream()
        .reduce((x, y) -> x > y ? x : y)
        .get();
System.out.println(max);    // 9

int initialized = numbers.stream()
        .reduce(9, (x, y) -> x * y);
System.out.println(initialized);    // -4374

 

4. Stream에 담긴 데이터를 collection으로 리턴


/**
 * Performs a <a href="package-summary.html#MutableReduction">mutable
 * reduction</a> operation on the elements of this stream using a
 * {@code Collector}.  A {@code Collector}
 * encapsulates the functions used as arguments to
 * {@link #collect(Supplier, BiConsumer, BiConsumer)}, allowing for reuse of
 * collection strategies and composition of collect operations such as
 * multiple-level grouping or partitioning.
 *
 * <p>If the stream is parallel, and the {@code Collector}
 * is {@link Collector.Characteristics#CONCURRENT concurrent}, and
 * either the stream is unordered or the collector is
 * {@link Collector.Characteristics#UNORDERED unordered},
 * then a concurrent reduction will be performed (see {@link Collector} for
 * details on concurrent reduction.)
 *
 * <p>This is a <a href="package-summary.html#StreamOps">terminal
 * operation</a>.
 *
 * <p>When executed in parallel, multiple intermediate results may be
 * instantiated, populated, and merged so as to maintain isolation of
 * mutable data structures.  Therefore, even when executed in parallel
 * with non-thread-safe data structures (such as {@code ArrayList}), no
 * additional synchronization is needed for a parallel reduction.
 *
 * @apiNote
 * The following will accumulate strings into an ArrayList:
 * <pre>{@code
 *     List<String> asList = stringStream.collect(Collectors.toList());
 * }</pre>
 *
 * <p>The following will classify {@code Person} objects by city:
 * <pre>{@code
 *     Map<String, List<Person>> peopleByCity
 *         = personStream.collect(Collectors.groupingBy(Person::getCity));
 * }</pre>
 *
 * <p>The following will classify {@code Person} objects by state and city,
 * cascading two {@code Collector}s together:
 * <pre>{@code
 *     Map<String, Map<String, List<Person>>> peopleByStateAndCity
 *         = personStream.collect(Collectors.groupingBy(Person::getState,
 *                                                      Collectors.groupingBy(Person::getCity)));
 * }</pre>
 *
 * @param <R> the type of the result
 * @param <A> the intermediate accumulation type of the {@code Collector}
 * @param collector the {@code Collector} describing the reduction
 * @return the result of the reduction
 * @see #collect(Supplier, BiConsumer, BiConsumer)
 * @see Collectors
 */
<R, A> R collect(Collector<? super T, A, R> collector);
  • Stream을 원하는 collection으로 바꿔줄 때 주로 사용하는 collector

 

예제로 살펴보자.

List<Integer> list = Stream.of(1,2,3)
        .collect(Collectors.toList());
System.out.println(list);   // [1, 2, 3]

Set<Integer> set = Stream.of(1,2,3,3,3,3,3)
        .collect(Collectors.toSet());
System.out.println(set);    // [1, 2, 3]


list = Stream.of(-1,2,-3)
        .collect(Collectors.mapping(x -> Math.abs(x), Collectors.toList()));
System.out.println(list);   // [1, 2, 3]

// reduce대신 아래와 같이 사용할 수도 있다.
int sum = Stream.of(-1,2,-3)
        .collect(Collectors.reducing(0, (x, y) -> x + y));
System.out.println(sum);   // -2
반응형

'프로그래밍 > JAVA' 카테고리의 다른 글

Optional  (0) 2022.08.12
Stream  (0) 2022.07.25
Method Reference  (0) 2022.07.14
Functional Interface  (0) 2022.07.06
Lambda Expression  (0) 2022.07.06
Comments