Java8 에서는 기존의 Java 에 비해 다양한 기능들이 접목되었다.
Optional 과 Lambda, Stream API 와 같은 기능들이 그것인데, 이는 새로이 대세로 떠오르고 있는 함수형 프로그래밍의 메타를 적용한 Java 의 새로운 진화라 할 수 있겠다.
Java8 에 대한 전반적인 소개는 다음 링크를 참조하자.
(http://jins-dev.tistory.com/entry/Java8-%EC%97%90%EC%84%9C-%EC%83%88%EB%A1%9C-%EC%83%9D%EA%B2%A8%EB%82%9C-API-%EB%93%A4?category=760006)
본 포스팅에서 집중해볼 것은 실무에서도 이제 널리 사용되고 있는 Stream API 에 대한 내용과 간단한 사용방법들이다.
대부분의 이전 Java는 데이터를 처리하기 위해 컬렉션을 사용하고 이는 전통적인 반복문과 조건문 사용에 기인한다.
컬렉션을 사용하여 할 수 있는 기능은 무한하고 강력하지만, 정작 데이터를 처리하는 패턴은 SQL에서 데이터를 찾는것과 비슷하게 데이터를 찾거나, 묶는것 정도에 국한된다.
Java8의 Stream API 는 컬렉션을 이용했던 기존의 코드를 좀 더 깔끔하게 구현하고 병렬처리에 있어서도 이점을 가질 수 있도록 하기 위하여 제공된다.
Stream API의 기본이 되는 원형 stream() 메서드는 모든 컬렉션 타입에 대해 제공되며, 컬렉션 내의 Element 들에 대해 하나씩 구분한 Stream 결과를 나타낸다.
먼저 Stream API 가 어떻게 구성되는지 알아보자.
Stream API 는 생성연산, 중간연산, 최종연산으로 Flow 가 구성되어 있다. 생성연산은 스트림의 생성을, 중간 연산은 스트림을 통한 데이터 가공 및 변환을 담당하고 최종 연산은 Stream 의 사용을 담당한다. 다음은 연산별로 Stream API 를 정리한 내용이다.
순서대로 스트림 생성연산, 중간연산, 최종연산을 분류하였다.
Stream API 를 사용하기 위해서는 반드시 위의 Pipeline 을 순서대로 따라야 하며, 여러개의 중간연산을 붙이는 것도 가능하다. 다만, 생성연산과 중간연산들은 Stream 을 반환해야만 추가 연산이 가능하다는 점을 잊지 말자.
이를 이용해서 다음 리스트에서 원하는 정보를 추출해보자.
Stream API 를 이용하면 위의 리스트에서 70 이상의 숫자들을 정렬해서 다음과 같이 추출해낼 수 있다.
결과도 다음과 같이 확인해볼 수 있다.
코드 역시 상당히 직관적으로 이해할 수 있다. 코드상에서 우리는 numList 의 Stream 을 생성하여, 70 이상인 요소들만 필터링하여 정렬 후 Array 형태로 반환하고 있다.
이처럼 Stream API 를 사용하면 만들어진 스트림을 별도의 저장이나 가공없이 filter(속성에 맞게 필터링), sorted(정렬), map(정보를 추출), collect(정보를 가공) 할 수 있으며 이 메서드들은 collect에 의해 연속되어 처리된 결과를 리턴하거나 형변환, 또는 Stream 연산의 결과를 취합하여 반환한다.
Stream API 를 사용할 때 꼭 알아두어야 할 점은 Stream은 요소들을 보관하지 않으며 요소들은 하부의 컬렉션에 보관되거나 필요할 때 생성된다는 점이다. 또한 원본을 변경하지 않는 대신 결과를 담은 새로운 스트림을 반환한다.
스트림 연산은 가능하면 지연(Lazy) 처리 되기 때문에 결과가 필요하기 전에는(최종 연산 이전) 실행되지 않는다.
따라서 최종연산 이전의 연산의 순서는 보장할 수 없다. (가령, Stream 연산에 2개 이상의 sorted 가 있을 경우 어떤 sorted 가 먼저 수행될지는 불분명하다.)
이 말을 다시한번 정리해보자. 스트림은 최대한 연산을 지연하며, 그에 따라 최종연산에 오기 전까지는 연산이 실제로 수행되지 않는다는 것이다.
이처럼 Stream 은 각 요소 단위로 연산이 수행되는데 지연(Lazy) 처리 특성에 따라 최종연산에서 한꺼번에 처리가 되므로 최종연산이 선언되지 않은 체인 스트림에서 동작을 수행할 경우 이는 반영되지 않는다. 즉, 다음과 같은 코드는 아무 동작도 하지 않는다.
위의 연산은 peek 이라는 중개 연산을 마지막으로 구문을 마치고 있기 때문에 결과적으로 Stream 연산이 수행되지 않는다. 따라서 출력도 되지 않는다.
이러한 Stream의 특성을 Lazy & ShortCircuit 이라 하며 이는 Stream 이 Collection 과 다르게 그 자체만으로 자료구조가 아닌 연산을 위한 자료구조인 특성을 반영한다
Stream 을 이용하면 가독성이 뛰어나고, 잘 사용하면 성능적으로도 뛰어난 결과를 가져올 수 있으며 쉽게 병렬 처리 환경으로 이식도 가능하다.
Stream 의 응용은 무궁무진하며 사용하기 위해서는 익숙해지는 것이 중요하다. 많이 사용해보고 적절하게 응용해보도록 하자.
'Programming Language > Java' 카테고리의 다른 글
Java9 의 Feature 들에 대한 정리 (0) | 2018.11.07 |
---|---|
Java 클래스로더(ClassLoader)에 대한 이해 (0) | 2018.10.14 |
가비지 컬렉터(Garbage Collector) 의 개념과 동작 원리 (0) | 2018.08.15 |
Java 의 Final Object에 대하여 (0) | 2018.08.12 |
Java8 에서 새로 생겨난 API 들 (0) | 2018.08.09 |