# Stream

# 1. Stream ์†Œ๊ฐœ

์—ฐ์†๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ์˜คํผ๋ ˆ์ด์…˜๋“ค์˜ ๋ชจ์Œ

๐Ÿ”ผ ์ด๋ฏธ์ง€ ์ถœ์ฒ˜ (opens new window)

  • stream ์€ ์ปจ๋ฒ ์ด์–ด ๋ฒจํŠธ์™€ ๋น„์Šทํ•˜๋‹ค.
  • ์ปจ๋ฒ ์ด์–ด ๋ฒจํŠธ์— ๋–ก์กฐ๊ฐ(๋ฐ์ดํ„ฐ)๋“ค์„ ํ˜๋ ค๋ณด๋‚ด๋ฉด์„œ ๋ฐ˜์ฃฝ์„ ํ•˜๊ณ , ์•™๊ธˆ์„ ์Œ“๊ณ  (map), ๋ถˆ๋Ÿ‰ํ’ˆ์€ ๋นผ๊ณ (filter) ํฌ์žฅ์„ ํ•ด์„œ(collect) ๋‚ด๋ณด๋‚ธ๋‹ค.

# 1-1. Stream์˜ ํŠน์ง•

  1. ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ด๊ณ  ์žˆ๋Š” ์ €์žฅ์†Œ(์ปฌ๋ ‰์…˜)์ด ์•„๋‹ˆ๋‹ค.
  2. Functional in nature.
  • stream์€ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐ์ดํ„ฐ ์†Œ์Šค๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • ์ฆ‰ ๋‚ด๊ฐ€ A๋ผ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •ํ•œ๋‹ค๊ณ  ํ•ด์„œ ์›๋ณธ๋ฐ์ดํ„ฐ๊ฐ€ ์ˆ˜์ •๋˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋ผ๋Š” ์˜๋ฏธ!
public static void main(String[] args) {
    List<String> names = new ArrayList<>();
    names.add("yj");

    Stream<String> stringStream = names.stream().map(String::toUpperCase);
    names.forEach(System.out::println);
}

/*
[์‹คํ–‰ ๊ฒฐ๊ณผ]
yj
*/
  1. ์ŠคํŠธ๋ฆผ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐ์ดํ„ฐ๋Š” ์˜ค์ง ํ•œ ๋ฒˆ๋งŒ ์ฒ˜๋ฆฌํ•œ๋‹ค.

    • ์ปจ๋ฒ ์ด์–ด ๋ฒจํŠธ์—์„œ ๋ฐ์ดํ„ฐ๋“ค์ด ํ•œ ๋ฒˆ ์ง€๋‚˜๊ฐ„๋’ค ๋‹ค์‹œ ๋Œ์•„์˜ค์ง€ ์•Š์Œ.
  2. ๋ฌด์ œํ•œ์˜ ๋ฐ์ดํ„ฐ์ผ์ˆ˜๋„ ์žˆ๋‹ค.

    • ์ด ๊ฒฝ์šฐ Short Circuit ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ œํ•œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  3. ์ค‘๊ฐœ ์˜คํผ๋ ˆ์ด์…˜์€ ๊ทผ๋ณธ์ ์œผ๋กœ lazyํ•˜๋‹ค.

    • ์—ฌ๋Ÿฌ ์ค‘๊ฐœ ์˜คํผ๋ ˆ์ด์…˜๋“ค์„ ๋ฉ”์†Œ๋“œ ์ฒด์ด๋‹์„ ํ•˜๋”๋ผ๋„ ๊ทธ ์‹œ์ ์—์„œ ์ฝ”๋“œ๊ฐ€ ์ˆ˜ํ–‰๋˜์ง€๋Š” ์•Š์Œ.
    • ๋ชจ๋“  ์ค‘๊ฐœ ์˜คํผ๋ ˆ์ด์…˜์˜ ์‹คํ–‰ ์‹œ๊ธฐ๋Š” ์ข…๋ฃŒ ์˜คํผ๋ ˆ์ด์…˜์˜ ํ˜ธ์ถœ์‹œ์ !
    • ๋”ฐ๋ผ์„œ ์ค‘๊ฐœ ์˜คํผ๋ ˆ์ด์…˜์˜ ๋ฐ˜ํ™˜ํƒ€์ž…์€ ๋˜ ๋‹ค๋ฅธ stream
