본문 바로가기

Web/JAVA

[JAVA 8↑] Stream

728x90

https://java-8-tips.readthedocs.io/en/stable/streamsapi.html

 

6. Stream API — Java 8 tips 1.0 documentation

Docs » 6. Stream API Edit on GitHub 6. Stream API In the previous chapter you saw how streams are related to collections, various stream sources and kind of stream operations. In this chapter we will have an extensive look at various operations supported

java-8-tips.readthedocs.io

 

https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html

 

Stream (Java Platform SE 8 )

A sequence of elements supporting sequential and parallel aggregate operations. The following example illustrates an aggregate operation using Stream and IntStream: int sum = widgets.stream() .filter(w -> w.getColor() == RED) .mapToInt(w -> w.getWeight())

docs.oracle.com

 

 

잘못된 내용이 있다면 알려주세요 감사합니다 😀

 

 


 

Stream 은 일반적인 명령형 프로그래밍 기술이 아닌 선언적 스타일로 코드를 작성하는 java8 의 release 기능 중의 핵심적인 기능 중 하나이다.

Stream은 원하는 것을 달성하는 것이 목표가 아니라, 이렇게 되게 해달라고 기대하는 코드라고 생각하면 좋을 것 같다.

a + b 를 구해라가 아니라, a가 b라면 a 랑 b를 더하고 어떻게 하라 라는 미래지향적인(?) 실행 전략 처리이다

 

stream 으로 코드 작성이 끝나면 java 8은 내부적으로 이를 실행할 수 있도록 준비하고, foreach로 처리한다

 

 

 

5.1. Stream vs Collection

Most of the time collections are one of the main source for stream to act on. Stream and collection are used togather, they don’t replace each other. Streams differ from collection in several ways:

  • No storage: Collections are typically physical set of data where as streams are a logial view that will be supplied to a pipeline of operations. Collections are about data and streams are about computations.
  • Functional in nature: An operation on a stream produces a result, but does not modify its source. For example, if we call filtering on a stream it will return a new stream rather than removing them from the original collection.
  • Lazyness execution: Many of stream operation like filtering, mapping etc are chained togather and executed in one shot using a terminal operation. This technique helps to create optimized execution strategy to process the operations. For example, to find first three odd numbers from a stream it doesn’t go through the complete data set and halts the execution once three values found.
  • Possibly unbounded: While collections have a finite size, streams need not. Short-circuiting operations such as limit(n) or findFirst() can allow computations on infinite streams to complete in finite time.
  • Consumable: The elements of a stream are only visited once during the life of a stream. Like an Iterator, a new stream must be generated to revisit the same elements of the source. If the source is FileInputStream etc, then you are out of luck because inputstream will be closed once consumed and you cann’t regenerate the stream.

 

 

사용할 수 있는 예시는

  • From a Collection via the stream() and parallelStream() methods;
  • From an array via Arrays.stream(T[]);
  • From static factory methods on the stream classes, such as Stream.of(T[]), IntStream.range(int, int) or Stream.iterate(T, UnaryOperator);
  • The lines of a file can be obtained from BufferedReader.lines();
  • Streams of file paths can be obtained from methods in Files;
  • Streams of random numbers can be obtained from Random.ints();

 

 

Stream 의 파이프라인을 형성하기 위해서 intermediate 작업과 terminal 작업을 구분한다

 

 

 

 

 

Stream API의 주요기능을 보자면 크게 Filter, Truncating, Consuming, Mapping, Matching, Finding element, Stream Reduction, To Array, Infinite Streams 가 있다

그리고 contains additional operations min, max, sum etc.

 

 

 

 

Filtering

 

Signature:

Stream<T> filter(Predicate<? super T> p)
// Finding words starts with vowel
List<String> words = Stream.of("apple", "mango", "orange")
                            .filter(s -> s.matches("^[aeiou].*"))
                            .collect(toList());

:Output: [apple, orange]

 

Truncating Stream

limit(n) n갯수 만큼 데이터를 가져와서 새로운 스트림 생성 후 반환, 주어진 n보다 작으면 전체를 반환

skip(n) n갯수 만큼 요소를 건너띄고 그 이후의 데이터를 가져와서 새로운 스트림 생성 후 반환

 

