본문 바로가기

개발 이야기

JAVA | Stream 에 대해 궁금점 정리

320x100

Java 8에 추가된 스트림(Stream) API는 데이터를 더 선언적, 간결하고 가독성 있는 방식으로 처리할 수 있게 도와줍니다. 이는 대량의 데이터를 효율적으로 처리하는 데 유용하며, 병렬처리도 지원하여 성능 향상을 도모할 수 있습니다.

스트림 API 사용의 주요 이점은 다음과 같습니다:

 

  1. 읽기 쉬운 코드: 스트림은 함수형 프로그래밍 패러다임을 따르므로, 코드가 더욱 읽기 쉽고 이해하기 쉬워집니다.
  2. 효율적인 데이터 처리: 스트림은 '게으른 연산'을 지원합니다. 즉, 필요한 시점에서만 연산이 수행되므로 메모리 사용량과 계산 비용을 절약할 수 있습니다.
  3. 병렬처리 가능: 스트림은 병렬처리를 지원하여 멀티코어 아키텍처에서 성능 향상을 얻을 수 있습니다.

 

아래에는 Java 8 이전의 방식과 스트림 API를 사용한 방식의 차이점을 보여주는 예제 코드를 제공하겠습니다.

 

Java 8 이전:

List<String> names = new ArrayList<>();
for (User user : users) {
    if (user.getAge() > 18) {
        names.add(user.getName());
    }
}

위 코드는 모든 사용자들 중에서 나이가 18세 초과인 사람들의 이름만 리스트에 추가하는 작업입니다.

J

 

ava 8 Stream API 사용:

List<String> names = users.stream()
    .filter(user -> user.getAge() > 18)
    .map(User::getName)
    .collect(Collectors.toList());

 

위 코드도 동일한 작업을 수행하지만, filter, map, collect와 같은 함수형 메서드를 활용하여 간결하고 가독성 있는 형태로 작성되었습니다.

 

이 예제에서 볼 수 있듯이, 스트림 API는 각 요소에 대해 순차적으로 연산(filtering, mapping 등)을 적용하며 결과 컬렉션(list of names)을 생성합니다. 이러한 방식은 읽기 쉽고 유지보수하기 편리하며, 병렬 처리를 쉽게 적용할 수 있다는 장점이 있습니다.

 

여기서 기존 자료들은 자기들만 아는 걸 클래스 몇줄 적어주고 끝내서 오히려 초보들은 그런가 하는 부분들이 너무 신경써여서 다시 확인해 봤다.

 

각 기본 function 들에 대한 내용을 정리해봤다.

 

Java 8의 Stream API는 데이터 처리를 위한 다양한 메서드를 제공합니다. 여기서는 filter(), map(), 그리고 collect()에 대해 설명하겠습니다.

  1. filter(Predicate predicate): 스트림에서 특정 조건을 만족하는 요소만 선택하여 새로운 스트림을 생성합니다. 이때 사용되는 Predicate 인터페이스는 매개변수를 받아 boolean 값을 반환하는 함수형 인터페이스입니다.
  2. map(Function mapper): 각 요소를 특정 함수를 통해 변환하여 새로운 스트림을 생성합니다. 이때 사용되는 Function 인터페이스는 한 개의 매개변수를 받아 결과값을 반환하는 함수형 인터페이스입니다.
  3. collect(Collector collector): 스트림의 모든 요소를 수집하여 컬렉션 또는 다른 형태의 값으로 변환합니다. 이때 사용되는 Collector 인터페이스는 스트림 요소들의 누적(accumulation) 방법을 정의합니다.

이러한 메서드들은 연속된 연산 파이프라인을 구성하여 복잡한 데이터 처리 작업도 간결하게 표현할 수 있게 도와줍니다.

그리고 Stream API에서 병렬처리(parallel processing)은 매우 간단하게 적용할 수 있습니다. 기본적으로 stream() 메서드 대신 parallelStream() 메서드를 호출하면 됩니다:

List<String> names = users.parallelStream()
    .filter(user -> user.getAge() > 18)
    .map(User::getName)
    .collect(Collectors.toList());

위 코드에서 parallelStream()은 멀티코어 프로세서 환경에서 병렬로 작업을 수행할 수 있도록 도와줍니다.

하지만 주의해야 할 점은, 모든 상황에서 병렬처리가 무조건적으로 성능 향상을 가져오지 않습니다. 데이터 크기가 작거나, 작업이 CPU보다 I/O에 의존적인 경우에는 오히려 성능이 저하될 수 있습니다. 따라서 병렬처리 적용 전 후 성능 비교 및 분석이 필요합니다.

 

 

최종적으로 기존 개발자분들도 지금껏 이야기한 내용을 편하게 확인할 수 있게 전체코드를 작성했습니다.아래 코드 보시면 기본적인 사용법 확인 가능할 겁니다.

 

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        List<User> users = Arrays.asList(
            new User("John", 17),
            new User("Jane", 20),
            new User("Tom", 22),
            new User("Alice", 15)
        );

        List<String> names = users.stream()
                .filter(user -> user.getAge() > 18)
                .map(User::getName)
                .collect(Collectors.toList());

        System.out.println(names);
    }
}

class User {
    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

위의 코드에서 main 메소드는 여러 User 객체를 포함하는 리스트를 생성하고, 이 리스트에서 나이가 18세 초과인 사용자들의 이름만을 선택하여 새로운 리스트(names)를 만듭니다. 그리고 이 names 리스트를 출력합니다.

 

이렇게 스트림 API와 람다 표현식을 활용하면 데이터 처리 로직을 간결하고 선언적으로 표현할 수 있습니다.

반응형