public static void main(String[] args) {
    List<String> names = new ArrayList<>();
    names.add("yj");
    names.add("youngjun");
    names.add("spring");
    names.add("java8");

    Stream<String> stringStream = names.stream().map(s->{
        System.out.println(s);
        return s.toUpperCase();
    });
    names.forEach(System.out::println);
}
  • names.stream().map(s->{ ... });
    • stream์˜ ์ค‘๊ฐœ์˜คํผ๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ˆœ๊ฐ„์—๋Š” ์ฝ”๋“œ๊ฐ€ ์ˆ˜ํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ์ค‘๊ฐœ์˜คํผ๋ ˆ์ด์…˜ map ์•ˆ์— ์žˆ๋Š” ์ถœ๋ ฅ๋ฌธ์ด ์ˆ˜ํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ˆ˜ํ–‰์‹œํ‚ค๊ธฐ์œ„ํ•ด์„œ๋Š” ์ŠคํŠธ๋ฆผ ํŒŒ์ดํ”„๋ผ์ธ์„ ์ •์˜ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.
  1. ์†์‰ฝ๊ฒŒ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ์ง€๊ธˆ๊ป ๊ตฌํ˜„ํ•œ ๋Œ€๋ถ€๋ถ„์˜ ์˜ˆ์ œ์ฝ”๋“œ์—์„œ๋Š” forEach๋ฅผ ํ†ตํ•ด ๋‹จ์ˆœํ•œ ๋ฐ˜๋ณต ์ถœ๋ ฅํ–ˆ๋‹ค.
    • ๊ทธ๋Ÿฐ๋ฐ ์—ฌ๊ธฐ์„œ ์กฐ๊ฑด๋ฌธ์ด ์ถ”๊ฐ€๋˜๊ณ  ๋กœ์ง์ด ์ถ”๊ฐ€๋ ์ˆ˜๋ก stream์„ ํ†ตํ•ด ๊ตฌํ˜„ํ• ์ˆ˜๋ก ๊ฐ„๊ฒฐํ•ด์ง€๊ธฐ ๋•Œ๋ฌธ์— stream์„ ์“ฐ๋Š” ๊ฒƒ ์™ธ์—๋„ ๊ธฐ๋ณธ์ ์ธ for๋ฌธ์ด๋‚˜ for-of๋ฌธ์œผ๋กœ๋Š” ๋กœ์ง๋“ค์„ ๋ณ‘๋ ฌ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š”๊ฒŒ ์‰ฝ์ง€ ์•Š๋‹ค.
    • ํ•˜์ง€๋งŒ, parallelStream()์„ ์ด์šฉํ•˜๋ฉด ์†์‰ฝ๊ฒŒ ๋ณ‘๋ ฌ์ฒ˜๋ฆฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ๋‹ค.
public static void main(String[] args) {
    List<String> names = new ArrayList<>();
    names.add("yj");
    names.add("youngjun");
    names.add("spring");
    names.add("java8");

    List<String> collect = names.parallelStream().map(s->{
        System.out.println(s + " " + Thread.currentThread().getName());
        return s.toUpperCase();
    }).collect(Collectors.toList());

    collect.forEach(System.out::println);
}
  • ์ฃผ์˜ํ•  ์ 
    • ์‚ฌ์‹ค์ƒ ๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ๋ฅผ ์ด์šฉํ•œ๋‹ค๊ณ  ์„ฑ๋Šฅ์ด ๋ฌด์กฐ๊ฑด ์ข‹์€ ๊ฒƒ์€ ์•„๋‹ˆ๋ฉฐ ์˜คํžˆ๋ ค ๋Š๋ ค์งˆ ๊ฐ€๋Šฅ์„ฑ์ด ๋” ๋†’๋‹ค.
    • ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ ๊ทธ๋ƒฅ stream์„ ์“ฐ๋Š”๊ฒŒ ๋‚˜์œผ๋ฉฐ ์ •๋ง ํฐ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃฐ ๋•Œ ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ ํ›„ parallelStream()์„ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

