[JAVA] STREAM

μ„±μž₯일기·2024λ…„ 7μ›” 29일
0

[SWCAMP] JAVA

λͺ©λ‘ 보기
16/16

슀트림

πŸ’‘ 슀트림(Stream)은 μžλ°” 8 λΆ€ν„° μΆ”κ°€λœ κΈ°λŠ₯으둜, μ»¬λ ‰μ…˜μ— μ €μž₯된 μ—˜λ¦¬λ¨ΌνŠΈλ“€μ„ ν•˜λ‚˜μ”© μˆœνšŒν•˜λ©΄μ„œ μ²˜λ¦¬ν•  수 μžˆλŠ” κΈ°λŠ₯이닀. λžŒλ‹€μ‹κ³Ό ν•¨κ»˜ μ‚¬μš©ν•  수 있으며 μ»¬λ ‰μ…˜μ— λ“€μ–΄μžˆλŠ” 데이터에 λŒ€ν•œ 처리λ₯Ό κ°„κ²°ν•˜κ²Œ ν‘œν˜„ν•  수 μžˆλ‹€.

λ˜ν•œ λ‚΄λΆ€ 반볡자λ₯Ό μ‚¬μš©ν•˜κΈ° λ•Œλ¬Έμ— λ³‘λ ¬μ²˜λ¦¬κ°€ μ‰½λ‹€λŠ” μž₯점이 μžˆλ‹€.

Stream 이 ν•„μš”ν•œ 이유

  • μžλ°” 8 이전에 λ°°μ—΄ λ˜λŠ” μ»¬λ ‰μ…˜ λ‹€λ£¨λŠ” 방법은 for, foreach λ₯Ό μ‚¬μš©ν•˜μ—¬ μ—˜λ¦¬λ¨ΌνŠΈλ“€μ„ κΊΌλ‚΄μ„œ λ‹€λ£¨λŠ” λ°©λ²•μ΄μ—ˆλ‹€. 간단할 경우 상관 μ—†μ§€λ§Œ μ½”λ“œ 양이 λ§Žμ•„μ§ˆμˆ˜λ‘ λ³΅μž‘μ„±μ΄ 많이 μ˜¬λΌκ°€κ²Œ λ˜μ—ˆλ‹€.

  • μŠ€νŠΈλ¦Όμ„ μ΄μš©ν•˜λ©΄ λ°°μ—΄ λ˜λŠ” μ»¬λ ‰μ…˜μ„ ν•¨μˆ˜ μ—¬λŸ¬ 개λ₯Ό μ‚¬μš©ν•΄μ„œ κ²°κ³Όλ₯Ό μ‰½κ²Œ 얻을 수 μžˆλ‹€.

  • μŠ€νŠΈλ¦Όμ„ μ‚¬μš©ν•˜λ©΄ λžŒλ‹€μ‹μ„ ν™œμš©ν•΄ μ½”λ“œ 양도 쀄이고 κ°„κ²°ν•˜κ²Œ ν‘œν˜„λ„ κ°€λŠ₯ν•˜λ‹€.

  • 슀트림의 또 ν•˜λ‚˜μ˜ μž₯점은 λ³‘λ ¬μ²˜λ¦¬κ°€ κ°€λŠ₯ν•˜λ‹€λŠ” 것이닀.

  • Java 6 이전엔 iterator λ₯Ό μ‚¬μš©ν•˜μ—¬ μ—˜λ¦¬λ¨ΌνŠΈλ₯Ό μˆœνšŒν•˜μ˜€λ‹€.

