Python의 GIL(Global Interpreter Lock)과 멀티스레딩의 한계

Java 8에서 도입된 스트림 API(Stream API)는 컬렉션과 배열 같은 데이터 소스를 간결하고 효율적으로 처리하기 위한 강력한 도구입니다. 스트림 API는 함수형 프로그래밍 스타일을 적용하여 복잡한 데이터 처리 작업을 단순화하고, 코드의 가독성과 유지보수성을 높입니다. 이 글에서는 Java의 스트림 API를 활용한 효율적인 데이터 처리 기법을 살펴보겠습니다.
스트림 API는 데이터의 흐름을 추상화한 개념으로, 데이터를 필터링, 변환, 집계하는 일련의 작업을 수행할 수 있습니다. 스트림은 데이터 요소의 연속적인 시퀀스로 간주되며, 이를 통해 데이터를 효율적으로 처리할 수 있습니다.
스트림은 데이터 소스(예: 컬렉션, 배열, I/O 채널)에서 생성됩니다.
예시:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
Stream<String> nameStream = names.stream();
중간 연산은 스트림을 변환하거나 필터링하는 작업을 수행하며, 항상 새로운 스트림을 반환합니다. 이 연산들은 지연 연산(lazy)으로 수행됩니다.
예시:
Stream<String> filteredStream = nameStream.filter(name -> name.startsWith("A"));
최종 연산은 스트림을 처리하고, 결과를 생성합니다. 이 연산이 수행되면 스트림은 더 이상 사용되지 않습니다.
예시:
List<String> result = filteredStream.collect(Collectors.toList());
데이터 소스에서 조건에 맞는 요소만을 추출합니다.
예시:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
각 요소를 다른 형식으로 변환하거나 매핑합니다. map
연산은 각 요소에 함수형 인터페이스를 적용하여 변환된 요소들로 이루어진 새로운 스트림을 반환합니다.
예시:
List<String> words = Arrays.asList("apple", "banana", "cherry");
List<Integer> wordLengths = words.stream()
.map(String::length)
.collect(Collectors.toList());
스트림의 모든 요소를 하나의 결과로 결합합니다. 대표적인 집계 연산으로는 reduce
, count
, sum
등이 있습니다.
예시:
int sum = numbers.stream()
.reduce(0, Integer::sum);
스트림의 요소를 정렬하여 새로운 스트림을 생성합니다.
예시:
List<String> sortedNames = names.stream()
.sorted()
.collect(Collectors.toList());
스트림을 병렬로 처리하여 성능을 향상시킬 수 있습니다. 병렬 스트림은 내부적으로 여러 쓰레드를 사용하여 작업을 병렬로 수행합니다.
예시:
List<Integer> largeNumbers = numbers.parallelStream()
.filter(n -> n > 100)
.collect(Collectors.toList());
forEach
와 같은 상태 유지 연산을 사용할 때는 병렬 처리에서 예상치 못한 결과가 발생할 수 있으므로 주의해야 합니다.Java의 스트림 API는 복잡한 데이터 처리 작업을 간결하고 효율적으로 구현할 수 있는 강력한 도구입니다. 스트림 API를 적절히 활용하면 코드의 가독성을 높이고 성능을 최적화할 수 있으며, 대규모 데이터 처리 작업을 쉽게 구현할 수 있습니다. 스트림 API를 통해 데이터 처리의 유연성과 효율성을 극대화함으로써, Java 애플리케이션의 품질을 한층 더 향상시킬 수 있습니다.