# 2. ์ŠคํŠธ๋ฆผ ํŒŒ์ดํ”„๋ผ์ธ

์ŠคํŠธ๋ฆผ์ด๋ผ๋Š” ์ปจ๋ฒ ์ด์–ด ๋ฒจํŠธ์— 0๊ฐœ ํ˜น์€ ๋‹ค์ˆ˜์˜ ์ค‘๊ฐœ ์˜คํผ๋ ˆ์ดํ„ฐ(intermediate operation)๊ณผ ํ•œ๊ฐœ์˜ ์ข…๋ฃŒ ์˜คํผ๋ ˆ์ด์…˜(terminal operation)์œผ๋กœ ๊ตฌ์„ฑ. ์ด ์ŠคํŠธ๋ฆผ์€ ๋ฐ˜๋“œ์‹œ ํ•˜๋‚˜์˜ ์ข…๋ฃŒ ์˜คํผ๋ ˆ์ด์…˜์ด ์žˆ์–ด์•ผ ํ•˜๋ฉฐ, ๋งŒ์•ฝ ์ข…๋ฃŒ ์˜คํผ๋ ˆ์ด์…˜์ด ์—†๋‹ค๋ฉด, ์ŠคํŠธ๋ฆผ์€ ์กด์žฌํ•˜์ง€๋งŒ ์ฝ”๋“œ ์ˆ˜ํ–‰์€ ๋˜์ง€ ์•Š๋Š”๋‹ค.

์ŠคํŠธ๋ฆผ์˜ ๋ฐ์ดํ„ฐ ์†Œ์Šค๋Š” ์˜ค์ง ํ„ฐ๋ฏธ๋„ ์˜คํผ๋ ˆ์ด์…˜์„ ์‹คํ–‰ํ•  ๋•Œ์—๋งŒ ์ฒ˜๋ฆฌํ•œ๋‹ค!

# 2-1. ์ค‘๊ฐœ ์˜คํผ๋ ˆ์ด์…˜

Stream์„ ๋ฆฌํ„ดํ•œ๋‹ค!

  • Stateless/Stateful ์˜คํผ๋ ˆ์ด์…˜์œผ๋กœ ๋” ์ƒ์„ธํ•˜๊ฒŒ ๊ตฌ๋ถ„ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.
  • ๋Œ€๋ถ€๋ถ„ Stateless์ง€๋งŒ distinct๋‚˜ sorted์ฒ˜๋Ÿผ ์ด์ „ ์†Œ์Šค ๋ฐ์ดํ„ฐ๋ฅผ ์ฐธ์กฐํ•ด์•ผ ํ•˜๋Š” ์˜คํผ๋ ˆ์ด์…˜์€ Stateful ์˜คํผ๋ ˆ์ด์…˜.
  • filter, map, limit, skip, sorted, ...

# 2-2. ์ข…๋ฃŒ ์˜คํผ๋ ˆ์ด์…˜

Stream์„ ๋ฆฌํ„ดํ•˜์ง€ ์•Š๋Š”๋‹ค!

  • collect, allmatch, count, forEach, min, max...
public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("hansol");
        names.add("toby");
        names.add("catsbi");
        names.add("mijeong");

        List<String> collect = names.stream().map(s -> {
            System.out.println(s);
            return s.toUpperCase();
        }).collect(Collectors.toList());
        collect.forEach(System.out::println);
    }

# 3. Stream API