Signature:

Stream<T> limit(long maxSize)
Stream<T> skip(long n)
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
stream.filter(i -> i%2 == 0).limit(2).collect(toList());
stream.filter(i -> i%2 == 0).skip(1).collect(toList());

 

 

Consuming Stream

peak : 중간 처리 method

forEach : 최종 처리 method

 

**중간 처리 method 는 최종 처리 메소드가 있어야만 동작한다 (없으면 실행 되기 전까지 lazy됨)

 

Signature:

Stream<T> peek(Consumer<? super T> action)
void forEach(Consumer<? super T> action)

The peek is an intermediate operation which returns the new stream where as forEach is the terminal operation returns void.

Stream<Integer> stream = Stream.of(1, 2, -3, 4, 5);
stream.filter(i -> i%2 == 0).peek(System.out::println).toArray();
stream.filter(i -> i%2 == 0).forEach(System.out::println);

 

보면 peak 뒤에는 무조건 최종처리가 가능한 메소드가 붙어야 한다

예를 들면 sum(), foreach() 등등 

 

Mapping

일반적으로 mapping 은 데이터 중에 필요한 특정 개체를 선택하여 id와 함께 map 화 시키는 것이다

 

Signature:

<R> Stream<R> map(Function<? super T, ? extends R> mapper)
List<Trade> trades = new ArrayList<>();
trades.add(new Trade("T101", "Paul", 5000, "USD", APAC));
trades.add(new Trade("T102", "Mr Bean", 3580, "SGD", NA));
trades.add(new Trade("T103", "Simond", 2300, "CAD", EMEA))

trades.stream().map(Trade::getTradeId).collect(Collectors.toList());

Output: [T101, T102, T103]

 

 

List<String> words =
        Files.lines(Paths.get("flatmap.txt"))     // Stream<String>
        .map(line -> line.split(" "))             // Stream<String[]>
        .map(Arrays::stream)                      // Stream<Stream<String>>
        .distinct()
        .collect(Collectors.toList());

System.out.println(words);

 

Matching

  • anyMatch: Returns true if any element found matching with the predicate.Predicate will not be applied to other elements if any matching found.
  • allMatch: Returns true if all elements are matching to the given predicate.
  • noneMatch: Returns true if none of the elements are matching to the predicate.
Stream.of(5, 10, 15, 20).anyMatch(i -> i % 10 == 0);
Stream.of(5, 10, 15, 20).allMatch(i -> i % 5 == 0);
Stream.of(5, 10, 15, 20).noneMatch(i -> i % 3 == 0);
 
 

Finding element

Signature:

Optional<T> findFirst()
Optional<T> findAny()
Stream.of(5, 10, 15).filter(i -> i % 20 == 0).findAny().orElse(0);
Stream.of(5, 10, 15).map(i -> i * 2).findFirst().get();

 

Optional 을 사용하는 이유는 stream이 비어있을 수 있기 때문에 사용한다

 
 

Stream Reduction

 

Arrays.stream(arr).reduce(0, Integer::sum)

Arrays.stream(arr).reduce(1, (i1, i2) -> i1 * i2)

  • T reduce(T identity, BinaryOperator<T> accumulator) The reduce operation here takes two arguments:
    • identity: The identity element is both the initial value of the reduction and the default result if there are no elements in the stream. In the reduce(0, Integer::sum) example, the identity element is 0; this is the initial value of the sum of the numbers and the default value if no members exist in the array.
    • accumulator: The accumulator function takes two parameters: a partial result of the reduction (in this example, the sum of all processed integers so far) and the next element of the stream (in this example, an integer). It returns a new partial result. In this example, the accumulator function is a lambda expression that adds two Integer values and returns an Integer value:
  • Optional<T> reduce(BinaryOperator<T> accumulator)

    Sequential reduction

  • This is almost equivalent to first reduction method except there is no initial value. Sometime you might be interested to perform some task in case stream has no elements rather than getting a default value. As an example if the reduce returns zero, then we are not sure that the sume is zero or it is the default value. Though there is no default value, its return type is an Optional object indicating result might be missing. You can use Optional.isPresent() to check presense of result.
  • U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner) In first two reduction operations your stream element type and return type were same means before using the reduce method you should convert your elements of type T to type U. But there is an 3 arguments reduce method which facilitates to pass elements of any type. So here accumulator accepts previous partial calculated result and element of type T and return type U result. Below example shows the usage of all three reduction operations.We saw the sample use of these reduction methods so let’s explore more on this 3-argument reduction operation. 
    // Find the number of characters in a string.
    List<String> words = Arrays
             .asList("This is stream reduction example learn well".split(" "));
    int result = words.stream().map(String::length).reduce(0, Integer::sum);
    Optional<Integer> opt = words.stream().map(String::length).reduce(Integer::sum);
    result = words.stream().reduce(0, (i, str) -> i + str.length(), Integer::sum);
    
  • public static void reduceThreeArgs(List<String> words) { int result = words.stream().reduce(0, (p, str) -> { System.out.println("BiFunc: " + p + " " + str); return p + str.length(); }, (i, j) -> { System.out.println("BiOpr: " + i + " " + j); return i + j; }); } output: BiFunc: 0 This BiFunc: 4 is BiFunc: 6 stream BiFunc: 12 reduction BiFunc: 21 example BiFunc: 28 learn BiFunc: 33 well

 

 

 

 

 

