λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°
μΉ΄ν…Œκ³ λ¦¬ μ—†μŒ

μžλ°” 슀트림(Stream) μ‚¬μš©λ²• & 예제

by 5566 2023. 10. 18.

1. μžλ°” 슀트림(Stream)μ΄λž€?

μžλ°” 슀트림(Stream)은 μžλ°” 8μ—μ„œ λ„μž…λœ 데이터 처리 및 μ»¬λ ‰μ…˜ μ‘°μž‘μ„ μœ„ν•œ μƒˆλ‘œμš΄ κ°œλ…μž…λ‹ˆλ‹€. μŠ€νŠΈλ¦Όμ€ λ°μ΄ν„°μ˜ μ—°μ†λœ 흐름을 ν‘œν˜„ν•˜λŠ” κ°œλ…μœΌλ‘œ, λ°°μ—΄μ΄λ‚˜ μ»¬λ ‰μ…˜ λ“±μ˜ 데이터 μ†ŒμŠ€μ—μ„œ λžŒλ‹€μ‹μ„ μ΄μš©ν•˜μ—¬ κ°„κ²°ν•˜κ³  효과적으둜 데이터λ₯Ό μ²˜λ¦¬ν•  수 있게 ν•΄μ€λ‹ˆλ‹€.

μŠ€νŠΈλ¦Όμ€ λ‚΄λΆ€μ μœΌλ‘œ 데이터λ₯Ό 순차적으둜 μ²˜λ¦¬ν•˜λ©°, 병렬 처리λ₯Ό μœ„ν•œ κΈ°λŠ₯도 μ œκ³΅ν•©λ‹ˆλ‹€. λ°μ΄ν„°μ˜ μ€‘κ°„μ²˜λ¦¬ 및 μ΅œμ’…μ²˜λ¦¬λ₯Ό μœ„ν•œ λ‹€μ–‘ν•œ λ©”μ„œλ“œλ₯Ό μ œκ³΅ν•˜μ—¬ 데이터λ₯Ό ν•„ν„°λ§ν•˜κ±°λ‚˜ λ§€ν•‘ν•˜λŠ” λ“±μ˜ λ‹€μ–‘ν•œ μž‘μ—…μ„ μˆ˜ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

μŠ€νŠΈλ¦Όμ€ ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°μ˜ κ°œλ…μ„ λ„μž…ν•˜μ—¬ μ½”λ“œμ˜ 가독성과 μœ μ§€λ³΄μˆ˜μ„±μ„ ν–₯μƒμ‹œμΌœμ€λ‹ˆλ‹€. λ˜ν•œ, μ§€μ—°λœ 연산을 μˆ˜ν–‰ν•˜μ—¬ 효율적인 λ©”λͺ¨λ¦¬ μ‚¬μš©μ„ κ°€λŠ₯ν•˜κ²Œ ν•©λ‹ˆλ‹€.

λ‹€μŒ ν•­λͺ©μ—μ„œλŠ” 슀트림의 μ£Όμš” νŠΉμ§•κ³Ό μ‚¬μš©λ²•μ— λŒ€ν•΄ μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

2. μžλ°” 슀트림(Stream)의 νŠΉμ§•