stream api๋ฅผ ์˜ˆ์ œ๋ฅผ ํ†ตํ•ด ์•Œ์•„๋ณด์ž.

  1. ๊ฑธ๋Ÿฌ๋‚ด๊ธฐ
    • Filter(Predicate)
    • stream์—์„œ ํŠน์ • ์กฐ๊ฑด(Predicate)์„ ๋งŒ์กฑํ•˜๋Š” ์—˜๋ฆฌ๋จผํŠธ๋งŒ ์ƒˆ๋กœ์šด ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ฐ˜ํ™˜
  2. ๋ณ€๊ฒฝํ•˜๊ธฐ
    • Map(Function) ๋˜๋Š” FlatMap(Function)
    • ๊ฐ๊ฐ์˜ ์š”์†Œ์—์„œ ํŠน์ •์š”์†Œ๋งŒ ๊บผ๋‚ด๊ฑฐ๋‚˜ ํ˜น์€ ๋ณ€๊ฒฝํ•˜์—ฌ ์ƒˆ๋กœ์šด ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ฐ˜ํ™˜
    • flatMap์˜ ๊ฒฝ์šฐ Array๋‚˜ Object๋กœ ๋ž˜ํ•‘๋˜์–ด์žˆ๋Š” ๋‚ด์šฉ๋ฌผ์„ ๊บผ๋‚ด์–ด ํ•˜๋‚˜๋กœ ํ•ฉ์นœ ์ŠคํŠธ๋ฆผ์œผ๋กœ ์ƒ์„ฑ ํ›„ ๋ฐ˜ํ™˜
  3. ์ƒ์„ฑํ•˜๊ธฐ
    • generate(Supplier) ๋˜๋Š” Iterator(T seed, UnaryOperator)
    • seed ๋กœ๋ถ€ํ„ฐ UnaryOperator ์„ ๋ฌด์ œํ•œ์œผ๋กœ ๋ฐ˜๋ณตํ•˜๋Š” ์ŠคํŠธ๋ฆผ ๋ฐ˜ํ™˜
    • ex: ๋žœ๋ค ๋ฌด์ œํ•œ ์ŠคํŠธ๋ฆผ
  4. ์ŠคํŠธ๋ฆผ์— ์žˆ๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ํŠน์ • ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๋Š”์ง€ ํ™•์ธ
    • anyMatch(), allMatch(), nonMatch()
    • ์ŠคํŠธ๋ฆผ์˜ ์—˜๋ฆฌ๋จผํŠธ๋ฅผ ๋Œ๋ฉฐ ํŠน์ • ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๋Š”์ง€ ํ™•์ธ
    • ex1: ์ŠคํŠธ๋ฆผ์˜ ์žˆ๋Š” ๋ชจ๋“  ๊ฐ’์ด 10๋ณด๋‹ค ์ž‘์€์ง€ ํ™•์ธ(allMatch)
    • ex2: ์ŠคํŠธ๋ฆผ์˜ ์ œ๋ชฉ ์ค‘ "Test"๊ฐ€ ๋“ค์–ด๊ฐ€๋Š” ์ œ๋ชฉ์ด ์žˆ๋Š”์ง€ ํ™•์ธ(anyMatch)
  5. ๊ฐœ์ˆ˜ ์„ธ๊ธฐ
    • count()
    • ex1: 10๋ณด๋‹ค ์ž‘์€ ์ˆ˜์˜ ๊ฐฏ์ˆ˜๋ฅผ ์„ผ๋‹ค.
  6. ์ŠคํŠธ๋ฆผ์„ ๋ฐ์ดํ„ฐ ํ•˜๋‚˜๋กœ ๋ญ‰์น˜๊ธฐ
    • reduce(identity, ByFunction), collect(), sum(), max()
    • ex1: ๋ชจ๋“  ์ˆซ์ž ํ•ฉ ๊ตฌํ•˜๊ธฐ
    • ex2: ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ํ•˜๋‚˜์˜ ๋ฆฌ์ŠคํŠธ ํ˜น์€ Set์— ์˜ฎ๊ฒจ ๋‹ด๊ธฐ
