๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

๋ชฉ๋ก์ด ์—†์Šต๋‹ˆ๋‹ค.

[JAVA] ์ŠคํŠธ๋ฆผ(Stream)

๐Ÿ—ฃ Language/JAVA
    ๋ฐ˜์‘ํ˜•

    1. ์ŠคํŠธ๋ฆผ(Stream)?


    ๋งŽ์€ ์ˆ˜์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃฐ ๋•Œ for๋ฌธ์ด๋‚˜ Iterator๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์™”๋‹ค. ๋˜ํ•œ ๋ฐ์ดํ„ฐ์˜ ํ˜•ํƒœ๋งˆ๋‹ค ๋‹ค๋ฅธ ๋ฐฉ์‹์œผ๋กœ ๋‹ค๋ค„์•ผ ํ–ˆ๋‹ค. ์˜ˆ๋ฅผ๋“ค์–ด, ๋ฐ์ดํ„ฐ์˜ ์ •๋ ฌ์„ ์˜ˆ๋กœ ๋“ค์ž๋ฉด ์ปฌ๋ ‰์…˜ ํด๋ž˜์Šค ์ค‘ List๋ฅผ ์ •๋ ฌํ•  ๋•Œ์—๋Š” Collections.sort()๋ฅผ, ๋ฐฐ์—ด์„ ์ •๋ ฌํ•  ๋•Œ๋Š” Arrays.sort()๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ–ˆ๋‹ค. ๊ฐ™์€ ๊ธฐ๋Šฅ์ด๊ณ  ๊ฐ™์€ ๋ฉ”์„œ๋“œ์ž„์—๋„ ๋‹ค๋ฅด๊ฒŒ ํ˜ธ์ถœ์„ ํ•ด์•ผ๋งŒ ํ–ˆ๋‹ค.


    ์ด๋Ÿฌํ•œ ๋ฌธ์ œ์ ๋“ค์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋งŒ๋“  ๊ฒƒ์ด ๋ฐ”๋กœ ์ž๋ฐ” 8๋ถ€ํ„ฐ ๋“ฑ์žฅํ•œ ์ŠคํŠธ๋ฆผ(Stream)์ด๋‹ค. ์–ด๋–ค ๋ฐ์ดํ„ฐ์ด๋“  ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๊ฒŒ ๋งŒ๋“ ๊ฒƒ์ด๋‹ค. ์ŠคํŠธ๋ฆผ์„ ์ด์šฉํ•˜๋ฉด, ๋ฐฐ์—ด์ด๋‚˜ ์ปฌ๋ ‰์…˜๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ํŒŒ์ผ์— ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๋„ ๋ชจ๋‘ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ฝ”๋“œ์˜ ์žฌ์‚ฌ์šฉ์„ฑ์ด ๋†’์•„์ง„๋‹ค. ์˜ˆ์ œ๋ฅผ ๋ณด๋ฉด ํ•œ ๋ˆˆ์— ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋‹ค.


    String[] strArr = { "aaa", "bbb", "ccc" };
    List strList = Arrays.asList(strArr);
     
    Stream strStream1 = strList.stream();    // ์ŠคํŠธ๋ฆผ์„ ์ƒ์„ฑ
    Stream strStream2 = Arrays.stream(strArr);    // ์ŠคํŠธ๋ฆผ์„ ์ƒ์„ฑ
     
    strStream1.sorted().forEach(System.out::println);
    strStream2.sorted().forEach(System.out::println);
     
    // ์ด์ „ ์ฝ”๋“œ
    /*
    Arrays.sort(strArr);
    Collections.sort(strList);
    for (String str : strArr) 
        System.out.println(str);
    for (String str : strList)
        System.out.println(str);
    */
    


    ์ŠคํŠธ๋ฆผ์„ ์‚ฌ์šฉํ•œ ์ฝ”๋“œ๊ฐ€ ๊ฐ„๊ฒฐํ•˜๊ณ  ์ดํ•ดํ•˜๊ธฐ ์‰ฌ์šฐ๋ฉด์„œ ์žฌ์‚ฌ์šฉ์„ฑ์ด ๋†’๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค. 


    ์—ฌ๊ธฐ์„œ ์ŠคํŠธ๋ฆผ์€ ์›๋ณธ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๋Š”๋‹ค. 

    ์›๋ณธ๋ฐ์ดํ„ฐ์—์„œ stream์„ ์ƒ์„ฑํ•˜์—ฌ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ŠคํŠธ๋ฆผ์„ ํ†ตํ•ด ์ •๋ ฌํ•œ ๊ฒฐ๊ณผ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์ปฌ๋ ‰์…˜์ด๋‚˜ ๋ฐฐ์—ด์— ๋‹ด์•„์„œ ๋ฐ˜ํ™˜ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.


    ์ŠคํŠธ๋ฆผ์€ ์ผํšŒ์šฉ์ด๋‹ค.

    ์ŠคํŠธ๋ฆผ์€ Iterator์ฒ˜๋Ÿผ ์ผํšŒ์šฉ์ด๋‹ค. Iterator๋กœ ์ปฌ๋ ‰์…˜์˜ ๋ชจ๋“  ์š”์†Œ๋ฅผ ์ฝ๊ณ  ๋‚œ ํ›„์— ๋‹ค์‹œ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” ๊ฒƒ์ฒ˜๋Ÿผ, ์ŠคํŠธ๋ฆผ๋„ ํ•œ๋ฒˆ ์‚ฌ์šฉํ•˜๋ฉด ๋‹ซํžˆ๊ธฐ ๋•Œ๋ฌธ์— ์žฌ์‚ฌ์šฉ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค.


    ์ŠคํŠธ๋ฆผ์€ ์ž‘์—…์„ ๋‚ด๋ถ€ ๋ฐ˜๋ณต์œผ๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค.

    ๋‚ด๋ถ€ ๋ฐ˜๋ณต์ด๋ž€, ๋ฐ˜๋ณต๋ฌธ์„ ๋ฉ”์„œ๋“œ์˜ ๋‚ด๋ถ€์— ๋„ฃ์—ˆ๋‹ค๋Š”๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค. ์œ„์˜ ์˜ˆ์ œ์ฝ”๋“œ์—์„œ forEach()๋Š” ์ŠคํŠธ๋ฆผ์— ์ •์˜๋œ ๋ฉ”์„œ๋“œ ์ค‘ ํ•˜๋‚˜๋กœ ๋งค๊ฐœ๋ณ€์ˆ˜์— ๋Œ€์ž…๋œ ๋žŒ๋‹ค์‹์„ ๋ชจ๋“  ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•ด for๋ฌธ ๋Œ๋ฆฌ๋Š” ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด 


    void forEach(Consumer action) {
        Objects.requireNonNull(action);    // ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ๋„ ์ฒดํฌ
     
        for(T t : src) {    // ๋‚ด๋ถ€๋ฐ˜๋ณต 
            action.accept(T);
        }
    }


    ์ŠคํŠธ๋ฆผ์€ ๋‹ค์–‘ํ•œ ์—ฐ์‚ฐ์„ ์ œ๊ณตํ•˜์—ฌ ๋ณต์žกํ•œ ์ž‘์—…์„ ๊ฐ„๋‹จํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค. ๋งˆ์น˜ DB์˜ ์ฟผ๋ฆฌ์™€ ๋น„์Šทํ•˜๋‹ค. ์—ฌ๊ธฐ์„œ ์—ฐ์‚ฐ์ด๋ž€ ์ŠคํŠธ๋ฆผ์—์„œ ์ •์˜๋œ ๋ฉ”์„œ๋“œ ์ค‘์— ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃจ๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์˜๋ฏธํ•œ๋‹ค. (ํ•œ๋งˆ๋””๋กœ ๋ฉ”์„œ๋“œ)


    ์ŠคํŠธ๋ฆผ์ด ์ œ๊ณตํ•˜๋Š” ์—ฐ์‚ฐ์€ ์ค‘๊ฐ„์—ฐ์‚ฐ๊ณผ ์ตœ์ข…์—ฐ์‚ฐ์œผ๋กœ ๋ถ„๋ฅ˜๋œ๋‹ค.

    ์ค‘๊ฐ„์—ฐ์‚ฐ์€ ์—ฐ์‚ฐ ๊ฒฐ๊ณผ๋ฅผ ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ฐ˜ํ™˜ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์—ฐ์†ํ•ด์„œ ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ์— ๋ฐ˜ํ•ด ์ตœ์ข… ์—ฐ์‚ฐ์€ ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๋ฅผ ์†Œ๋ชจํ•˜๋ฉด์„œ ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋‹จ ํ•œ๋ฒˆ๋งŒ ์—ฐ์‚ฐ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.


    stream.distinct().limit(5).sorted().forEach(System.out::println)


    Stream์— ์ •์˜๋œ ์—ฐ์‚ฐ์€ ์ฑ…์„ ์ฐธ๊ณ ํ•˜์ž.


    ์ค‘๊ฐ„์—ฐ์‚ฐ์€ map()๊ณผ flatMap(), ์ตœ์ข… ์—ฐ์‚ฐ์€ reduce()์™€ collect()๊ฐ€ ํ•ต์‹ฌ์ด๋‹ค. ๋‚˜๋จธ์ง€๋Š” ์ดํ•ดํ•˜๊ธฐ ์‰ฝ๊ณ  ์‚ฌ์šฉ๋ฒ•๋„ ๊ฐ„๋‹จํ•˜๋‹ค.


    ์ง€์—ฐ๋œ ์—ฐ์‚ฐ

    ์ŠคํŠธ๋ฆผ ์—ฐ์‚ฐ์—์„œ ์ค‘์š”ํ•œ ์ ์€ ์ตœ์ข… ์—ฐ์‚ฐ์ด ์ˆ˜ํ–‰๋˜๊ธฐ ์ „๊นŒ์ง€๋Š” ์ค‘๊ฐ„ ์—ฐ์‚ฐ์ด ์ˆ˜ํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค. ์ŠคํŠธ๋ฆผ์— ๋Œ€ํ•ด distinct()๋‚˜ sort()๊ฐ™์€ ์ค‘๊ฐ„ ์—ฐ์‚ฐ์„ ํ˜ธ์ถœํ•ด๋„ ์ฆ‰๊ฐ์ ์ธ ์—ฐ์‚ฐ์ด ์ˆ˜ํ–‰๋˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋‹ค. ์ตœ์ข… ์—ฐ์‚ฐ์ด ํ˜ธ์ถœ๋˜์–ด์•ผ ์ค‘๊ฐ„ ์—ฐ์‚ฐ์„ ๊ฑฐ์ณ ์ตœ์ข… ์—ฐ์‚ฐ์ด ์ˆ˜ํ–‰๋œ๋‹ค.



    Stream<Integer>์™€ IntStream

    ์ŠคํŠธ๋ฆผ์€ ๊ธฐ๋ณธ์ ์œผ๋กœ Stream<T>์ด์ง€๋งŒ, ์˜คํ† ๋ฐ•์‹&์–ธ๋ฐ•์‹ฑ์œผ๋กœ ์ธํ•œ ๋น„ํšจ์œจ์„ ์ค„์ด๊ธฐ ์œ„ํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ๋ณธํ˜•์œผ๋กœ ๋‹ค๋ฃจ๋Š” ์ŠคํŠธ๋ฆผ, IntStream, LongStream, DoubleStream์ด ์ œ๊ณต๋œ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ Stream<Integer> ๋Œ€์‹  IntStream์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋” ํšจ์œจ์ ์ด๋ผ๊ณ  ํ•œ๋‹ค. ๋˜ํ•œ IntStream์—๋Š” intํƒ€์ž…์˜ ๊ฐ’์„ ๋‹ค๋ฃจ๋Š”๋ฐ ์œ ์šฉํ•œ ๋ฉ”์„œ๋“œ๋“ค์ด ํฌํ•จ๋˜์–ด ์žˆ๋‹ค๊ณ  ํ•œ๋‹ค.


    ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ

    ์ŠคํŠธ๋ฆผ์˜ ์žฅ์ ์œผ๋กœ ๋ณ‘๋ ฌ์ฒ˜๋ฆฌ๊ฐ€ ์‰ฝ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. parallel()์ด๋ผ๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด์„œ ๋ณ‘๋ ฌ๋กœ ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•˜๋„๋ก ํ•˜๋ฉด๋œ๋‹ค. ๊ทธ์™€ ๋ฐ˜๋Œ€๋กœ ์ง๋ ฌ ์ŠคํŠธ๋ฆผ์€ sequential()์„ ํ˜ธ์ถœํ•˜๋ฉด ๋˜๋Š”๋ฐ ๋ชจ๋“  ์ŠคํŠธ๋ฆผ์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ง๋ ฌ ์ŠคํŠธ๋ฆผ์ด๊ธฐ ๋•Œ๋ฌธ์— sequential()์€ ๊ตณ์ด ํ˜ธ์ถœํ•  ํ•„์š” ์—†๋‹ค.



    2. ์ŠคํŠธ๋ฆผ ๋งŒ๋“ค๊ธฐ


    ์ŠคํŠธ๋ฆผ์œผ๋กœ ์ž‘์—…ํ•˜๊ธฐ ์œ„ํ•ด์„œ ์ŠคํŠธ๋ฆผ๋ถ€ํ„ฐ ์ƒ์„ฑํ•˜์ž. ์ŠคํŠธ๋ฆผ์ด ๋ฝ‘์•„๋‚ผ ์ˆ˜ ์žˆ๋Š” ๋Œ€์ƒ์€ ๋งŽ๊ธฐ ๋•Œ๋ฌธ์— ๋Œ€์ƒ์— ๋”ฐ๋ผ ์ •๋ฆฌํ•ด๋ณด์ž.


    ์ปฌ๋ ‰์…˜

    ๋ฐฐ์—ด

    ํŠน์ • ๋ฒ”์œ„์˜ ์ •์ˆ˜

    ์ž„์˜์˜ ์ˆ˜

    ๋žŒ๋‹ค์‹

    ํŒŒ์ผ

    ๋นˆ ์ŠคํŠธ๋ฆผ

    ๋‘ ์ŠคํŠธ๋ฆผ์˜ ์—ฐ๊ฒฐ


    ์ปฌ๋ ‰์…˜

    ์ปฌ๋ ‰์…˜์€ Collection ํด๋ž˜์Šค์— strearm()์ด ์ •์˜๋˜์–ด ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ Collection์„ ์ƒ์†๋ฐ›์€ List์™€ Set์„ ๊ตฌํ˜„ํ•œ ์ปฌ๋ ‰์…˜ ํด๋ž˜์Šค๋“ค์€ ๋ชจ๋‘ ์ด ๋ฉ”์„œ๋“œ๋กœ ์ŠคํŠธ๋ฆผ์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด List๋กœ๋ถ€ํ„ฐ ์ŠคํŠธ๋ฆผ์„ ์ƒ์„ฑํ•ด๋ณด์ž.


    List list = Arrays.asList(1,2,3,4,5);
    Stream intStream = list.stream();
    intStream.forEach(System.out::println);
    intStream.forEach(System.out::println);    // ์—๋Ÿฌ ๋ฐœ์ƒ. ์ŠคํŠธ๋ฆผ์ด ์ด๋ฏธ ๋‹ซํž˜.


    [์‹คํ–‰๊ฒฐ๊ณผ]


    1

    2

    3

    4

    5

    java.lang.IllegalStateException: 

      stream has already been operated upon or closed


    ํ•œ ๊ฐ€์ง€ ์ฃผ์˜ํ•  ์ ์€ forEach()๋ฅผ jstl์˜ forEach์™€ ๊ฐ™์ด ์ƒ๊ฐํ•˜๋ฉด ๋˜๋Š”๋ฐ ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๋ฅผ ์†Œ๋ชจํ•˜๋ฉด์„œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ•œ ๋ฒˆ ์ˆ˜ํ–‰๋œ ๋’ค์—๋Š” ๊ฐ™์€ ์ŠคํŠธ๋ฆผ์— ๋˜ forEach()๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์—†๋‹ค. ๊ทธ๋ž˜์„œ ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๋ฅผ ํ•œ๋ฒˆ ๋” ์ถœ๋ ฅํ•˜๋ ค๋ฉด ์ŠคํŠธ๋ฆผ์„ ์ƒˆ๋กœ ์ƒ์„ฑํ•ด์•ผํ•œ๋‹ค.



    ๋ฐฐ์—ด

    ๋ฐฐ์—ด๋กœ ์ŠคํŠธ๋ฆผ์„ ์ƒ์„ฑํ•˜๋Š” ๋ฉ”์„œ๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด Stream๊ณผ Arrays์— static๋ฉ”์„œ๋“œ๋กœ ์ •์˜๋˜์–ด ์žˆ๋‹ค.


    Stream.of(T... values) // ๊ฐ€๋ณ€์ธ์ž
    Stream.of(T[])
    Arrays.stream(T[])
    Arrays.stream(T[] array, int startInclusive, int endExclusive)


    ์˜ˆ๋ฅผ๋“ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.


    Stream strStream = Stream.of("a", "b", "c"); // ๊ฐ€๋ณ€์ธ์ž
    Stream strStream = Stream.of(new String[] {"a", "b", "c"});
    Stream strStream = Arrays.stream(new String[] {"a", "b", "c"});
    Stream strStream = Arrays.stream(new String[] {"a", "b", "c"}, 1, 3)


    ๊ทธ๋ฆฌ๊ณ  int, long, double๊ณผ ๊ฐ™์€ ๊ธฐ๋ณธํ˜• ๋ฐฐ์—ด์„ ๋ฐ์ดํ„ฐ๋กœํ•˜๋Š” ์ŠคํŠธ๋ฆผ์„ ์ƒ์„ฑํ•˜๋Š” ๋ฉ”์„œ๋“œ๋„ ์žˆ๋‹ค.


    IntStream IntStream.of(int... values)
    IntStream IntStream.of(int[])
    IntStream Arrays.stream(int[])
    IntStream Arrays.stream(int[] array, int startInclusive, int endExclusive)



    ํŠน์ • ๋ฒ”์œ„์˜ ์ •์ˆ˜


    ์•ž์—์„œ๋„ ์–ธ๊ธ‰ํ–ˆ์ง€๋งŒ ๊ธฐ๋ณธํ˜• ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃจ๋Š” ์ŠคํŠธ๋ฆผ์€ ๊ธฐ๋ณธํ˜• ๋ฐ์ดํ„ฐ๋ฅผ ์ข€ ๋” ์œ ์šฉํ•˜๊ฒŒ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๋Š” ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ๋‹ค๊ณ  ํ–ˆ๋Š”๋ฐ, IntStream๊ณผ LongStream์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ง€์ •๋œ ๋ฒ”์œ„์˜ ์—ฐ์†๋œ ์ •์ˆ˜๋ฅผ ์ŠคํŠธ๋ฆผ์œผ๋กœ ์ƒ์„ฑํ•ด์„œ ๋ฐ˜ํ™˜ํ•˜๋Š” range()์™€ rangeClosed()๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.


    IntStream IntStream.range(int begin, int end)
    IntStream IntStream.rangeClosed(int begin, int end)


    ๋‘˜์˜ ์ฐจ์ด์ ์€ range()๋Š” ๋์— end๊ฐ€ ๋ฒ”์œ„์— ํฌํ•จ๋˜์ง€ ์•Š๊ณ , rangeClosed()๋Š” ํฌํ•จ๋œ๋‹ค.


    IntStream intStream = IntStream.range(1, 5)    // 1,2,3,4
    IntStream intStream = IntStream.rangeClosed(1, 5)    // 1,2,3,4,5



    ์ž„์˜์˜ ์ˆ˜


    ๋‚œ์ˆ˜๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” Randomํด๋ž˜์Šค์—๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ์ธ์Šคํ„ด์Šค ๋ฉ”์„œ๋“œ๋“ค์ด ํฌํ•จ๋˜์–ด ์žˆ๋‹ค. ์ด ๋ฉ”์„œ๋“œ๋“ค์€ ํ•ด๋‹น ํƒ€์ž…์˜ ๋‚œ์ˆ˜๋“ค๋กœ ์ด๋ฃจ์–ด์ง„ ์ŠคํŠธ๋ฆผ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.


    IntStream ints()
    LongStream longs()
    DoubleStream doubles()


    ์—ฌ๊ธฐ์„œ ๋ฐ˜ํ™˜๋˜๋Š” ์ŠคํŠธ๋ฆผ๋“ค์€ ํฌ๊ธฐ๊ฐ€ ์ •ํ•ด์ง€์ง€์•Š์€ '๋ฌดํ•œ ์ŠคํŠธ๋ฆผ'์ด๋ฏ€๋กœ limit()๋„ ๊ฐ™์ด ์‚ฌ์šฉํ•ด์„œ ์ŠคํŠธ๋ฆผ์˜ ํฌ๊ธฐ๋ฅผ ์ œํ•œํ•ด์ฃผ์–ด์•ผ ์•ˆ์ „ํ•œ ํ”„๋กœ๊ทธ๋žจ์ด ๋œ๋‹ค. limit()์€ ์ŠคํŠธ๋ฆผ์˜ ๊ฐœ์ˆ˜๋ฅผ ์ง€์ •ํ•˜๋Š”๋ฐ ์‚ฌ์šฉ๋˜๋ฉฐ, ๋ฌดํ•œ ์ŠคํŠธ๋ฆผ์„ ์œ ํ•œ ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋งŒ๋“ค์–ด ์ค€๋‹ค.


    IntStream intStream = new Random().ints();    // ๋ฌดํ•œ ์ŠคํŠธ๋ฆผ
    intStream.limit(5).forEach(System.out::println);


    [์‹คํ–‰๊ฒฐ๊ณผ]


    1443532478

    -2115253488

    1079105334

    941471356

    178558647


    ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์œผ๋กœ๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์ŠคํŠธ๋ฆผ์˜ ํฌ๊ธฐ๋ฅผ ์ง€์ •ํ•˜์—ฌ ์œ ํ•œ ์ŠคํŠธ๋ฆผ์„ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ํ•  ์ˆ˜ ์žˆ๋‹ค.


    IntStream ints(long streamSize)
    LongStream longs(long streamSize)
    DoubleStream doubles(long streamSize)


    ๋˜ํ•œ ์œ„์˜ ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ์ƒ์„ฑ๋œ ์ŠคํŠธ๋ฆผ์˜ ๋‚œ์ˆ˜๋Š” ์•„๋ž˜์˜ ๋ฒ”์œ„๋ฅผ ๊ฐ–๋Š”๋‹ค.


    Integer.MIN_VALUE <= ints() <= Integer.MAX_VALUE
    Long.MIN_VALUE <= longs() <= Long.MAX_VALUE
    0.0 <= doubles() < 1.0


    ์ง€์ •๋œ ๋ฒ”์œ„(begin ~ end)์˜ ๋‚œ์ˆ˜๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋Š” ์ŠคํŠธ๋ฆผ์„ ์–ป๋Š” ๋ฉ”์„œ๋“œ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค. ๋‹จ, end๋Š” ๋ฒ”์œ„์— ํฌํ•จ๋˜์ง€ ์•Š๋Š”๋‹ค.


    IntStream ints(int begin, int end)
    LongStream longs(long begin, long end)
    DoubleStream doubles(double begin, double end)
     
    IntStream ints(long streamSize, int begin, int end)
    LongStream longs(long streamSize, long begin, long end)
    DoubleStream doubles(long streamSize, double begin, double end)



    ๋žŒ๋‹ค์‹


    Stream ํด๋ž˜์Šค์˜ iterate()์™€ generate()๋Š” ๋žŒ๋‹ค์‹์„ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฐ›์•„์„œ, ์ด ๋žŒ๋‹ค์‹์— ์˜ํ•ด ๊ณ„์‚ฐ๋˜๋Š” ๊ฐ’๋“ค์„ ์š”์†Œ๋กœ ํ•˜๋Š” ๋ฌดํ•œ ์ŠคํŠธ๋ฆผ์„ ์ƒ์„ฑํ•œ๋‹ค.


    static  Stream  iterate(T seed, UnaryOperator f)
    static  Stream  generate(Supplier s)


    iterate()๋Š” ์”จ์•—๊ฐ’(seed)์œผ๋กœ ์ง€์ •๋œ ๊ฐ’๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด์„œ, ๋žŒ๋‹ค์‹ f์— ์˜ํ•ด ๊ณ„์‚ฐ๋œ ๊ฒฐ๊ณผ๋ฅผ ๋‹ค์‹œ seed๊ฐ’์œผ๋กœ ํ•ด์„œ ๊ณ„์‚ฐ์„ ๋ฐ˜๋ณตํ•œ๋‹ค. ์•„๋ž˜์˜ evenStream์€ 0๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด์„œ ๊ฐ’์ด 2์”ฉ ๊ณ„์† ์ฆ๊ฐ€ํ•œ๋‹ค.


    Stream evenStream = Stream.iterate(0, n->n+2);    // 0, 2, 4, 6, ...


    generate()๋Š” iterate()์™€ ๋‹ฌ๋ฆฌ ๊ณ„์‚ฐ๋œ ๊ฒฐ๊ณผ๋ฅผ ๋‹ค์‹œ seed๊ฐ’์œผ๋กœ ํ•ด์„œ ๊ณ„์‚ฐํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋˜ํ•œ ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ํƒ€์ž…์€ Supplier<T>๋กœ ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ์—†๋Š” ๋žŒ๋‹ค์‹๋งŒ์„ ํ—ˆ์šฉํ•œ๋‹ค.


    Stream randomStream = Stream.generate(Math::random);
    Stream oneStream = Stream.generate(()->1));



    โ€ป ์ฃผ์˜์‚ฌํ•ญ : iterate()์™€ generate()๋กœ ์ƒ์„ฑ๋œ ์ŠคํŠธ๋ฆผ์€ ๊ธฐ๋ณธํ˜• ์ŠคํŠธ๋ฆผ ํƒ€์ž…์˜ ์ฐธ์กฐ๋ณ€์ˆ˜๋กœ ๋‹ค๋ฃฐ ์ˆ˜ ์—†๋‹ค.


    IntStream evenStream = Stream.iterate(0, n->n+2);    // ์—๋Ÿฌ
    DoubleStream randomStream = Stream.generate(Math::random);    // ์—๋Ÿฌ


    ๊ตณ์ด ํ•„์š”ํ•˜๋‹ค๋ฉด, ์•„๋ž˜์™€ ๊ฐ™์ด mapToInt()์™€ ๊ฐ™์€ ๋ฉ”์„œ๋“œ๋กœ ๋ณ€ํ™˜ํ•ด์•ผ ํ•œ๋‹ค. ๋ฐ˜๋Œ€๋กœ IntStreamํƒ€์ž…์˜ ์ŠคํŠธ๋ฆผ์„ Stream<Integer>ํƒ€์ž…์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋ ค๋ฉด, boxed()๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.


    IntStream evenStream = Stream.iterate(0, n->n+2).mapToInt(Integer::valueOf);
    Stream stream = evenStream.boxed();    // IntStream -> Stream



    ํŒŒ์ผ

    java.nio.file.FilesํŒจํ‚ค์ง€์— ํŒŒ์ผ์„ ๋‹ค๋ฃจ๋Š” ์œ ์šฉํ•œ ๋ฉ”์„œ๋“œ๋“ค์ด ์žˆ๋‹ค. ๊ทธ ์ค‘ list()๋Š” ์ง€์ •๋œ ๋””๋ ‰ํ† ๋ฆฌ(dir)์— ์žˆ๋Š” ํŒŒ์ผ์˜ ๋ชฉ๋ก์„ ์†Œ์Šค๋กœ ํ•˜๋Š” ์ŠคํŠธ๋ฆผ์„ ์ƒ์„ฑํ•ด์„œ ๋ฐ˜ํ™˜ํ•œ๋‹ค.


    Stream Files.list(Path dir)


    ๊ทธ๋ฆฌ๊ณ , ํŒŒ์ผ์˜ ํ•œ ํ–‰์„ ์š”์†Œ๋กœ ํ•˜๋Š” ์ŠคํŠธ๋ฆผ์„ ์ƒ์„ฑํ•˜๋Š” ๋ฉ”์„œ๋“œ๋„ ์žˆ๋‹ค.


    Stream Files.lines(Path path)
    Stream Files.lines(Path path, Charset cs)
    Stream lines()


    ์•„๋ž˜ ์„ธ๋ฒˆ์งธ ๋ฉ”์„œ๋“œ๋Š” BufferdReaderํด๋ž˜์Šค์— ์†ํ•œ ๊ฒƒ์ธ๋ฐ, ํŒŒ์ผ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋‹ค๋ฅธ ์ž…๋ ฅ๋Œ€์ƒ์œผ๋กœ๋ถ€ํ„ฐ๋„ ๋ฐ์ดํ„ฐ๋ฅผ ํ–‰๋‹จ์œ„๋กœ ์ฝ์–ด์˜ฌ ์ˆ˜  ์žˆ๋‹ค.



    ๋นˆ ์ŠคํŠธ๋ฆผ

    ์š”์†Œ๊ฐ€ ํ•˜๋‚˜๋„ ์—†๋Š” ํ…… ๋นˆ ์ŠคํŠธ๋ฆผ์„ ์ƒ์„ฑํ•  ์ˆ˜๋„ ์žˆ๋‹ค. ์ŠคํŠธ๋ฆผ์— ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•œ ๊ฒฐ๊ณผ๊ฐ€ ํ•˜๋‚˜๋„ ์—†์„ ๋•Œ, null๋ณด๋‹ค ๋นˆ ์ŠคํŠธ๋ฆผ์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ์ด ์—๋Ÿฌ๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋” ๋‚ซ๋‹ค.


    Stream emptyStream = Stream.empty();    // ๋นˆ ์ŠคํŠธ๋ฆผ ์ƒ์„ฑ
    long count = emptyStream.count()    // 0


    count()๋Š” ์š”์†Œ์˜ ๊ฐœ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.



    ๋‘ ์ŠคํŠธ๋ฆผ์˜ ์—ฐ๊ฒฐ

    Stream์˜ static๋ฉ”์„œ๋“œ์ธ concat()์„ ์‚ฌ์šฉํ•˜๋ฉด, ๋‘ ์ŠคํŠธ๋ฆผ์„ ํ•˜๋‚˜๋กœ ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ๋•Œ ์—ฐ๊ฒฐํ•˜๋Š” ๋‘ ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๋Š” ๊ฐ™์€ ํƒ€์ž…์ด์–ด์•ผ ํ•œ๋‹ค.


    Stream strs1 = Stream.of(str1);
    Stream strs2 = Stream.of(str2);
    Stream strs3 = Stream.concat(stgr1, str2);    // ๋‘ ์ŠคํŠธ๋ฆผ์„ ํ•˜๋‚˜๋กœ ์—ฐ๊ฒฐ



    3. ์ŠคํŠธ๋ฆผ์˜ ์ค‘๊ฐ„์—ฐ์‚ฐ


    ์ŠคํŠธ๋ฆผ ์ž๋ฅด๊ธฐ - skip(), limit()

    ์ŠคํŠธ๋ฆผ ์ผ๋ถ€๋ฅผ ์ž๋ฅผ ๋•Œ skip()๊ณผ limit()์„ ์‚ฌ์šฉํ•œ๋‹ค. skip(3)์€ ์ฒ˜์Œ 3๊ฐœ์˜ ์š”์†Œ๋ฅผ ๊ฑด๋„ˆ๋›ฐ๊ณ , limit(5)๋Š” ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๋ฅผ ์ตœ๋Œ€ 5๊ฐœ๋กœ ์ œํ•œํ•œ๋‹ค.


    Stream skip(long n)
    Stream limit(long maxSize)
    


    ์˜ˆ๋ฅผ๋“ค์–ด 10๊ฐœ์˜ ์š”์†Œ๋ฅผ ๊ฐ€์ง„ ์ŠคํŠธ๋ฆผ์— skip(3)๊ณผ limit(5)๋ฅผ ์ ์šฉํ•˜๋ฉด 4๋ฒˆ์งธ ์š”์†Œ๋ถ€ํ„ฐ 5๊ฐœ๋ฅผ ๊ฐ€์ง„ ์ŠคํŠธ๋ฆผ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.


    IntStream intStream = IntStream.rangeClosed(1, 10);    // 1~10์˜ ์š”์†Œ๋ฅผ ๊ฐ€์ง„ ์ŠคํŠธ๋ฆผ ์ƒ์„ฑ
    intStream.skip(3).limit(5).forEach(System.out::println);


    [์‹คํ–‰ ๊ฒฐ๊ณผ]


    4

    5

    6

    7

    8


    ๊ธฐ๋ณธํ˜• ์ŠคํŠธ๋ฆผ์—๋„ skip()๊ณผ limit()์ด ์ •์˜ ๋˜์–ด ์žˆ๊ณ  ๋ฐ˜ํ™˜ ํƒ€์ž…์ด ๊ธฐ๋ณธํ˜• ์ŠคํŠธ๋ฆผ์ด๋ผ๋Š” ๊ฒƒ๋งŒ ๋‹ค๋ฅด๋‹ค.


    ์ŠคํŠธ๋ฆผ ์š”์†Œ ๊ฑธ๋Ÿฌ๋‚ด๊ธฐ - filter(), distinct()

    distinct()๋Š” ์ค‘๋ณต์š”์†Œ๋“ค์„ ์ œ๊ฑฐํ•˜๊ณ  filter()๋Š” ์กฐ๊ฑด์— ๋งž์ง€ ์•Š๋Š” ์š”์†Œ๋ฅผ ๊ฑธ๋Ÿฌ๋‚ธ๋‹ค. ์ฟผ๋ฆฌ์™€ ์•„์ฃผ ํก์‚ฌํ•˜๋‹ค.


    Stream filter(Predicate predicate)


    filter๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ Predicate๋ฅผ ํ•„์š”๋กœ ํ•˜๋Š”๋ฐ, ์•„๋ž˜์™€ ๊ฐ™์ด ์—ฐ์‚ฐ๊ฒฐ๊ณผ๊ฐ€ boolean ์ธ ๋žŒ๋‹ค์‹์„ ์‚ฌ์šฉํ•ด๋„ ๋œ๋‹ค.


    IntStream intStream = IntStream.rangeClosed(1, 10);    // 1~10์˜ ์š”์†Œ๋ฅผ ๊ฐ€์ง„ ์ŠคํŠธ๋ฆผ ์ƒ์„ฑ
    intStream.filter(i -> i%2 == 0).forEach(System.out::println);    // 2 4 6 8 10


    ํ•„์š”ํ•˜๋‹ค๋ฉด filter()๋ฅผ ๋‹ค๋ฅธ ์กฐ๊ฑด์œผ๋กœ ์—ฌ๋Ÿฌ ๋ฒˆ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๋‹ค.



    ์ •๋ ฌ - sorted()

    ์ŠคํŠธ๋ฆผ์„ ์ •๋ ฌํ•  ๋•Œ๋Š” sorted()๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.


    Stream sorted()
    Stream sorted(Comparator comparator)


    sorted()๋Š” ์ง€์ •๋œ Comparator๋กœ ์ŠคํŠธ๋ฆผ์„ ์ •๋ ฌํ•˜๋Š”๋ฐ, Comparator ๋Œ€์‹  int๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋žŒ๋‹ค์‹์„ ์‚ฌ์šฉํ•ด๋„ ๋œ๋‹ค. Comparator๋ฅผ ์ง€์ •ํ•˜์ง€ ์•Š์œผ๋ฉด ์ŠคํŠธ๋ฆผ ์š”์†Œ์˜ ๊ธฐ๋ณธ ์ •๋ ฌ ๊ธฐ์ค€์œผ๋กœ ์ •๋ ฌํ•œ๋‹ค. ๋‹จ, ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๊ฐ€ Comparable์„ ๊ตฌํ˜„ํ•œ ํด๋ž˜์Šค๊ฐ€ ์•„๋‹ˆ๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.


    Stream strStream = Stream.of("dd", "aaa", "CC", "cc", "b");
    strStream.sorted().forEach(System.out::println);


    [์‹คํ–‰๊ฒฐ๊ณผ]


    CC

    aaa

    b

    cc

    dd



    Comparator์˜ static๋ฉ”์„œ๋“œ


    naturalOrder()
    reverseOrder()
    comparing(Function(T, U) keyExtractor)
    comparing(Function(T, U) keyExtractor, Comparator keyComparator)
    comparingInt(ToIntFunction keyExtractor)
    comparingLong(ToLongFunction keyExtractor)
    comparingDouble(ToDoubleFunction keyExtractor)
    nullsFirst(Comparator comparator)
    nullsLast(Comparator comparator)


    ์ •๋ ฌ์— ์‚ฌ์šฉ๋˜๋Š” ๋ฉ”์„œ๋“œ๋Š” ๋งŽ์ง€๋งŒ ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ๋ฉ”์„œ๋“œ๋Š” comparing()์ด๋‹ค.


    ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๊ฐ€ Comparable์„ ๊ตฌํ˜„ํ•œ ๊ฒฝ์šฐ, ๋งค๊ฐœ๋ณ€์ˆ˜ ํ•˜๋‚˜์งœ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋˜๊ณ  ์•„๋‹ˆ๋ฉด ์ถ”๊ฐ€์ ์ธ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์ •๋ ฌ๊ธฐ์ค€(Comparator)๋ฅผ ๋”ฐ๋กœ ์ง€์ •ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.


    ๋น„๊ต ๋Œ€์ƒ์ด ๊ธฐ๋ณธํ˜•์ธ ๊ฒฝ์šฐ, comparing()๋Œ€์‹  comparingInt, comparingLong, comparingDouble์„ ์‚ฌ์šฉํ•˜๋ฉด ์˜คํ† ๋ฐ•์‹ฑ๊ณผ ์–ธ๋ฐ•์‹ฑ ๊ณผ์ •์ด ์—†๊ธฐ ๋•Œ๋ฌธ์— ๋” ํšจ์œจ์ ์ด๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ •๋ ฌ ์กฐ๊ฑด์„ ์ถ”๊ฐ€ํ•  ๋•Œ์—๋Š” thenComparing()์„ ์‚ฌ์šฉํ•œ๋‹ค.


    thenComparing(Comparator other)
    thenComparing(Function keyExtractor)
    thenComparing(Function keyExtractor, Comparator keyComp)



    ๋ณ€ํ™˜ - map()

    ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ์— ์ €์žฅ๋œ ๊ฐ’ ์ค‘์—์„œ ์›ํ•˜๋Š” ํ•„๋“œ๋งŒ ๋ฝ‘์•„๋‚ด๊ฑฐ๋‚˜ ํŠน์ • ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜ํ•ด์•ผ ํ•  ๋•Œ๊ฐ€ ์žˆ๋Š”๋ฐ ๊ทธ ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋ฐ”๋กœ map()์ด๋‹ค. ์ด ๋ฉ”์„œ๋“œ์˜ ์„ ์–ธ๋ถ€๋Š” ์•„๋ž˜์™€ ๊ฐ™๊ณ , Tํƒ€์ž…์„ Rํƒ€์ž…์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์ง€์ •ํ•ด์•ผํ•œ๋‹ค.


    Stream map(Function mapper)


    ์˜ˆ๋ฅผ๋“ค์–ด File์ŠคํŠธ๋ฆผ์—์„œ ํŒŒ์ผ์˜ ์ด๋ฆ„๋งŒ ๋ฝ‘์•„์„œ ์ถœ๋ ฅํ•˜๊ณ  ์‹ถ์„ ๋•Œ, ์•„๋ž˜์™€ ๊ฐ™์ด map()์„ ์ด์šฉํ•˜๋ฉด ์‰ฝ๋‹ค.


    Stream<File> fileStream = Stream.of(new File("Ex1.java"), new File("Ex1"),
     new File("Ex1.bak"), new File("Ex2.java"), new File("Ex1.txt"));
     
    // map()์œผ๋กœ Stream์„ Stream์œผ๋กœ ๋ณ€ํ™˜
    Stream filenameStream = fileStream.map(File::getName);
    filenameStream.forEach(System.out::println);    // ์ŠคํŠธ๋ฆผ์˜ ๋ชจ๋“  ํŒŒ์ผ์˜ ์ด๋ฆ„์„ ์ถœ๋ ฅ


    [์‹คํ–‰๊ฒฐ๊ณผ]


    Ex1.java

    Ex1

    Ex1.bak

    Ex2.java

    Ex1.txt


    map()์€ ์ค‘๊ฐ„ ์—ฐ์‚ฐ์œผ๋กœ ์ŠคํŠธ๋ฆผ์„ ๋ฐ˜ํ™˜ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ•˜๋‚˜์˜ ์ŠคํŠธ๋ฆผ์— ์—ฌ๋Ÿฌ ๋ฒˆ ์ ์šฉํ•  ์ˆ˜๋„ ์žˆ๊ณ , distinct๋ฅผ ๋’ค์— ์ ์šฉํ•ด์„œ ์ค‘๋ณต์„ ์ œ๊ฑฐํ•  ์ˆ˜๋„ ์žˆ๋‹ค.



    flatMap() - Stream<T[]>๋ฅผ Stream<T>๋กœ ๋ณ€ํ™˜

    ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๊ฐ€ ๋ฐฐ์—ด์ด๊ฑฐ๋‚˜ map()์˜ ์—ฐ์‚ฐ๊ฒฐ๊ณผ๊ฐ€ ๋ฐฐ์—ด์ธ ๊ฒฝ์šฐ, ์ฆ‰ ์ŠคํŠธ๋ฆผ์˜ ํƒ€์ž…์ด Stream<T[]>์ธ ๊ฒฝ์šฐ, Stream<T>๋กœ ๋‹ค๋ฃจ๋Š” ๊ฒƒ์ด ๋” ํŽธ๋ฆฌํ•  ๋•Œ๊ฐ€ ์žˆ๋Š”๋ฐ, ๊ทธ๋Ÿด ๋•Œ๋Š” map()๋Œ€์‹  flatMap()์„ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค. ๋‹ค์Œ ์˜ˆ๋ฅผ ๋ณด์ž.


    Stream strArrStrm = Stream.of(new String[] { "abc", "def", "ghi" }, new String[] { "ABc", "DEF", "GHI" });


    ์ด ๋•Œ ๊ฐ ์š”์†Œ์˜ ๋ฌธ์ž์—ด๋“ค์„ ํ•ฉ์ณ์„œ ๋ฌธ์ž์—ด์„ ๋‹ค๋ฃจ๋Š” ์ŠคํŠธ๋ฆผ, ์ฆ‰ Stream<String>์œผ๋กœ ๋งŒ๋“ค๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผํ• ๊นŒ? for๋ฌธ์„ ๋Œ๋ ค์„œ ํ• ์ˆ˜๋„ ์žˆ๊ฒ ์ง€๋งŒ flatMap์„ ์‚ฌ์šฉํ•˜๋ฉด ํ•œ ์ค„๋กœ ๋๋‚ผ ์ˆ˜ ์žˆ๋‹ค.


    String<String> strStrm = strArrStrm.flatMap(Arrays::stream);


    ๋˜ ๋‹ค๋ฅธ ์˜ˆ์ œ๋ฅผ ๋ณด๋ฉด ์—ฌ๋Ÿฌ ๋ฌธ์žฅ์„ ์š”์†Œ๋กœ ํ•˜๋Š” ์ŠคํŠธ๋ฆผ์ด ์žˆ์„ ๋•Œ, ์ด ๋ฌธ์žฅ๋“ค์„ split()์œผ๋กœ ๋‚˜๋ˆ ์„œ ์š”์†Œ๊ฐ€ ๋‹จ์–ด์ธ ์ŠคํŠธ๋ฆผ์„ ๋งŒ๋“ค๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ๊ฐ€ ์žˆ์„ ๊ฒƒ์ด๋‹ค.


    String[] lineArr = {
        "Belive or not It is true",
        "Do or do not There is no try"
    };
     
    Stream<String> lineStream = Arrays.stream(lineArr);
    // Stream strArrStream = lineStream.map(1->Stream.of(line.split(" +")));
    Stream<String[]> strArrStream = lineStream.flatMap(1->Stream.of(line.split(" +")));


    ์ด๋Ÿด ๋•Œ map์€ Stream<String[]>์„ ๋Œ๋ ค์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ์ ์ ˆํ•˜์ง€ ์•Š๊ณ  flatMap์œผ๋กœ ํ•˜๊ฒŒ๋˜๋ฉด ์›ํ•˜๋Š” Stream<String>์„ ๋ฝ‘์•„๋‚ผ ์ˆ˜ ์žˆ๋‹ค.


    ๋“œ๋ฌผ์ง€๋งŒ, ์ŠคํŠธ๋ฆผ์„ ์š”์†Œ๋กœ ํ•˜๋Š” ์ŠคํŠธ๋ฆผ, ์ฆ‰ ์ŠคํŠธ๋ฆผ์˜ ์ŠคํŠธ๋ฆผ์„ ํ•˜๋‚˜์˜ ์ŠคํŠธ๋ฆผ์œผ๋กœ ํ•ฉ์น  ๋•Œ๋„ flatMap()์„ ์‚ฌ์šฉํ•œ๋‹ค.


    Stream<String> strStrm = Stream.of("abc", "def", "jkl");
    Stream<String> strStrm2 = Stream.of("ABC", "DEF", "JKL");
     
    Stream<Stream<String>> strmStrm = Stream.of(strStrm, strStrm2);


    ์ด ๋•Œ Stream<Stream<String>>์„ Stream<String>์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋ ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด map()๊ณผ flatMap()์„ ํ•จ๊ป˜ ์“ฐ๋ฉด ๋œ๋‹ค.


    Stream<String> strStream = strmStrm
    .map(s -> s.toArray(String[]::new))    // Stream<Stream<String>> -> Stream<String[]>
    .flatMap(Arrays::stream);    // Stream<String[]> -> Stream<String>


    toArray()๋Š” ์ŠคํŠธ๋ฆผ์„ ๋ฐฐ์—ด๋กœ ๋ณ€ํ™˜ํ•ด์„œ ๋ฐ˜ํ™˜ํ•˜๋Š”๋ฐ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์ง€์ •ํ•˜์ง€ ์•Š์œผ๋ฉด Object[]์„ ๋ฐ˜ํ™˜ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์œ„์™€ ๊ฐ™์ด ํŠน์ • ํƒ€์ž…์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ํ•ด๋‹น ํƒ€์ž…์˜ ์ƒ์„ฑ์ž๋ฅผ ์ง€์ •ํ•ด์ฃผ์–ด์•ผํ•œ๋‹ค.



    4. ์ตœ์ข…์—ฐ์‚ฐ


    ๋ฆฌ๋“€์‹ฑ - reduce()

    ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๋ฅผ ์ค„์—ฌ๋‚˜๊ฐ€๋ฉด์„œ ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ์ตœ์ข…๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ๊ทธ๋ž˜์„œ ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ํƒ€์ž…์ด BinaryOperator<T>์ด๋‹ค. ์ฒ˜์Œ ๋‘ ์š”์†Œ๋ฅผ ๊ฐ€์ง€๊ณ  ์—ฐ์‚ฐํ•˜๊ณ  ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋‹ค์Œ์š”์†Œ์™€ ์—ฐ์‚ฐํ•œ๋‹ค. ์ด ๊ณผ์ •์—์„œ ํ•˜๋‚˜์”ฉ ์š”์†Œ๋ฅผ ์†Œ๋ชจํ•˜๊ณ  ์ŠคํŠธ๋ฆผ์˜ ๋ชจ๋“  ์š”์†Œ๋ฅผ ์†Œ๋ชจํ•œ ํ›„์— ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.


    Optional<T> reduce(BinaryOperator<T> accumulator)


    ์—ฌ๊ธฐ์„œ Optional<T>์ด๋ž€ ์ตœ์ข… ์—ฐ์‚ฐ์˜ ๊ฒฐ๊ณผ ํƒ€์ž…์ด ์œ„์™€ ๊ฐ™์ด Optional์ธ ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋Š”๋ฐ ์ด๋Š” ์ง€๋„ค๋ฆญ ํด๋ž˜์Šค๋กœ "Tํƒ€์ž…์˜ ๊ฐ์ฒด"๋ฅผ ๊ฐ์‹ธ๋Š” ๋ž˜ํผ ํด๋ž˜์Šค์ด๋‹ค. ๊ทธ๋ž˜์„œ Optionalํƒ€์ž…์˜ ๊ฐ์ฒด์—๋Š” ๋ชจ๋“  ํƒ€์ž…์˜ ์ฐธ์กฐ๋ณ€์ˆ˜๋ฅผ ๋‹ด์„ ์ˆ˜ ์žˆ๋‹ค. (JDK1.8๋ถ€ํ„ฐ ์ถ”๊ฐ€๋จ)


    public final class Optional<T> {
        private final T value;    // T ํƒ€์ž…์˜ ์ฐธ์กฐ๋ณ€์ˆ˜
        ...
    }


    ์ตœ์ข… ์—ฐ์‚ฐ์˜ ๊ฒฐ๊ณผ๋ฅผ ๊ทธ๋ƒฅ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ Optional๊ฐ์ฒด์— ๋‹ด์•„์„œ ๋ฐ˜ํ™˜ํ•˜๋ฏ€๋กœ ๋ฐ˜ํ™˜๋œ ๊ฒฐ๊ณผ๊ฐ€ null์ธ์ง€ ๋งค๋ฒˆ if๋ฌธ์œผ๋กœ ์ฒดํฌํ•˜๋Š” ๋Œ€์‹  Optional์— ์ •์˜๋œ ๋ฉ”์„œ๋“œ๋ฅผ ํ™œ์šฉํ•ด์„œ ๊ฐ„๋‹จํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋กœ ์ธํ•ด ๋„ ์ฒดํฌ๋ฅผ ์œ„ํ•œ if๋ฌธ ์—†์ด๋„ NullPointerException์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š” ๋ณด๋‹ค ๊ฐ„๊ฒฐํ•˜๊ณ  ์•ˆ์ „ํ•œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅํ•ด์ง„๋‹ค.


    โ€ป ์ฐธ๊ณ  : Objectsํด๋ž˜์Šค์— isNull(), nonNull(), requireNonNull()๊ณผ ๊ฐ™์€ ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ๋Š” ๊ฒƒ๋„ ๋„ ์ฒดํฌ๋ฅผ ์œ„ํ•œ if๋ฌธ์„ ๋ฉ”์„œ๋“œ ์•ˆ์— ๋„ฃ์–ด์„œ ์ฝ”๋“œ์˜ ๋ณต์žก๋„๋ฅผ ๋‚ฎ์ถ”๊ธฐ ์œ„ํ•œ ๊ฒƒ.


    ๋‹ค์‹œ ๋Œ์•„์™€์„œ ์—ฐ์‚ฐ๊ฒฐ๊ณผ์˜ ์ดˆ๊ธฐ๊ฐ’์„ ๊ฐ–๋Š” reduce()๋„ ์žˆ๋Š”๋ฐ, ์ด ๋ฉ”์„œ๋“œ๋“ค์€ ์ดˆ๊ธฐ๊ฐ’๊ณผ ์ŠคํŠธ๋ฆผ์˜ ์ฒซ ๋ฒˆ์งธ ์š”์†Œ๋กœ ์—ฐ์‚ฐ์„ ์‹œ์ž‘ํ•œ๋‹ค. ์š”์†Œ๊ฐ€ ํ•˜๋‚˜๋„ ์—†๋Š” ๊ฒฝ์šฐ ์ดˆ๊ธฐ๊ฐ’์ด ๋ฐ˜ํ™˜๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ˜ํ™˜ ํƒ€์ž…์ด Optional<T>๊ฐ€ ์•„๋‹ˆ๋ผ T์ด๋‹ค.


    T reduce(T identity, Binaryoperator<T> accumulator)
    U reduce(U identity, BiFunction<U,T,U> accumulator, Binaryoperator<U> combiner)


    ์œ„์˜ ๋‘๋ฒˆ์งธ ๋ฉ”์„œ๋“œ์˜ ๋งˆ์ง€๋ง‰ ๋งค๊ฐœ๋ณ€์ˆ˜์ธ combiner๋Š” ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์— ์˜ํ•ด ์ฒ˜๋ฆฌ๋œ ๊ฒฐ๊ณผ๋ฅผ ํ•ฉ์น  ๋•Œ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ๊ฒƒ์ด๋‹ค.


    int count = intStream.reduce(0, (a,b) -> a+1);    // count()
    int sum = intStream.reduce(0, (a,b) -> a+b);    // sum()
    int max = intStream.reduce(0, (a,b) -> a>b ? a:b);    // max()
    int min = intStream.reduce(Integer.MIN_VALUE, (a,b) -> a


    min, max๋Š” ์ดˆ๊ธฐ๊ฐ’์ด ํ•„์š” ์—†๊ธฐ ๋•Œ๋ฌธ์— ์œ„์—์„œ ์–ธ๊ธ‰ํ•œ Optional<T>๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋” ๋‚ซ๋‹ค. ๋‹จ intStream์˜ ํƒ€์ž…์ด IntStream์ธ ๊ฒฝ์šฐ OptionalInt๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.


    T reduce(T identity, BinaryOperator<T> accumulator) {
        T a = identity;
     
        for (T b : stream)
            a = accumulator.apply(a, b);
     
        return a;
    }



    5. collect()


    ์ŠคํŠธ๋ฆผ์˜ ์ตœ์ข… ์—ฐ์‚ฐ ์ค‘ ๊ฐ€์žฅ ๋ณต์žกํ•˜๋ฉด์„œ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด collect()์ด๋‹ค.


    ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๋ฅผ ์ˆ˜์ง‘ํ•˜๊ธฐ ์œ„ํ•œ ๋ฉ”์„œ๋“œ๋กœ ์ˆ˜์ง‘ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์ •์˜๋˜์–ด ์žˆ์–ด์•ผ ํ•˜๋Š”๋ฐ ์ด ๋ฐฉ๋ฒ•์„ ์ •์˜ํ•œ ๊ฒƒ์ด ๋ฐ”๋กœ ์ปฌ๋ ‰ํ„ฐ(collector)์ด๋‹ค.


    ์ปฌ๋ ‰ํ„ฐ๋Š” Collector์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ๊ฒƒ์œผ๋กœ, ์ง์ ‘ ๊ตฌํ˜„ํ•  ์ˆ˜๋„ ์žˆ๊ณ  ๋ฏธ๋ฆฌ ์ž‘์„ฑ๋œ ๊ฒƒ์„ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๋‹ค. 


    collect() ์ŠคํŠธ๋ฆผ์˜ ์ตœ์ข…์—ฐ์‚ฐ, ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์ปฌ๋ ‰ํ„ฐ๋ฅผ ํ•„์š”๋กœ ํ•œ๋‹ค.

    Collector ์ธํ„ฐํŽ˜์ด์Šค, ์ปฌ๋ ‰ํ„ฐ๋Š” ์ด ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค.

    Collectors ํด๋ž˜์Šค, static๋ฉ”์„œ๋“œ๋กœ ๋ฏธ๋ฆฌ ์ž‘์„ฑ๋œ ์ปฌ๋ ‰ํ„ฐ๋ฅผ ์ œ๊ณตํ•œ๋‹ค.


    collect()๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ํƒ€์ž…์ด Collector์ธ๋ฐ ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ Collector๋ฅผ ๊ตฌํ˜„ํ•œ ํด๋ž˜์Šค์˜ ๊ฐ์ฒด์ด์–ด์•ผ ํ•œ๋‹ค๋Š” ๋œป์ด๋‹ค. ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋„˜์–ด์˜จ ๊ฐ์ฒด์— ๊ตฌํ˜„๋œ ๋ฐฉ๋ฒ•๋Œ€๋กœ ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๋ฅผ ์ˆ˜์ง‘ํ•œ๋‹ค.


    Object collect(Collector collector)
    Object collect(Supplier supplier, BiConsumer accumulator, BiConsumer combiner)


    ์•„๋ž˜์˜ collect()๋Š” ์ž˜ ์‚ฌ์šฉ๋˜์ง€ ์•Š์ง€๋งŒ , Collector์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜์ง€ ์•Š๊ณ  ๊ฐ„๋‹จํžˆ ๋žŒ๋‹ค์‹์œผ๋กœ ์ˆ˜์ง‘ํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋ฉด ํŽธ๋ฆฌํ•˜๋‹ค๊ณ  ํ•œ๋‹ค.


    ์ŠคํŠธ๋ฆผ์„ ์ปฌ๋ ‰์…˜๊ณผ ๋ฐฐ์—ด๋กœ ๋ณ€ํ™˜ - toList(), toSet(), toCollection(), toArray()

    ์ŠคํŠธ๋ฆผ์˜ ๋ชจ๋“  ์š”์†Œ๋ฅผ ์ปฌ๋ ‰์…˜์— ์ˆ˜์ง‘ํ•˜๋ ค๋ฉด, Collectorsํด๋ž˜์Šค์˜ toList()์™€ ๊ฐ™์€ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค. List๋‚˜ Set์ด ์•„๋‹Œ ํŠน์ • ์ปฌ๋ ‰์…˜์„ ์ง€์ •ํ•˜๋ ค๋ฉด, toCollection()์— ํ•ด๋‹น ์ปฌ๋ ‰์…˜์˜ ์ƒ์„ฑ์ž ์ฐธ์กฐ๋ฅผ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋„ฃ์–ด์ฃผ๋ฉด ๋œ๋‹ค.


    List<String> names = stuStream.map(Student::getName).collect(Collectors.toList());
    ArrayList<String> list = names.stream().collect(Collectors.toCollection(ArrayList::new));


    Map์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” ํ‚ค์™€ ๊ฐ’์˜ ์Œ์œผ๋กœ ์ €์žฅํ•ด์•ผ ํ•˜๋ฏ€๋กœ ๊ฐ์ฒด์˜ ์–ด๋–ค ํ•„๋“œ๋ฅผ ํ‚ค๋กœ ์‚ฌ์šฉํ• ์ง€, ๊ฐ’์œผ๋กœ ์‚ฌ์šฉํ• ์ง€๋ฅผ ์ง€์ •ํ•ด์ฃผ์–ด์•ผํ•œ๋‹ค.



    ๋ฆฌ๋“€์‹ฑ, ํ†ต๊ณ„ - counting(), summmingInt(), averagingInt(), maxBy(), minBy(), reducing()

    ์ตœ์ข…์—ฐ์‚ฐ์— ์ด๋ฏธ ์กด์žฌํ•˜๋Š” ์—ฐ์‚ฐ๋“ค์„ collect()๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.


    // int sum = intStream.reduce(0, (a,b) -> a+b);
     
    int sum = intStream.boxed().collect(reducing(0, (a,b) -> a+b));


    boxed()์„ ์‚ฌ์šฉํ•œ ์ด์œ ๋Š” IntStream์—๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ 3๊ฐœ์งœ๋ฆฌ collect()๋งŒ ์ •์˜๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ•์‹ฑ์„ ํ†ตํ•ด Stream<Integer>๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ์–ด์•ผ ๋งค๊ฐœ๋ณ€์ˆ˜ 1๊ฐœ์งœ๋ฆฌ collect()๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.


    ๋ฌธ์ž์—ด ๊ฒฐํ•ฉ - joining()

    ๋ฌธ์ž์—ด ์ŠคํŠธ๋ฆผ์˜ ๋ชจ๋“  ์š”์†Œ๋ฅผ ํ•˜๋‚˜์˜ ๋ฌธ์ž์—ด๋กœ ์—ฐ๊ฒฐํ•ด์„œ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ๊ตฌ๋ถ„์ž๋ฅผ ์ง€์ •ํ•ด์ค„ ์ˆ˜๋„ ์žˆ๊ณ , ์ ‘๋‘์‚ฌ์™€ ์ ‘๋ฏธ์‚ฌ๋„ ์ง€์ • ๊ฐ€๋Šฅํ•˜๋‹ค. ๋Œ€์‹  ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๊ฐ€ String์ด๋‚˜ StringBuffer์ฒ˜๋Ÿผ ChareSequence์˜ ์ž์†์ธ ๊ฒฝ์šฐ๋งŒ ๊ฐ€๋Šฅํ•˜๋‹ค. ๋”ฐ๋ผ์„œ ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๊ฐ€ ๋ฌธ์ž์—ด์ด ์•„๋‹Œ ๊ฒฝ์šฐ map()์„ ์ด์šฉํ•ด์„œ ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๋ฅผ ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ํ•ด์„œ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค.


    String studentNames = stuStream.map(Student::getName).collect(joining());
    String studentNames = stuStream.map(Student::getName).collect(joining(","));
    String studentNames = stuStream.map(Student::getName).collect(joining(",", "[", "]"));


    map()์—†์ด ์ŠคํŠธ๋ฆผ์— ๋ฐ”๋กœ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ์— toString()์„ ํ˜ธ์ถœํ•œ ๊ฒฐ๊ณผ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.



    ๊ทธ๋ฃนํ™”์™€ ๋ถ„ํ•  - groupingBy(), partitioningBy()

    ์—ฌ๊ธฐ๋ถ€ํ„ฐ collect()์˜ ์œ ์šฉํ•จ์„ ์•Œ ์ˆ˜ ์žˆ๋Š” ๋ถ€๋ถ„์ด๋‹ค. ๊ทธ๋ฃนํ™”๋Š” ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๋ฅผ ํŠน์ • ๊ธฐ์ค€์œผ๋กœ ๊ทธ๋ฃนํ™”ํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•˜๊ณ  ๋ถ„ํ• ์€ ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๋ฅผ ๋‘ ๊ฐ€์ง€ ์ง€์ •๋œ ์กฐ๊ฑด์— ์ผ์น˜ํ•˜๋Š” ๊ทธ๋ฃน๊ณผ ์ผ์น˜ํ•˜์ง€ ์•Š๋Š” ๊ทธ๋ฃน์œผ๋กœ์˜ ๋ถ„ํ• ์„ ์˜๋ฏธํ•œ๋‹ค.


    groupingBy()๋Š” ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๋ฅผ Function์œผ๋กœ, partitioningBy()๋Š” Predicate๋กœ ๋ถ„๋ฅ˜ํ•œ๋‹ค.


    Collector groupingBy(Function classifier)
    Collector groupingBy(Function classifier, Collector downstream)
    Collector groupingBy(Function classifier, Supplier mapFactory, Collector downstream)


    Collector partitioningBy(Predicate predicate)
    Collector partitioningBy(Predicate predicate, Collector downstream)


    ๋ฉ”์„œ๋“œ ์ •์˜๋ฅผ ๋ดค์„ ๋•Œ ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ์ฐจ์ด ๋ง๊ณ ๋Š” ๋™์ผํ•˜๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค. ์ŠคํŠธ๋ฆผ์„ ๋‘ ๊ฐœ์˜ ๊ทธ๋ฃน์œผ๋กœ ๋‚˜๋ˆ ์•ผ ํ•œ๋‹ค๋ฉด ๋‹น์—ฐํžˆ partitioningBy()๋กœ ๋ถ„ํ• ํ•˜๋Š” ๊ฒƒ์ด ๋” ๋น ๋ฅด๋‹ค. ๊ทธ ์™ธ์—๋Š” groupingBy()๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค. ๋‘๊ฐœ ๋‹ค ๊ฒฐ๊ณผ๋Š” Map์— ๋‹ด๊ฒจ ๋ฐ˜ํ™˜๋œ๋‹ค.


    ์˜ˆ์‹œ์— ์‚ฌ์šฉ๋  Studentํด๋ž˜์Šค


    class Student {
        String name;    // ์ด๋ฆ„
        boolean isMale; // ์„ฑ๋ณ„
        int hak;        // ํ•™๋…„
        int ban;        // ๋ฐ˜
        int score;        // ์ ์ˆ˜
        Student(String name, boolean isMale, int hak, int ban, int score) { 
            this.name    = name;
            this.isMale    = isMale;
            this.hak    = hak;
            this.ban    = ban;
            this.score  = score;
        }
        String    getName()  { return name;}
        boolean isMale()   { return isMale;}
        int        getHak()   { return hak;}
        int        getBan()   { return ban;}
        int        getScore() { return score;}
        public String toString() { 
            return String.format("[%s, %s, %dํ•™๋…„ %d๋ฐ˜, %3d์ ]", name, isMale ? "๋‚จ":"์—ฌ", hak, ban, score); 
        }
        enum Level {    // ์„ฑ์ ์„ ์ƒ, ์ค‘, ํ•˜ 3๋‹จ๊ณ„๋กœ ๋ถ„๋ฅ˜
            HIGH, MID, LOW
        }
    }    // class Student


    ๊ทธ๋ฆฌ๊ณ  ์ŠคํŠธ๋ฆผ stuStudent๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ์š”์†Œ๋“ค์˜ค ์ด๋ฃจ์–ด์ ธ ์žˆ๋‹ค๊ณ  ํ•˜์ž.


    Stream<Student> stuStream = Stream.of(
        new Student("๋‚˜์ž๋ฐ”", true,  1, 1, 300),    
        new Student("๊น€์ง€๋ฏธ", false, 1, 1, 250),    
        new Student("๊น€์ž๋ฐ”", true,  1, 1, 200),    
        new Student("์ด์ง€๋ฏธ", false, 1, 2, 150),    
        new Student("๋‚จ์ž๋ฐ”", true,  1, 2, 100),    
        new Student("์•ˆ์ง€๋ฏธ", false, 1, 2,  50),    
        new Student("ํ™ฉ์ง€๋ฏธ", false, 1, 3, 100),    
        new Student("๊ฐ•์ง€๋ฏธ", false, 1, 3, 150),    
        new Student("์ด์ž๋ฐ”", true,  1, 3, 200),    
        new Student("๋‚˜์ž๋ฐ”", true,  2, 1, 300),    
        new Student("๊น€์ง€๋ฏธ", false, 2, 1, 250),    
        new Student("๊น€์ž๋ฐ”", true,  2, 1, 200),    
        new Student("์ด์ง€๋ฏธ", false, 2, 2, 150),    
        new Student("๋‚จ์ž๋ฐ”", true,  2, 2, 100),    
        new Student("์•ˆ์ง€๋ฏธ", false, 2, 2,  50),    
        new Student("ํ™ฉ์ง€๋ฏธ", false, 2, 3, 100),    
        new Student("๊ฐ•์ง€๋ฏธ", false, 2, 3, 150),    
        new Student("์ด์ž๋ฐ”", true,  2, 3, 200)
    );


    partitioningBy()์— ์˜ํ•œ ๋ถ„๋ฅ˜

    ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ๋ถ„ํ• ์€ ํ•™์ƒ์ •๋ณด๊ฐ€ ์žˆ๋‹ค๊ณ  ํ–ˆ์„ ๋•Œ ํ•™์ƒ๋“ค์„ ์„ฑ๋ณ„๋กœ ๋‚˜๋ˆ„๋Š” ๊ฒƒ์ด๋‹ค.


    Map<Boolean, List<Student>> stuBySex = stuStream.collect(partitioningBy(Student::isMale));
     
    List<Student> maleStudent = stuBySex.get(true);
    List<Student> femaleStudent = stuBySex.get(false);


    counting()์„ ์ถ”๊ฐ€ํ•ด์„œ ๊ฐ๊ฐ ํ•™์ƒ์˜ ์ˆ˜๋ฅผ ๊ตฌํ• ์ˆ˜๋„ ์žˆ๋‹ค.


    Map<Boolean, List<Student>> stuNumBySex = stuStream
                                .collect(partitioningBy(Student::isMale), counting());
     
    System.out.println("๋‚จํ•™์ƒ ์ˆ˜ : " + stuNumBySex.get(true));    // 8
    System.out.println("์—ฌํ•™์ƒ ์ˆ˜ : " + stuNumBySex.get(false));    // 10


    groupingBy()์— ์˜ํ•œ ๋ถ„๋ฅ˜

    partitioningBy()์™€ ์‚ฌ์šฉ๋ฒ•์€ ๊ฐ™๋‹ค. ๋ฐ˜ ๋ณ„๋กœ ๊ทธ๋ฃน์ง€์–ด Map์— ์ €์žฅํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์ž.


    Map<Boolean, List<Student>> stuByBan = stuStream
                                .collect(groupingBy(Student::getBan));


    groupingBy()๋กœ ๊ทธ๋ฃนํ™”๋ฅผ ํ•˜๋ฉด ๊ธฐ๋ณธ์ ์œผ๋กœ List<T>์— ๋‹ด๋Š”๋‹ค. ๊ทธ๋ž˜์„œ ์œ„์˜ ๋ฌธ์žฅ์€ toList()๊ฐ€ ์ƒ๋žต๋œ ํ˜•ํƒœ๋‹ค.


    Map<Boolean, List<Student>> stuByBan = stuStream
                                .collect(groupingBy(Student::getBan), toList());


    toList() ๋Œ€์‹  toSet()์ด๋‚˜ toCollection(HashSet::new) ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค. ๋‹จ Map์˜ ์ง€๋„ค๋ฆญํƒ€์ž…๋„ ๊ทธ์— ๋งž์ถฐ ๋ฐ”๊พธ์–ด์•ผ ํ•œ๋‹ค.



    ์ฐธ์กฐ : ์ž๋ฐ”์˜ ์ •์„ 3rd



    ๋ฐ˜์‘ํ˜•