To Array

Stream interface supports two overloaded toArray methods that will collect stream elements as an array.

  • Object[] toArray():This is the simplest form of toArray operation which returns an Object array of length equal to Stream length.
  • Example: Integer[] arr = Stream.<Integer>of(10, 20, 30, 40, 50).toArray();
  • T[] toArray(IntFunction<T[]> generator):You saw the first toArray method always returns array of Object type, but this overloaded method will return array of desired type. It accepts an IntFunction as argument that describes the behaviour of taking array length as input and returns the array of generic type.
Employee[] arr = employees.stream().filter(e -> e.getGender() == MALE)
                       .toArray(Employee[]::new);

                                     OR

                 employees.stream().filter(e -> e.getGender() == MALE)
                       .toArray(len -> new Employee[]);
 

 

 

Infinite Streams

We already discussed, Streams can be derived from different sources:

  • From array - Arrays.stream(T[])
  • From known elements - Stream<String>.of(“Stream”, “is”, “great”)
  • From file - Files.lines(Paths.get(“myfile.txt”))

Please visit the Stream sources section for basics of stream sources. The streams generated from above sources are bounded streams where elements size is known. Stream interface supports two static methods Stream.iterate() and Stream.generate which returns infinitite streams that will produce unbounded stream. As generated stream will be unbounded , it’s necessary to call limit(n) to convert stream into bounded.

Note

You can use findAny or findFirst terminal operations to terminate the stream if you assure required result is exist in the stream. Example: Stream.<Integer>iterate(1, v -> v + 3).filter(i -> i % 5 == 0).findAny().get()) Here we are sure that there will be an element which will be divisible by 5 so you can use findAny to terminate the stream.

  • Stream.iterate:: Signature: Stream<T> iterate(T seed, UnaryOperator<T> f)
  • It returns an infinite sequential ordered Stream produced by iterative application of the given function. The function here is a UnaryOperator which uses the previous calculated result to produce next result. It also accepts a seed value that will be supplied to the UnaryOperator as initial value.
// Generating fibonacci numbers of a given length
Stream.iterate(new int[] { 0, 1 }, a -> {
    int next = a[0] + a[1];
    a[0] = a[1];
    a[1] = next;
    return a;
}).limit(10).map(a -> a[0]).forEach(System.out::println);

Output: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
  • Stream.generate:: Signature: Stream<T> generate(Supplier<T> s)Stream.generate(UUID::randomUUID).limit(5).forEach(System.out::println)
  • It returns an infinite sequential unordered stream where each element is generated by the provided Supplier. As we know Supplier doesn’t accept any argument so the generator doesn’t depend on previously calculated value. Below example generates UUID values of a given length.
728x90

'Web > JAVA' 카테고리의 다른 글

[Java] Interface Supplier<T>  (0) 2023.03.22
[Java] this  (0) 2023.03.22
[IntelliJ Javadocs] Javadocs 문서 만들기  (0) 2023.03.18
[Java] try-with-resources  (0) 2023.02.25
[JAVA 8↑] lambda expression  (0) 2023.02.20