Java

Java Stream

728x90

스트림 처리

스트림이란?

  • 한번에 한개씩 만들어지는 연속적인 데이터 항목들의 모임이다.

프로그램은 입력 스트림에서 데이터를 한개씩 읽어 들이며 마찬가지로 출력 스트림으로 데이터를 한개씩 기록한다. 즉 어떤 프로그램의 출력스트림은 입력스트림이 될 수 있다.

유닉스와 리눅스의 많은 프로그램은 표준입력에서 데이터를 읽은 뒤에, 데이터를 처리하고 결과를 표준 출력으로 기록한다. 예를 들면 아래와 같다.

cat file file2 | tr "[A-Z] "[a-z]" | sort | tail -3

위의 예제는 파일의 단어를 소문자로 바꾼 다음에 사전순으로 단어를 정렬했을 때 가장 마지막에 위치한 세 단어를 출력하는 프로그램이다. 해당 명령어가 실행되는 과정은 아래와 같다.

image

각각의 명령어는 자신의 임무를 수행하고 출력 스트림 -> 입력 스트림 형태로 파이프라인을 만든다.

Java 8 부터는 java.util.stream 패키지에 Stream API 가 추가되었다.

위에서 파이프라인을 구축했던 것처럼 자바에서도 Stream 을 통해 파이프라인을 만드는 메서드를 제공한다.

동작 파라미터화로 메서드에 코드 전달하기

자바8 에 추가된 개념은 코드 일부를 API로 전달하는 기능이다.

예를 들면 2021UK001, 2014US002, ... 등의 형식을 갖는 송장 ID 가 있고

이를 순서대로 정렬하고 싶다면 우리는 sort 에 우리가 지정하는 순서대로 자료를 정리하도록 명령을 내려야 한다.

이때 두 송장 ID 를 비교하는 함수인 compareUsingCustomerId 와 같은 함수를 인자로 전달할 수 있게 된다.

간략하게 의사코드로 아래와 같다고 생각하면 된다.

.sort(compareUsingCustomerId)

이러한 기능을 이론적으로 동작 파라미터화라고 한다.

즉 우리는 어떠한 행위를 이제 파라미터로 넘길 수 있게 되는것이다.

자바 함수

프로그래밍 언어에서 함수라는 뜻은 메서드 특히 정적메서드라는 의미로 많이 표현된다.

함수형 프로그래밍에서 함수는 수학적인 함수처럼 이용되며 부작용을 일으키지 않는 함수를 의미한다.

자바8 에서는 함수를 새로운 값으로 추가했다. 즉 함수가 1급 시민이 된것이다.

그렇기에 아까 동적 파라미터화가 가능한 것이다.

메서드와 람다를 1급 시미으로 만들기 위해 자바8 에 새롭게 생긴 기능은 메서드 참조(method Reference) 라는 기능이다. 예를 들면 아래와 같은 코드는 자바 8에 와서 변할 수 있다.

File[] hiddenFiles = new File(".").listFiles(new FileFilter() {
    public boolean accept(File file) {
        return file.isHidden(); // 숨겨진 파일 필터링
    }
})

자바 8이상을 써왔다면 "아 이렇게까지 코드를 작성해야해?" 라고 말할 수 있을 것이다.

자바8 에서는 어떻게 바뀔까?

아래와 같이 변할 것이다.

File[] hiddenFiles = new File(".").listFiles(File::isHidden);

isHidden 이라는 메소드는 준비되어 있으므로 '메서드참조 ::' 을 이용해서 메서드를 값으로 이용하여 직접 listFiles 에 전달할 수 있게 되었다.

람다(lambda)

자바8 에서는 메서드를 일급값으로 취급할 뿐만 아니라 람다를 포함하여 함수도 값으로 취급할 수 있다.

예를들어 사과 박스에서 초록색인사과를 걸러 내는 과정을 아래와 같을것이다.

filterApples(inventory, (Apple a) -> GREEN.equals(a.getColor()));

익명함수(람다) 를 통해서 위와 같이 간단하게 적을 수 있다.

멀티 스레딩은 어렵다

Java Stream 은 멀티스레딩의 어려움을 어느정도 극복해줄 수 있도록 도와주었는데, 예를들면 주어진 조건에 따라 데이터를 필터링하거나, 데이터를 추출하거나 그룹화하는 기능들은 아래와 같이 서로 다른 CPU로 처리될 수 있게 만들어 졌습니다.

image

예를 들면 5개의 리스트에서 CPU 1이 앞부분을 CPU2 가 뒷부분을 처리하도록 요청하게 되는데 이 과정을 포킹 단계(forking step) 라고 합니다. 그리고 결과물들이 나오게 되면 하나로 합쳐서 클라이언트가 원하는 결과물을 도출하게 됩니다.

즉 스트림은 어떻게 계산할지가 조금 더 중점적으로 동작하므로 좀 더 쉽게 병렬로 처리 될 수 있는 환경을 제공한다는 것이 핵심입니다.

더 쉽게 얘기하면 컬렉션을 가장 빠르게 필터링할 수 있는 방법은 스트림을 쓴 후에 병렬처리하고 리스트로 다시 복원하는 것입니다.

'Java' 카테고리의 다른 글

객체지향) 객체의 역할, 책임, 협력  (2) 2021.11.24
JPA PersistContext  (0) 2021.11.22
젠킨스 JVM 메모리 설정  (0) 2021.10.20
객체는 무엇일까?  (0) 2021.10.14
Switch 문 반복을 없애기  (0) 2021.10.07