μžλ°” 슀트림(Stream)의 μ£Όμš” νŠΉμ§•μ€ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€:

  • μ—°μ†λœ 흐름: μŠ€νŠΈλ¦Όμ€ λ°μ΄ν„°μ˜ μ—°μ†λœ 흐름을 ν‘œν˜„ν•©λ‹ˆλ‹€. 데이터λ₯Ό μ—°μ†λœ μš”μ†Œλ“€μ˜ μ‹œν€€μŠ€λ‘œ μ²˜λ¦¬ν•˜λŠ” 것을 κ°€λŠ₯ν•˜κ²Œ ν•΄μ€λ‹ˆλ‹€. μ΄λŠ” λ°˜λ³΅λ¬Έμ„ μ‚¬μš©ν•˜μ§€ μ•Šκ³ λ„ 데이터λ₯Ό 순차적으둜 μ²˜λ¦¬ν•  수 μžˆλ‹€λŠ” μž₯점을 μ œκ³΅ν•©λ‹ˆλ‹€.

  • λ‚΄λΆ€ 반볡: μŠ€νŠΈλ¦Όμ€ λ‚΄λΆ€μ μœΌλ‘œ 데이터λ₯Ό 반볡 μ²˜λ¦¬ν•˜λ―€λ‘œ κ°œλ°œμžκ°€ 직접 λ°˜λ³΅λ¬Έμ„ μž‘μ„±ν•  ν•„μš”κ°€ μ—†μ–΄μ‘ŒμŠ΅λ‹ˆλ‹€. λŒ€μ‹ , λžŒλ‹€μ‹μ„ μ΄μš©ν•˜μ—¬ 처리 과정을 μ„ μ–Έν˜•μœΌλ‘œ ν‘œν˜„ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

  • ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°: μŠ€νŠΈλ¦Όμ€ ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°μ˜ κ°œλ…μ„ λ„μž…ν•˜μ—¬ 가독성과 μœ μ§€λ³΄μˆ˜μ„±μ„ λ†’μ—¬μ€λ‹ˆλ‹€. μŠ€νŠΈλ¦Όμ€ λ©”μ„œλ“œ 체이닝을 톡해 λ‹€μ–‘ν•œ 연산을 μ‘°ν•©ν•  수 있으며, 각각의 연산은 독립적이고 μ‚¬μ΄λ“œ μ΄νŽ™νŠΈκ°€ μ—†λŠ” ν•¨μˆ˜λ‘œ μž‘μ„±λ˜μ–΄μ•Ό ν•©λ‹ˆλ‹€.

  • μ§€μ—°λœ μ—°μ‚°: μŠ€νŠΈλ¦Όμ€ μ΅œμ’… 연산이 호좜될 λ•ŒκΉŒμ§€ 쀑간 연산을 μ§€μ—°ν•˜μ—¬ μ²˜λ¦¬ν•©λ‹ˆλ‹€. μ΄λŠ” ν•„μš”ν•œ λ°μ΄ν„°λ§Œ μ²˜λ¦¬ν•˜κ³ , λΆˆν•„μš”ν•œ 데이터 처리λ₯Ό ν”Όν•  수 μžˆμ–΄ 효율적인 λ©”λͺ¨λ¦¬ μ‚¬μš©μ„ κ°€λŠ₯ν•˜κ²Œ ν•©λ‹ˆλ‹€.

  • 병렬 처리: μŠ€νŠΈλ¦Όμ€ λ‚΄λΆ€μ μœΌλ‘œ 병렬 처리λ₯Ό μ§€μ›ν•©λ‹ˆλ‹€. λ§Žμ€ 데이터λ₯Ό λ™μ‹œμ— μ²˜λ¦¬ν•  수 μžˆλŠ” 병렬 μŠ€νŠΈλ¦Όμ„ μ‚¬μš©ν•˜λ©΄ λ©€ν‹°μ½”μ–΄ ν”„λ‘œμ„Έμ„œμ˜ μ„±λŠ₯을 μ΅œλŒ€ν•œ ν™œμš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

μžλ°” μŠ€νŠΈλ¦Όμ€ μ΄λŸ¬ν•œ νŠΉμ§•λ“€μ„ 톡해 κ°„κ²°ν•˜κ³  효율적인 데이터 처리λ₯Ό κ°€λŠ₯ν•˜κ²Œ ν•΄μ£ΌλŠ” κ°•λ ₯ν•œ λ„κ΅¬μž…λ‹ˆλ‹€. λ‹€μŒ ν•­λͺ©μ—μ„œλŠ” μžλ°” 슀트림의 μ‚¬μš©λ²•μ— λŒ€ν•΄ μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

3. μžλ°” 슀트림(Stream)의 μ‚¬μš©λ²•

