본문 바로가기
Java/Java이론

Java Stream을 사용한 리스트 처리 예제

by P_eli 2024. 10. 23.
728x90
반응형

Java 8의 등장과 함께 소개된 Stream API는 컬렉션 데이터를 효율적으로 처리하고 변환할 수 있는 강력한 도구입니다. Stream을 사용하면 코드가 간결해지며, 컬렉션 데이터를 반복 처리할 때 발생할 수 있는 오류를 줄이고, 함수형 프로그래밍의 장점을 살릴 수 있습니다.

이번 글에서는 Stream API를 활용한 리스트 필터링, 매핑, 정렬 등의 간단한 예제와 함께 병렬 스트림을 사용하여 성능을 개선할 수 있는 방법을 소개하겠습니다.

 

1. Stream API의 기본 개념

Stream은 데이터의 흐름을 추상화한 개념으로, 데이터를 필터링하거나 변환할 때 각 요소에 대해 선언적으로 정의할 수 있게 해줍니다. Stream은 **데이터 소스(컬렉션, 배열 등)**에서 시작하여, 다양한 중간 연산(filter, map, sorted 등)을 거쳐 최종적으로 종단 연산(collect, forEach 등)을 수행합니다.

2. 기본 예제

2.1 리스트 필터링 (Filtering)

리스트에서 특정 조건에 맞는 요소들만 걸러내는 작업입니다.

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

public class StreamExample {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David", "Edward");

        // 이름이 'C'로 시작하는 요소들 필터링
        List<String> filteredNames = names.stream()
            .filter(name -> name.startsWith("C"))
            .collect(Collectors.toList());

        System.out.println(filteredNames); // [Charlie]
    }
}

위 코드에서는 filter() 메서드를 사용해 이름이 'C'로 시작하는 요소들만 리스트로 반환합니다. 조건이 명시적이어서 코드의 가독성이 좋아집니다.

2.2 리스트 매핑 (Mapping)

매핑은 리스트의 요소를 특정 로직에 따라 변환하는 작업입니다.

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

public class StreamExample {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

        // 모든 이름을 대문자로 변환
        List<String> upperCaseNames = names.stream()
            .map(String::toUpperCase)
            .collect(Collectors.toList());

        System.out.println(upperCaseNames); // [ALICE, BOB, CHARLIE]
    }
}

map() 메서드는 스트림의 각 요소를 변환하는 데 사용됩니다. 여기서는 String::toUpperCase를 사용해 모든 이름을 대문자로 변환했습니다.

2.3 리스트 정렬 (Sorting)

스트림을 정렬하여 처리할 수 있습니다. 기본적으로 오름차순 정렬이 제공되며, 사용자 정의 Comparator를 사용할 수도 있습니다.

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

public class StreamExample {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Charlie", "Alice", "Bob");

        // 이름을 알파벳 순으로 정렬
        List<String> sortedNames = names.stream()
            .sorted()
            .collect(Collectors.toList());

        System.out.println(sortedNames); // [Alice, Bob, Charlie]
    }
}

sorted() 메서드는 스트림의 요소를 정렬합니다. 기본적으로 오름차순 정렬이지만, Comparator를 사용하여 사용자 정의 정렬도 가능합니다.

 

3. 병렬 스트림 (Parallel Stream)으로 성능 개선

대용량 데이터를 처리할 때, 스트림은 병렬 처리를 지원하여 성능을 크게 향상시킬 수 있습니다. **parallelStream()**을 사용하면 컬렉션의 데이터를 여러 스레드에서 동시에 처리하여 더 빠르게 결과를 도출할 수 있습니다.

병렬 스트림 예제

import java.util.Arrays;
import java.util.List;

public class ParallelStreamExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        // 병렬 스트림을 사용하여 각 요소에 대해 연산 수행
        int sum = numbers.parallelStream()
            .mapToInt(n -> n * 2)
            .sum();

        System.out.println("Sum of doubled numbers: " + sum); // 110
    }
}

위 코드에서는 parallelStream()을 사용하여 리스트의 각 요소를 병렬로 처리하고, mapToInt()로 두 배로 만든 후 sum()을 통해 합계를 구했습니다.

병렬 스트림의 성능 개선 효과

병렬 스트림을 사용하면 다중 코어를 효과적으로 활용할 수 있으므로 대용량 데이터 처리 시 성능이 크게 향상됩니다. 특히 CPU 집약적인 작업이나 I/O를 많이 사용하는 작업에 적합합니다. 하지만 모든 경우에 병렬 스트림이 성능을 개선하는 것은 아닙니다.

  • 작은 데이터에 대해서는 오히려 스레드 관리 비용이 커질 수 있어 병렬 스트림이 적합하지 않습니다.
  • 병렬 스트림 사용 시 스레드 안전성을 보장해야 하며, 공유 자원에 대한 동기화 문제가 발생할 수 있습니다.

4. 결론

Java 8 이후 도입된 Stream API는 컬렉션 데이터를 간결하고 직관적으로 처리할 수 있는 강력한 기능입니다. 필터링, 매핑, 정렬 등의 연산을 손쉽게 수행할 수 있으며, 병렬 스트림을 사용하여 성능을 개선할 수도 있습니다.

Stream을 적절히 활용하면 가독성이 높아질 뿐만 아니라, 함수형 프로그래밍의 장점을 살려 더욱 효율적인 프로그램을 작성할 수 있습니다. 다만 병렬 스트림의 경우 모든 상황에서 성능이 향상되지는 않으므로, 적절한 상황에서만 사용하는 것이 중요합니다.

728x90
반응형