Stream νŠΉμ§•

  1. μŠ€νŠΈλ¦Όμ€ 원본을 λ³€κ²½ν•˜μ§€ μ•ŠλŠ” 읽기 μ „μš©μ΄λ‹€.
  2. μŠ€νŠΈλ¦Όμ€ iterator 처럼 ν•œλ²ˆλ§Œ μ‚¬μš©λ˜κ³  사라진닀. ν•„μš”ν•˜λ©΄ λ‹€μ‹œ μŠ€νŠΈλ¦Όμ„ 생성해야 ν•œλ‹€.
  3. μ΅œμ’… μ—°μ‚° μ „κΉŒμ§€ 쀑간 연산이 μ²˜λ¦¬λ˜μ§€ μ•ŠλŠ”λ‹€.
  4. 병렬 μ²˜λ¦¬κ°€ μš©μ΄ν•˜λ‹€.

Stream ν™œμš©

  • μŠ€νŠΈλ¦Όμ€ 크게 생성, 가곡, κ²°κ³Ό 3κ°€μ§€λ‘œ κ΅¬λΆ„λœλ‹€.

    • 생성 : 슀트림 생성
    • 가곡 : μ›ν•˜λŠ” κ²°κ³Όλ₯Ό λ§Œλ“€κΈ° μœ„ν•œ 필터링, λ§€ν•‘ λ“±μ˜ μž‘μ—…
    • κ²°κ³Ό : μ΅œμ’… κ²°κ³Όλ₯Ό λ§Œλ“€μ–΄ λ‚΄λŠ” μž‘μ—…
λ°°μ—΄, μ»¬λ ‰μ…˜ -> 슀트림 생성 -> λ§€ν•‘ -> 필터링 -> κ²°κ³Ό

Stream 생성

  • μžλ°”μ—μ„œλŠ” 자주 μ‚¬μš©ν•˜λŠ” λ°°μ—΄κ³Ό μ»¬λ ‰μ…˜ κ°μ²΄μ—μ„œ stream() λ©”μ†Œλ“œλ₯Ό μ§€μ›ν•œλ‹€.

  • 이 λ©”μ†Œλ“œλ₯Ό μ‚¬μš©ν•˜λ©΄ 슀트림이 μƒμ„±λœλ‹€. 이 μ™Έ λ‹€μ–‘ν•œ λ°©λ²•μœΌλ‘œ μŠ€νŠΈλ¦Όμ„ 생성할 μˆ˜λ„ μžˆλ‹€.

λ°°μ—΄ 슀트림 생성

  • μŠ€νŠΈλ¦Όμ„ 생성할 배열을 μƒμ„±ν•œ ν›„ stream() μ‚¬μš©