μžλ°” 슀트림(Stream)을 μ‚¬μš©ν•˜κΈ° μœ„ν•΄μ„œλŠ” λ‹€μŒκ³Ό 같은 단계λ₯Ό 따라야 ν•©λ‹ˆλ‹€:

  1. 슀트림 생성: μŠ€νŠΈλ¦Όμ„ μƒμ„±ν•˜κΈ° μœ„ν•΄μ„œλŠ” 데이터 μ†ŒμŠ€λ₯Ό 가져와야 ν•©λ‹ˆλ‹€. λ°°μ—΄, μ»¬λ ‰μ…˜, 파일 λ“± λ‹€μ–‘ν•œ 데이터 μ†ŒμŠ€λ₯Ό 슀트림으둜 λ³€ν™˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, ArrayList의 μŠ€νŠΈλ¦Όμ„ μƒμ„±ν•˜λ €λ©΄ stream() λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜λ©΄ λ©λ‹ˆλ‹€.
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream();
  1. 쀑간 μ—°μ‚° μˆ˜ν–‰: μŠ€νŠΈλ¦Όμ„ κ°€κ³΅ν•˜κΈ° μœ„ν•΄ 쀑간 연산을 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 쀑간 연산은 데이터λ₯Ό λ³€ν™˜ν•˜κ±°λ‚˜ ν•„ν„°λ§ν•˜λŠ” μž‘μ—…μ„ μˆ˜ν–‰ν•©λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, filter() λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ 쑰건에 λ§žλŠ” μš”μ†Œλ§Œμ„ κ±ΈλŸ¬λ‚Ό 수 μžˆμŠ΅λ‹ˆλ‹€.
Stream<String> filteredStream = stream.filter(s -> s.length() > 5);
  1. μ΅œμ’… μ—°μ‚° μˆ˜ν–‰: μ΅œμ’… 연산은 슀트림의 μš”μ†Œλ₯Ό μ†ŒλΉ„ν•˜μ—¬ κ²°κ³Όλ₯Ό μƒμ„±ν•˜κ±°λ‚˜ λ°˜ν™˜ν•©λ‹ˆλ‹€. 슀트림의 μš”μ†Œλ“€μ„ μˆ˜μ§‘ν•˜μ—¬ μ»¬λ ‰μ…˜μœΌλ‘œ λ°˜ν™˜ν•˜κ±°λ‚˜, μš”μ†Œλ“€μ„ ν•˜λ‚˜μ”© νƒμƒ‰ν•˜κ±°λ‚˜, μš”μ†Œλ“€μ„ μ΄ν•©ν•˜κ±°λ‚˜, μš”μ†Œλ“€μ˜ 개수λ₯Ό μ„ΈλŠ” λ“±μ˜ μž‘μ—…μ„ μˆ˜ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, collect() λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ 슀트림의 μš”μ†Œλ₯Ό 리슀트둜 λ°˜ν™˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
List<String> resultList = filteredStream.collect(Collectors.toList());

μžλ°” μŠ€νŠΈλ¦Όμ€ μœ„μ™€ 같이 λ‹€μ–‘ν•œ λ©”μ„œλ“œ 체이닝을 톡해 데이터 처리 μž‘μ—…μ„ κ°„κ²°ν•˜κ²Œ ν‘œν˜„ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 쀑간 μ—°μ‚°κ³Ό μ΅œμ’… 연산을 μ‘°ν•©ν•˜μ—¬ ν•„μš”ν•œ 데이터 처리λ₯Ό μˆ˜ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€. λ˜ν•œ, ν•„μš”ν•œ κ²½μš°μ—λŠ” 병렬 처리λ₯Ό μ‚¬μš©ν•˜μ—¬ μž‘μ—…μ„ 가속화할 μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.

μŠ€νŠΈλ¦Όμ€ λΆˆν•„μš”ν•œ 데이터 처리λ₯Ό ν”Όν•˜κ³  μ„ μ–Έμ μœΌλ‘œ μ½”λ“œλ₯Ό μž‘μ„±ν•  수 μžˆλŠ” μž₯점을 μ œκ³΅ν•˜μ—¬, 효율적이고 가독성 쒋은 데이터 처리λ₯Ό κ°€λŠ₯ν•˜κ²Œ ν•©λ‹ˆλ‹€.

