[JAVA 문법] Stream 파보기

su_y2on·2022년 3월 28일
0

JAVA

목록 보기
12/14
post-thumbnail

Stream vs Collections



1. conceptual difference

먼저 Stream과 Collections는 컨셉자체가 다릅니다. Collections는 데이터를 모아서 그룹으로 저장하기 위한 자료구조입니다. 예를들면 List, Map등이 있습니다.

이와 다르게 Stream은 이런 그룹으로 저장된 데이터를 다루기 위한 것입니다. 예를들면 List정렬과 출력등이 이에 해당합니다.



2. Data 수정

또한 Collections는 데이터를 수정하거나 삭제할 수 있지만 Stream은 넘겨받은 데이터모음을 다룰 뿐 그 원본을 바꿀 수는 없습니다. 그리고 애초에 그런 내부적인 함수가 존재하지도 않습니다



3. External Iteration Vs Internal Iteration

Collections는 반복을 외부적(loops)으로 드러나게 하지만 Stream은 내부적으로 반복이 숨어있습니다. 아래의 예제를 보면 더 이해가 쉬울 것입니다

List<String> names = new ArrayList<>();
                 
names.add("Charlie");
                 
names.add("Douglas");
                 
names.add("Sundaraman");
                 
//External iteration of collections
         
for (String name : names) 
{
    System.out.println(name);
}
         
//Output :
         
//Charlie
//Douglas
//Sundaraman

         
//Internal iteration of streams. No for loops
 
names.stream().map(String::toUpperCase).forEach(System.out::println);
                 
//Output :
                 
//CHARLIE
//DOUGLAS
//SUNDARAMAN



4. Traversal(순회)

Collections는 순회를 여러번 할 수 있지만 Stream은 한번 순회가 끝나면(terminate) 그 뒤로 순회는 불가능합니다. 이를 Consumed(소비)되었다고 표현합니다. 순회를 다시 하고 싶다면 Stream을 다시 생성해야합니다

List<Integer> numbers = Arrays.asList(4, 2, 8, 9, 5, 6, 7);
         
Stream<Integer> numbersGreaterThan5 = numbers.stream().filter(i -> i > 5);
         
//Traversing numbersGreaterThan5 stream first time
         
numbersGreaterThan5.forEach(System.out::println);
         
//Second time traversal will throw error
         
//Error : stream has already been operated upon or closed
         
numbersGreaterThan5.forEach(System.out::println);



5. Eager Construction Vs Lazy Construction

마지막으로 Collection은 바로 생성되는 반면에 Stream은 필요시에 생성되며 terminate operation을 만나기 전까지 연산을 하지 않습니다.





terminate vs intermediate

Stream의 operation은 Stream을 끝내는 terminate operation과 나머지인 intermediate operation으로 나뉩니다. 이는 각각 operation의 정의를 까보면 확인할 수 있습니다.

  • terminate : toArray(), forEach(), max(), min() ...
  • intermediate : limit(), sorted(), peek()...





stateful vs stateless

Stream이 어떻게 진행되는지 아래의 코드를 통해 확인해보겠습니다.

List<String> list = new ArrayList<>(Arrays.asList("a","ab","abc","abcd"));

Stream<Integer> stream = list.stream()
                .map(i -> {
                    System.out.println("map " + i);
                    return i.length();
                })
                .peek(i -> System.out.println("check" + i))
                .filter(i -> {
                    System.out.println("filter " + i);
                    return i % 2 == 0;
                })
                .sorted()
                .peek(i -> System.out.println("sorted" + i));

System.out.println("Invoking terminal method count.");
System.out.println(stream.count());

/* output
Invoking terminal method count.
map a
check1
filter 1
map ab
check2
filter 2
map abc
check3
filter 3
map abcd
check4
filter 4
sorted2
sorted4
2
*/

먼저 terminate operation인 count를 만나서야 연산이 진행되는 것으로 lazy를 확인할 수 있습니다. 그리고 순서대로 operation을 통과하는 것을 확인할 수 있습니다.

근데 이상한것이 다른 operation과는 다르게 sorted는 위에 모든 operation이 진행되고나서 한번에 모였다가 다시 순서대로 진행을 합니다.

그 이유은 sorted는 정렬이기 때문에 하나씩 진행을 못하고 다 모일 때 까지 기다리는 것입니다. 이것 또한 직접 정의 코드를 까보면 다 분류가 되어있습니다.

1개의 댓글

comment-user-thumbnail
2022년 3월 31일

헐... 정리 엄청 깔끔해요 👍
저도 담에 포스팅 할 때 이렇게 해봐야겠어요 ㅋㅋ
다음 포스팅 기대하겠습니다 😎

답글 달기