public static void main(String[] args) {

    String[] strArr = new String[]{"hello", "stream", "world"};

    Stream<String> stringStream1 = Arrays.stream(strArr);
    Stream<String> stringStream2 = Arrays.stream(strArr, 0, 2);  // 인덱슀 0, 1 좜λ ₯(2 μ œμ™Έ)
`
    stringStream1.forEach(str -> System.out.print(str + " "));
    System.out.println();
    stringStream2.forEach(str -> System.out.print(str + " "));
}
  • μ‹€ν–‰κ²°κ³Ό
    hello stream world 
    hello stream

μ»¬λ ‰μ…˜ 슀트림 생성

  • μ»¬λ ‰μ…˜λ„ λ§ˆμ°¬κ°€μ§€λ‘œ stream() λ₯Ό μ‚¬μš©ν•  수 μžˆλ‹€.
public static void main(String[] args) {

    List<String> stringList = Arrays.asList("hello", "stream", "world");
    
    Stream<String> stringStream = stringList.stream();
    Stream<String> parallelStream = stringList.parallelStream();    // 병렬 처리 슀트림
}

λΉ„μ–΄ μžˆλŠ” 슀트림

  • λΉ„μ–΄μžˆλŠ” μŠ€νŠΈλ¦Όλ„ 생성할 수 μžˆλŠ”λ°, μš”μ†Œκ°€ 없을 λ•Œ μ‚¬μš©ν•˜λ©΄ λœλ‹€.
public Stream<String> streamOf(List<String> list) {
    return list == null || list.isEmpty()
            ? Stream.empty()
            : list.stream();
}

Stream.builder()

  • λΉŒλ”λ₯Ό μ‚¬μš©ν•΄ 직접 값을 넣을 μˆ˜λ„ μžˆλ‹€.
public static void main(String[] args) {

    Stream<String> builderStream =
        Stream.<String>builder()
                .add("hello")
                .add("stream")
                .add("world")
                .build();

    builderStream.forEach(str -> System.out.print(str + " "));
}
  • μ‹€ν–‰κ²°κ³Ό
    hello stream world

Stream().iterate()

  • iterate() λ©”μ†Œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ μˆ˜μ—΄ ν˜•νƒœλ‘œ μŠ€νŠΈλ¦Όμ„ 생성할 수 μžˆλ‹€.
public static void main(String[] args) {

    Stream<Integer> stream = Stream.iterate(10, x -> x * 2).limit(10);

    stream.forEach(val -> System.out.print(val + " "));
}

슀트림 생성 - κΈ°λ³Έ νƒ€μž…

  • μŠ€νŠΈλ¦Όμ„ μ œλ„€λ¦­μ„ μ‚¬μš©ν•˜μ§€ μ•Šκ³  κΈ°λ³Έ νƒ€μž…μœΌλ‘œ μŠ€νŠΈλ¦Όμ„ 생성할 수 μžˆλ‹€. (range() 와 rangeClosed() λŠ” λ²”μœ„ 차이닀.)

  • μ œλ„€λ¦­μ„ μ‚¬μš©ν•˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— λΆˆν•„μš”ν•œ μ˜€ν† λ°•μ‹±λ„ μΌμ–΄λ‚˜μ§€ μ•ŠλŠ”λ‹€.

  • ν•„μš”ν•œ κ²½μš°μ—λŠ” boxed() λ₯Ό μ‚¬μš©ν•΄ 박싱을 ν•  μˆ˜λ„ μžˆλ‹€.

public static void main(String[] args) {

    IntStream intStream = IntStream.range(5, 10);           // 5, 6, 7, 8, 9
    LongStream longStream = LongStream.rangeClosed(1, 3);   // 1, 2, 3
    Stream<Double> boxedDoubleStream = new Random().doubles(5).boxed();

    intStream.forEach(i -> System.out.print(i + " "));
    System.out.println();
    longStream.forEach(l -> System.out.print(l + " "));
    System.out.println();
    boxedDoubleStream.forEach(d -> System.out.print(d + " "));
}
  • μ‹€ν–‰κ²°κ³Ό
    5 6 7 8 9 
    1 2 3 
    0.003291382437904611 0.9568968822141412 0.1968739072117316 0.5724730036434903 0.1273612419533796

문자 κ΄€λ ¨ 슀트림

  • 문자 κ΄€λ ¨ 슀트림 생성이 κ°€λŠ₯ν•˜λ‹€.
public static void main(String[] args) {

    IntStream intStream = "Hello Stream World".chars();

    Stream<String> stringStream = Pattern.compile(",").splitAsStream("Hello,Stream,World");

    intStream.forEach(i -> System.out.print(i + " "));
    System.out.println();
    stringStream.forEach(s -> System.out.print(s + " "));
}
  • μ‹€ν–‰κ²°κ³Ό
    72 101 108 108 111 32 83 116 114 101 97 109 32 87 111 114 108 100 
    Hello Stream World

슀트림 ν•©μΉ˜κΈ°

public static void main(String[] args) {

    Stream<String> stringStream1 = Stream.of("Hello", "Stream", "World");
    Stream<String> stringStream2 = Stream.of("Java", "Spring", "SpringBoot");

    Stream<String> concatStream = Stream.concat(stringStream1, stringStream2);

    concatStream.forEach(s -> System.out.print(s + " "));
}
  • μ‹€ν–‰κ²°κ³Ό
    Hello Stream World Java Spring SpringBoot

Stream 가곡

  • μŠ€νŠΈλ¦Όμ— μžˆλŠ” 데이터에 λŒ€ν•΄ λ‚΄κ°€ μ›ν•˜λŠ” κ²°κ³Όλ₯Ό λ§Œλ“€κΈ° μœ„ν•΄μ„œ 쀑간 처리 μž‘μ—…μ„ 가곡이라고 ν•œλ‹€.

  • μŠ€νŠΈλ¦Όμ—μ„œλŠ” 데이터λ₯Ό 가곡할 수 μžˆλŠ” λ©”μ†Œλ“œλ“€μ„ μ œκ³΅ν•˜λŠ”λ°, ν•΄λ‹Ή λ©”μ†Œλ“œλ“€μ€ Stream을 전달받아 Stream을 λ°˜ν™˜ν•˜λ―€λ‘œ μ—°μ†ν•΄μ„œ λ©”μ†Œλ“œλ₯Ό μ—°κ²°ν•  수 μžˆλ‹€. λ˜ν•œ Stream 가곡할 λ•Œμ— ν•„ν„°-λ§΅(filter-map) 기반 APIλ₯Ό μ‚¬μš©ν•˜κΈ° λ•Œλ¬Έμ— 지연연산을 톡해 μ„±λŠ₯ μ΅œμ ν™”κ°€ κ°€λŠ₯ν•˜λ‹€.

상세 μ—­ν• λ©”μ†Œλ“œ
필터링filter(), distinct()
λ³€ν™˜map(), flatMap()
μ œν•œlimit(), skip()
μ •λ ¬sorted()
κ²°κ³Ό 확인peek()

Filter

  • ν•„ν„°(Filter)λŠ” μŠ€νŠΈλ¦Όμ—μ„œ νŠΉμ • λ°μ΄ν„°λ§Œ κ±ΈλŸ¬λ‚΄λŠ” λ©”μ†Œλ“œμ΄λ‹€. λ§€κ°œλ³€μˆ˜λ‘œ λ°›λŠ” PredicateλŠ” boolean을 λ¦¬ν„΄ν•˜λŠ” ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€μ΄λ‹€.
Stream<T> filter(Predicate<? super T> predicate);

Map

  • map() λ©”μ†Œλ“œλŠ” 값을 가곡할 수 μžˆλŠ” λžŒλ‹€μ‹μ„ λ§€κ°œλ³€μˆ˜λ‘œ λ°›λŠ”λ‹€.
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
  • μŠ€νŠΈλ¦Όμ— λ“€μ–΄μžˆλŠ” 데이터λ₯Ό νŠΉμ • λžŒλ‹€μ‹μ„ 톡해 데이터λ₯Ό κ°€κ³΅ν•˜κ³  μƒˆλ‘œμš΄ μŠ€νŠΈλ¦Όμ— λ‹΄μ•„μ£ΌλŠ” 역할을 ν•œλ‹€.

flatMap

  • flatMap() 은 map() κ³Ό λΉ„μŠ·ν•˜μ§€λ§Œ 쑰금 더 λ³΅μž‘ν•œ μž‘μ—…μ„ ν•˜λŠ”λ° ν™•μΈν•΄λ³΄μž.
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
  • flatMap() λŠ” 쀑첩 ꡬ쑰λ₯Ό ν•œ 단계 μ œκ±°ν•˜κ³  단일 μ»¬λ ‰μ…˜μœΌλ‘œ λ§Œλ“€μ–΄μ€€λ‹€. μ΄λŸ¬ν•œ μž‘μ—…μ„ ν”Œλž˜νŠΈλ‹(flattening)μ΄λΌκ³ ν•œλ‹€.

Sorting

  • μŠ€νŠΈλ¦Όμ— μžˆλŠ” 데이터듀을 μ •λ ¬ν•  λ•ŒλŠ” sorted() λ©”μ†Œλ“œλ₯Ό μ‚¬μš©ν•œλ‹€.

Stream κ²°κ³Ό

  • κ°€κ³΅λœ μŠ€νŠΈλ¦Όμ„ 톡해 μ΄μ œλŠ” κ²°κ³Όλ₯Ό λ§Œλ“€μ–΄ λ‚΄λŠ” μž‘μ—…μ΄ ν•„μš”ν•˜λ‹€.

  • 이 과정은 데이터λ₯Ό ν•„ν„°λ§ν•˜κ³  κ°€κ³΅ν•œ 뒀에 좜λ ₯ν•˜κΈ° μœ„ν•΄μ„œ μ§„ν–‰ν•˜λŠ” μž‘μ—…μ΄λ‹€.

Calculating

  • μŠ€νŠΈλ¦Όμ—μ„œλŠ” λ‹€μ–‘ν•œ λ©”μ†Œλ“œλ“€μ„ μ œκ³΅ν•˜λŠ”λ°, λ¨Όμ € μ΅œμ†Œ/μ΅œλŒ€/총합/평균 λ“± κ³Ό 같은 κ²°κ³Όλ₯Ό μ•Œμ•„λ³΄μž.

  • λ§Œμ•½ 슀트림이 λΉ„μ–΄ 있으면 count 와 sum은 0을 좜λ ₯ν•  것이닀.

public static void main(String[] args) {

    long count = IntStream.range(1, 10).count();
    long sum = IntStream.range(1, 10).sum();

    OptionalInt max = IntStream.range(1, 10).max();
    OptionalInt min = IntStream.range(1, 10).min();

    int OddSum = IntStream.range(1, 10)
            .filter(i -> i % 2 == 1)
            .sum();    
}

Reduction

  • reduce() λΌλŠ” λ©”μ†Œλ“œλŠ” μŠ€νŠΈλ¦Όμ— μžˆλŠ” λ°μ΄ν„°λ“€μ˜ 총합을 계산해쀀닀. reduce() νŒŒλΌλ―Έν„°μ— 따라 3κ°€μ§€ μ’…λ₯˜κ°€ μžˆλ‹€.
// 1개 (accumulator)
Optional<T> reduce(BinaryOperator<T> accumulator);

// 2개 (identity)
T reduce(T identity, BinaryOperator<T> accumulator);

// 3개 (combiner)
<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);

Collecting

  • collect() λŠ” Collector νƒ€μž…μ„ λ°›μ•„μ„œ μ²˜λ¦¬ν•˜λŠ”λ°, ν•΄λ‹Ή λ©”μ†Œλ“œλ₯Ό 톡해 μ»¬λ ‰μ…˜μ„ 좜λ ₯으둜 받을 수 μžˆλ‹€.

  • collect() λ©”μ†Œλ“œλŠ” Collector κ°μ²΄μ—μ„œ μ œκ³΅ν•˜λŠ” 정적 λ©”μ†Œλ“œλ₯Ό μ‚¬μš©ν•  수 μžˆλ‹€.

Collectors.toList()

- 슀트림 μž‘μ—… κ²°κ³Όλ₯Ό 리슀트둜 λ°˜ν™˜ν•΄μ£ΌλŠ” λ©”μ†Œλ“œμ΄λ‹€.

Collectors.joining()

- 슀트림의 μž‘μ—… κ²°κ³Όλ₯Ό String νƒ€μž…μœΌλ‘œ 이어 뢙인닀.

- μ„Έκ°œμ˜ 인자λ₯Ό 받을 수 μžˆλ‹€. 각 μΈμžλŠ” delimiter(κ΅¬λΆ„μž), prefix(맨 μ•ž 문자), suffix(맨 λ’€ 문자) 이닀.

Matching

  • Matching 은 Predicate λ₯Ό 인자둜 λ°›μ•„ 쑰건을 λ§Œμ‘±ν•˜λŠ” μ—˜λ¦¬λ¨ΌνŠΈκ°€ μžˆλŠ”μ§€ ν™•μΈν•˜κ³  boolean 으둜 리턴해쀀닀.
profile
μ—”μ§€λ‹ˆμ–΄λ‘œμ˜ μ„±μž₯일지

0개의 λŒ“κΈ€