4. μžλ°” 슀트림(Stream)의 예제: 필터링

μžλ°” 슀트림(Stream)을 μ‚¬μš©ν•˜μ—¬ 데이터λ₯Ό ν•„ν„°λ§ν•˜λŠ” 방법에 λŒ€ν•΄ μ•Œμ•„λ³΄κ² μŠ΅λ‹ˆλ‹€. 필터링은 슀트림의 μš”μ†Œλ“€ μ€‘μ—μ„œ νŠΉμ • 쑰건을 λ§Œμ‘±ν•˜λŠ” μš”μ†Œλ“€λ§Œμ„ μ„ νƒν•˜λŠ” μž‘μ—…μ„ λ§ν•©λ‹ˆλ‹€. 필터링은 λ°μ΄ν„°μ˜ 쑰건에 따라 μ„ νƒμ μœΌλ‘œ μž‘μ—…μ„ μˆ˜ν–‰ν•  λ•Œ μœ μš©ν•˜κ²Œ μ‚¬μš©λ©λ‹ˆλ‹€.

λ‹€μŒμ€ μžλ°” μŠ€νŠΈλ¦Όμ„ μ‚¬μš©ν•˜μ—¬ λ¬Έμžμ—΄ λ¦¬μŠ€νŠΈμ—μ„œ 길이가 5 이상인 λ¬Έμžμ—΄λ“€λ§Œμ„ ν•„ν„°λ§ν•˜λŠ” 예제 μ½”λ“œμž…λ‹ˆλ‹€:

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

public class StreamExample {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("Alice");
        names.add("Bob");
        names.add("Charlie");
        names.add("David");
        names.add("Eve");

        // 길이가 5 이상인 λ¬Έμžμ—΄λ“€λ§Œ 필터링
        List<String> filteredNames = names.stream()
                                          .filter(name -> name.length() >= 5)
                                          .collect(Collectors.toList());

        // κ²°κ³Ό 좜λ ₯
        filteredNames.forEach(System.out::println);
    }
}

μœ„ μ˜ˆμ œμ—μ„œ filter() λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ name.length() >= 5 λΌλŠ” 쑰건을 λ§Œμ‘±ν•˜λŠ” μš”μ†Œλ“€μ„ μ„ νƒν•˜κ³ , collect() λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ μ„ νƒλœ μš”μ†Œλ“€μ„ 리슀트둜 λ°˜ν™˜ν•©λ‹ˆλ‹€. 이후 forEach() λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ 리슀트의 μš”μ†Œλ“€μ„ 좜λ ₯ν•©λ‹ˆλ‹€.

μ‹€ν–‰ κ²°κ³ΌλŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€:

Alice
Charlie
David

μœ„ μ˜ˆμ œμ—μ„œλŠ” 길이가 5 이상인 λ¬Έμžμ—΄λ“€λ§Œ ν•„ν„°λ§ν–ˆμ§€λ§Œ, λ‹€μ–‘ν•œ 쑰건을 μ‚¬μš©ν•˜μ—¬ 필터링할 수 μžˆμŠ΅λ‹ˆλ‹€. filter() λ©”μ„œλ“œμ— μ „λ‹¬λ˜λŠ” λžŒλ‹€μ‹μ„ μˆ˜μ •ν•˜μ—¬ μ›ν•˜λŠ” 쑰건으둜 필터링 μž‘μ—…μ„ μˆ˜ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

μŠ€νŠΈλ¦Όμ„ μ‚¬μš©ν•œ 필터링은 κ°„κ²°ν•˜κ³  가독성이 쒋은 μ½”λ“œλ₯Ό μž‘μ„±ν•  수 있으며, 데이터 처리 μž‘μ—…μ„ μ„ μ–Έμ μœΌλ‘œ ν‘œν˜„ν•  수 μžˆμ–΄ μœ μ§€λ³΄μˆ˜μ„±μ„ λ†’μ—¬μ€λ‹ˆλ‹€.