public static void main(String[] args) {
    List<OnlineClass> springClasses = new ArrayList<>();
    springClasses.add(new OnlineClass(1, "spring boot", true));
    springClasses.add(new OnlineClass(2, "spring data jpa", true));
    springClasses.add(new OnlineClass(3, "spring mvc", false));
    springClasses.add(new OnlineClass(4, "spring core", false));
    springClasses.add(new OnlineClass(5, "rest api development", false));

    List<OnlineClass> javaClasses = new ArrayList<>();
    javaClasses.add(new OnlineClass(6, "The Java, Test", true));
    javaClasses.add(new OnlineClass(7, "The Java, Code mainpulation", true));
    javaClasses.add(new OnlineClass(8, "The Java 8 to 11", false));

    List<List<OnlineClass>> keesunEvents = new ArrayList<>();
    keesunEvents.add(springClasses);
    keesunEvents.add(javaClasses);


    System.out.println("spring์œผ๋กœ ์‹œ์ž‘ํ•˜๋Š” ์ˆ˜์—…");
    springClasses.stream()
            .filter(oc -> oc.getTitle().startsWith("spring"))
            .forEach(System.out::println);

    System.out.println("closed๋˜์ง€ ์•Š์€ ์ˆ˜์—…");
    springClasses.stream()
            .filter(Predicate.not(OnlineClass::isClosed))
            .forEach(System.out::println);

    System.out.println("์ˆ˜์—… ์ด๋ฆ„๋งŒ ๋ชจ์•„์„œ ์ŠคํŠธ๋ฆผ ๋งŒ๋“ค๊ธฐ");
    Stream<String> springTitleStream = springClasses.stream()
            .map(OnlineClass::getTitle);
    springTitleStream.forEach(System.out::println);

    System.out.println("๋‘ ์ˆ˜์—… ๋ชฉ๋ก์— ๋“ค์–ด์žˆ๋Š” ๋ชจ๋“  ์ˆ˜์—… ์•„์ด๋”” ์ถœ๋ ฅ");
    keesunEvents.stream()
            .flatMap(Collection::stream)
            .forEach(oc-> System.out.println(oc.getId()));

    System.out.println("10๋ถ€ํ„ฐ 1์”ฉ ์ฆ๊ฐ€ํ•˜๋Š” ๋ฌด์ œํ•œ ์ŠคํŠธ๋ฆผ ์ค‘์—์„œ ์•ž์— 10๊ฐœ ๋นผ๊ณ  ์ตœ๋Œ€ 10๊ฐœ ๊นŒ์ง€๋งŒ");
    Stream.iterate(10, i -> i+1)
            .skip(10)
            .limit(10)
            .forEach(System.out::println);

    System.out.println("์ž๋ฐ” ์ˆ˜์—… ์ค‘์— Test๊ฐ€ ๋“ค์–ด์žˆ๋Š” ์ˆ˜์—…์ด ์žˆ๋Š”์ง€ ํ™•์ธ");
    boolean test = javaClasses.stream().
            anyMatch(oc -> oc.getTitle().contains("Test"));
    System.out.println(test);

    System.out.println("์Šคํ”„๋ง ์ˆ˜์—… ์ค‘์— ์ œ๋ชฉ์ด spring์ด ๋“ค์–ด๊ฐ„ ์ œ๋ชฉ๋งŒ ๋ชจ์•„์„œ List๋กœ ๋งŒ๋“ค๊ธฐ");
    List<String> spring = springClasses.stream()
            .filter(oc -> oc.getTitle().contains("spring"))
            .map(OnlineClass::getTitle)
            .collect(Collectors.toList());
    spring.forEach(System.out::println);
}

# Reference

Last Updated: 6/18/2023, 2:13:15 PM