5. μžλ°” 슀트림(Stream)의 예제: 맀핑

μžλ°” 슀트림(Stream)을 μ‚¬μš©ν•˜μ—¬ 데이터λ₯Ό λ§€ν•‘ν•˜λŠ” 방법에 λŒ€ν•΄ μ•Œμ•„λ³΄κ² μŠ΅λ‹ˆλ‹€. 맀핑은 슀트림의 각 μš”μ†Œλ₯Ό λ‹€λ₯Έ μš”μ†Œλ‘œ λ³€ν™˜ν•˜λŠ” μž‘μ—…μ„ λ§ν•©λ‹ˆλ‹€. 맀핑은 데이터λ₯Ό μ›ν•˜λŠ” ν˜•νƒœλ‘œ λ³€ν™˜ν•˜κ±°λ‚˜, νŠΉμ • μ†μ„±λ§Œμ„ μΆ”μΆœν•˜λŠ” λ“±μ˜ μž‘μ—…μ— μœ μš©ν•˜κ²Œ μ‚¬μš©λ©λ‹ˆλ‹€.

λ‹€μŒμ€ μžλ°” μŠ€νŠΈλ¦Όμ„ μ‚¬μš©ν•˜μ—¬ λ¬Έμžμ—΄ 리슀트의 μš”μ†Œλ“€μ„ λŒ€λ¬Έμžλ‘œ λ³€ν™˜ν•˜λŠ” 예제 μ½”λ“œμž…λ‹ˆλ‹€:

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

public class StreamExample {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("alice");
        names.add("bob");
        names.add("charlie");
        names.add("david");
        names.add("eve");

        // λŒ€λ¬Έμžλ‘œ 맀핑
        List<String> mappedNames = names.stream()
                                        .map(name -> name.toUpperCase())
                                        .collect(Collectors.toList());

        // κ²°κ³Ό 좜λ ₯
        mappedNames.forEach(System.out::println);
    }
}

μœ„ μ˜ˆμ œμ—μ„œ map() λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ 각 μš”μ†Œλ₯Ό name.toUpperCase() 둜 λ§€ν•‘ν•©λ‹ˆλ‹€. 이후 collect() λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ λ§€ν•‘λœ μš”μ†Œλ“€μ„ 리슀트둜 λ°˜ν™˜ν•©λ‹ˆλ‹€.

μ‹€ν–‰ κ²°κ³ΌλŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€:

ALICE
BOB
CHARLIE
DAVID
EVE

μœ„ μ˜ˆμ œμ—μ„œλŠ” λ¬Έμžμ—΄ μš”μ†Œλ“€μ„ λŒ€λ¬Έμžλ‘œ λ§€ν•‘ν–ˆμ§€λ§Œ, λ°μ΄ν„°μ˜ ν˜•νƒœλ‚˜ 속성에 따라 λ‹€μ–‘ν•œ 맀핑 μž‘μ—…μ„ μˆ˜ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€. map() λ©”μ„œλ“œμ— μ „λ‹¬λ˜λŠ” λžŒλ‹€μ‹μ„ μˆ˜μ •ν•˜μ—¬ μ›ν•˜λŠ” ν˜•νƒœλ‘œ 맀핑 μž‘μ—…μ„ μˆ˜ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

μŠ€νŠΈλ¦Όμ„ μ‚¬μš©ν•œ 맀핑은 데이터λ₯Ό μœ μ—°ν•˜κ²Œ λ³€ν™˜ν•˜κ±°λ‚˜ μΆ”μΆœν•  수 μžˆλŠ” κ°„κ²°ν•˜κ³  가독성이 쒋은 μ½”λ“œλ₯Ό μž‘μ„±ν•  수 μžˆμ–΄ 맀우 μœ μš©ν•©λ‹ˆλ‹€.

λŒ“κΈ€