DevLog ๐Ÿ˜ถ

[๋ชจ๋˜์ž๋ฐ”์ธ์•ก์…˜] ์ŠคํŠธ๋ฆผ์„ ํ™œ์šฉํ•ด๋ณด์ž! - ์ŠคํŠธ๋ฆผ ๋ฉ”์„œ๋“œ ๋ณธ๋ฌธ

๐Ÿ“–/Modern Java in Action

[๋ชจ๋˜์ž๋ฐ”์ธ์•ก์…˜] ์ŠคํŠธ๋ฆผ์„ ํ™œ์šฉํ•ด๋ณด์ž! - ์ŠคํŠธ๋ฆผ ๋ฉ”์„œ๋“œ

dolmeng2 2023. 4. 7. 16:02

๐Ÿ’ฌ ๋ชจ๋˜ ์ž๋ฐ” ์ธ ์•ก์…˜ ์ฑ•ํ„ฐ 5์„ ์ฝ๊ณ  ์ •๋ฆฌํ•œ ๊ธ€์ž…๋‹ˆ๋‹ค.

 


โœ”๏ธ ํ•„ํ„ฐ๋ง

๐ŸŒฑ ํ”„๋ฆฌ๋””์ผ€์ดํŠธ๋กœ ํ•„ํ„ฐ๋งํ•˜๊ธฐ

List<Crew> crews = new ArrayList<>();

List<Crew> backendCrews = crews.stream()
        // ๋žŒ๋‹ค์˜ ์ธ์ˆ˜(๋ฐฑ์—”๋“œ ํฌ๋ฃจ)๋ฅผ ๋ฐ›์•„ ํŠน์ • ์š”์†Œ๋ฅผ ์„ ํƒํ•œ๋‹ค.
        .filter(crew -> crew.getCourse() == Course.BACKEND)
        .collect(Collectors.toUnmodifiableList());

์ง€๋‚œ ์ฑ•ํ„ฐ์—์„œ ์‚ฌ์šฉํ–ˆ๋˜ ์˜ˆ์ œ์ด๋‹ค.

์—ฌ๊ธฐ์„œ filter ๋ฉ”์„œ๋“œ๋Š” Predicate<T>๋ฅผ ์ธ์ˆ˜๋กœ ๋ฐ›์•„์„œ, ํ•ด๋‹น ํ”„๋ฆฌ๋””์ผ€์ดํŠธ์™€ ์ผ์น˜ํ•˜๋Š” ๋ชจ๋“  ์š”์†Œ๋ฅผ ํฌํ•จํ•˜๋Š” ์ŠคํŠธ๋ฆผ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

 

 

๐ŸŒฑ ๊ณ ์œ  ์š”์†Œ ํ•„ํ„ฐ๋ง

distinct ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ค‘๋ณต์„ ์ œ๊ฑฐํ•œ ์š”์†Œ๋กœ ์ด๋ฃจ์–ด์ง„ ์ŠคํŠธ๋ฆผ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

์ด๋•Œ, ์ค‘๋ณต ์—ฌ๋ถ€๋Š” hashCode์™€ equals๋ฅผ ํ†ตํ•ด ๊ฒฐ์ •๋œ๋‹ค. (์žฌ์ •์˜ ํ•„์š”!)

List<Crew> crews = new ArrayList<>();
List<Crew> backendCrews = crews.stream()
        .filter(crew -> crew.getCourse() == Course.BACKEND)
        .distinct()
        .collect(Collectors.toUnmodifiableList());

 


โœ”๏ธ ์ŠคํŠธ๋ฆผ ์Šฌ๋ผ์ด์‹ฑ

๐ŸŒฑ takeWhile

์ž๋ฐ”9์—์„œ๋Š” takeWhile, dropWhile์ด๋ผ๋Š” ๊ฒƒ์„ ํ†ตํ•ด์„œ ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๋ฅผ ํšจ๊ณผ์ ์œผ๋กœ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋„๋ก ์ง€์›ํ•œ๋‹ค.

List<Crew> crews = new ArrayList<>();
List<Crew> backendCrews = crews.stream()
        .filter(crew -> crew.getAge() <= 25)
        .collect(Collectors.toUnmodifiableList());

์šฐ์„ , ์•ž์„œ ๋งํ•œ filter๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด ์ „์ฒด ์ŠคํŠธ๋ฆผ์„ ๋ฐ˜๋ณตํ•˜๋ฉด์„œ ๊ฐ๊ฐ์˜ ์š”์†Œ์— Predicate<T>๋ฅผ ์ ์šฉํ•œ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜, ๋งŒ์•ฝ ๋Œ€์ƒ ๋ฆฌ์ŠคํŠธ๊ฐ€ ์ •๋ ฌ๋˜์–ด ์žˆ๋‹ค๋ฉด ์–ด๋–จ๊นŒ?

predicate๋ฅผ ๋งŒ์กฑํ•˜๋Š” ์ง€์ ๊นŒ์ง€ ํ™•์ธํ•˜๊ณ  (์—ฌ๊ธฐ์„œ๋Š” ๋‚˜์ด๊ฐ€ 25์‚ด ์ดํ•˜) ๊ทธ ๋’ค๋กœ๋Š” ๋ณด์ง€ ์•Š์•„๋„ ๋˜๋Š” ๊ฒƒ์ด๋‹ค.

 

์ด๋Ÿฌํ•œ ์—ฐ์‚ฐ์„ takeWhile์„ ํ™œ์šฉํ•˜๋ฉด ์‰ฝ๊ฒŒ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

List<Crew> crews = new ArrayList<>();
List<Crew> backendCrews = crews.stream()
        .takeWhile(crew -> crew.getAge() <= 25)
        .collect(Collectors.toUnmodifiableList());

takeWhile์€ ์กฐ๊ฑด์— ๋Œ€ํ•ด ์ฐธ์ด ์•„๋‹ ๊ฒฝ์šฐ ๋ฐ”๋กœ ๊ฑฐ๊ธฐ์„œ ๋ฉˆ์ถ”๊ฒŒ ๋œ๋‹ค.

์ฆ‰, ํ•ด๋‹น ๋ฆฌ์ŠคํŠธ์— ๋‚˜์ด์— ๋Œ€ํ•ด ์ •๋ ฌ๋˜์–ด ์žˆ์„ ๋•Œ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค๋Š” ์ ์„ ๊ธฐ์–ตํ•ด์•ผ ํ•œ๋‹ค.

 

๐ŸŒฑ dropWhile

๋งŒ์•ฝ, takeWhile๋กœ ์„ ํƒ๋˜์ง€ ์•Š์€ ๋‹ค๋ฅธ ์š”์†Œ๋“ค์— ์„ ํƒํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” dropWhile์„ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

List<Crew> crews = new ArrayList<>();
List<Crew> backendCrews = crews.stream()
        .dropWhile(crew -> crew.getAge() <= 25)
        .collect(Collectors.toUnmodifiableList());

dropWhile์„ ์‚ฌ์šฉํ•˜๋ฉด takeWhile๊ณผ ๋ฐ˜๋Œ€๋กœ ์ˆ˜ํ–‰ํ•œ๋‹ค.

dropWhile์€ ์ฒ˜์Œ์œผ๋กœ ๊ฑฐ์ง“์ด ๋˜๋Š” ์ง€์ ๊นŒ์ง€ ๋ฐœ๊ฒฌ๋œ ์š”์†Œ๋“ค์„ ๋ฒ„๋ฆฌ๊ณ , ๋‚จ์€ ์š”์†Œ๋“ค์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

 


โœ”๏ธ ์ŠคํŠธ๋ฆผ ์ถ•์†Œ

๐ŸŒฑ limit

์ฃผ์–ด์ง„ ๊ฐ’ ์ดํ•˜์˜ ํฌ๊ธฐ๋ฅผ ๊ฐ€์ง€๋Š” ์ƒˆ๋กœ์šด ์ŠคํŠธ๋ฆผ์„ ๋ฐ˜ํ™˜ํ•˜๋Š” limit(n) ๋ฉ”์„œ๋“œ๋„ ์กด์žฌํ•œ๋‹ค.

์ด๋Š”, ์ŠคํŠธ๋ฆผ์ด ์ •๋ ฌ๋˜์–ด ์žˆ์„ ๋•Œ ์ตœ๋Œ€ ์š”์†Œ n๊ฐœ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

List<Crew> crews = new ArrayList<>();
List<String> backendCrews = crews.stream()
        .filter(crew -> crew.getCourse() == Course.BACKEND)
        .limit(10)
        .collect(Collectors.toUnmodifiableList());

๋ฌผ๋ก , ์ •๋ ฌ๋˜์ง€ ์•Š์€ ์ƒํƒœ์—์„œ๋„ limit์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด๋Ÿฌ๋ฉด ๊ฒฐ๊ณผ ์—ญ์‹œ ์ •๋ ฌ๋˜์ง€ ์•Š์€ ์ƒํƒœ๋กœ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

 


โœ”๏ธ ์š”์†Œ ๊ฑด๋„ˆ๋›ฐ๊ธฐ

๐ŸŒฑ skip

์ฒ˜์Œ ์š”์†Œ n๊ฐœ๋ฅผ ์ œ์™ธํ•œ ์ŠคํŠธ๋ฆผ์„ ๋ฐ˜ํ™˜ํ•˜๋Š” skip(n) ๋ฉ”์„œ๋“œ๊ฐ€ ์กด์žฌํ•œ๋‹ค.

๋งŒ์•ฝ, n๊ฐœ ์ดํ•˜์˜ ์š”์†Œ๊ฐ€ ์กด์žฌํ•œ๋‹ค๋ฉด ๋นˆ ์ŠคํŠธ๋ฆผ์ด ๋ฐ˜ํ™˜๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ŠคํŠธ๋ฆผ์€ ์ฃผ์–ด์ง„ ๊ฐ’๋ณด๋‹ค ๋” ํฐ ์‚ฌ์ด์ฆˆ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์–ด์•ผ ํ•œ๋‹ค.

List<Crew> crews = new ArrayList<>();
List<Crew> backendCrews = crews.stream()
        .filter(crew -> crew.getCourse() == Course.BACKEND)
        .skip(3)
        .collect(Collectors.toUnmodifiableList());

 

 


โœ”๏ธ ๋งคํ•‘ 

๐ŸŒฑ map

์ŠคํŠธ๋ฆผ์—์„œ๋Š” map() ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ํ•จ์ˆ˜๋ฅผ ์ธ์ˆ˜๋กœ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.

๋ฐ›์€ ํ•จ์ˆ˜๋Š” ๊ฐ ์š”์†Œ์— ์ ์šฉ๋˜๋ฉฐ, ์ ์šฉ๋œ ๊ฒฐ๊ณผ๊ฐ€ ์ƒˆ๋กœ์šด ์š”์†Œ๋กœ ๋งคํ•‘๋œ๋‹ค.

List<Crew> crews = new ArrayList<>();
List<String> backendCrews = crews.stream()
        .filter(crew -> crew.getCourse() == Course.BACKEND)
        .map(Crew::getName)
        .collect(Collectors.toUnmodifiableList())

์œ„ ์ฝ”๋“œ์—์„œ map์„ ํ†ตํ•ด Crew ํƒ€์ž…์„ String ํ˜•ํƒœ๋กœ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ๋งŒ๋“ค์—ˆ๋‹ค.

 

 

๐ŸŒฑ flatMap

๋งคํ•‘ ๊ธฐ๋Šฅ ์ค‘์—์„œ, flatMap()์ด๋ผ๋Š” ํŠน๋ณ„ํ•œ ๊ธฐ๋Šฅ์ด ์กด์žฌํ•œ๋‹ค.

์ด๋Š” ์ŠคํŠธ๋ฆผ ๊ฐ๊ฐ์˜ ์š”์†Œ๋ฅผ ๋‹ค๋ฅธ ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋งŒ๋“  ๋‹ค์Œ์—, ๋ชจ๋“  ์ŠคํŠธ๋ฆผ์„ ํ•˜๋‚˜์˜ ์ŠคํŠธ๋ฆผ์œผ๋กœ ์—ฐ๊ฒฐํ•œ๋‹ค.

String[] words = {"Hello", "Wootechco"};
List<String> splitedWords =
        // ๋ฐฐ์—ด์˜ ๊ฐ ์š”์†Œ์— ๋Œ€ํ•ด์„œ stream ์ˆœํšŒ	
        Arrays.stream(words)
                // ๊ฐ ์š”์†Œ๋ฅผ ๋ถ„ํ•ดํ•˜์—ฌ ๋งคํ•‘ (H,e,l,l,o / W,o,o,t,e,c,h,c,o)
                .map(word -> word.split(""))
                // ์ƒ์„ฑ๋œ ์ŠคํŠธ๋ฆผ์„ ํ•˜๋‚˜์˜ ์ŠคํŠธ๋ฆผ์œผ๋กœ ํ‰๋ฉดํ™” ์ง„ํ–‰, (word -> Arrays.stream(word))์™€ ๋™์ผํ•œ ์˜๋ฏธ์ด๋‹ค.
                .flatMap(Arrays::stream)
                // ๋ฆฌ์ŠคํŠธ๋กœ ๋ชจ์œผ๊ธฐ
                .collect(Collectors.toList());

// [H, e, l, l, o, W, o, o, t, e, c, h, c, o]
System.out.println(splitedWords);

Intellij์—์„œ ์ŠคํŠธ๋ฆผ์˜ ๊ฐ ๋ฐ˜ํ™˜๊ฐ’์— ๋Œ€ํ•ด ์ถ”๋ก ํ•œ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์ž.

word.split(””) ํ–ˆ์„ ๋•Œ “Hello”, “wootechco”๋ผ๋Š” ๊ฐ ๋ฌธ์ž์—ด์— ๋Œ€ํ•˜์—ฌ split์„ ์ง„ํ–‰ํ•œ ๊ฒฐ๊ณผ๋กœ String[]์„ ๊ฐ€์ง„ ์ŠคํŠธ๋ฆผ์„ ๋ฐ˜ํ™˜ํ•˜๊ฒŒ ๋˜๊ณ , ํ•ด๋‹น ์ŠคํŠธ๋ฆผ์— ๋Œ€ํ•ด flatMap์„ ์‚ฌ์šฉํ•˜์—ฌ Stream<String>์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. (String[] -> String์œผ๋กœ ํ‰๋ฉดํ™”)

 


โœ”๏ธ ๊ฒ€์ƒ‰๊ณผ ๋งค์นญ

ํŠน์ • ์†์„ฑ์ด ๋ฐ์ดํ„ฐ ์ง‘ํ•ฉ์— ์กด์žฌํ•˜๋Š”์ง€์— ๋Œ€ํ•ด Stream API๋ฅผ ํ™œ์šฉํ•˜๋ฉด ์‰ฝ๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

๐ŸŒฑ anyMatch

boolean isBackendCrew = crews.stream()
       .anyMatch(crew -> crew.getCourse() == Course.BACKEND);

์ ์–ด๋„ ํ•˜๋‚˜์˜ ์š”์†Œ์™€ ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.

 

 

๐ŸŒฑ allMatch

boolean isBackendCrew = crews.stream()
       .allMatch(crew -> crew.getCourse() == Course.BACKEND);

๋ชจ๋“  ์š”์†Œ์™€ ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.

 

 

๐ŸŒฑ noneMatch

boolean isBackendCrew = crews.stream()
       .noneMatch(crew -> crew.getCourse() == Course.BACKEND);

๋ชจ๋“  ์š”์†Œ์™€ ์ผ์น˜ํ•˜์ง€ ์•Š๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.

 

์œ„ 3๊ฐ€์ง€ ์—ฐ์‚ฐ์€ ๋ชจ๋‘ ‘์‡ผํŠธ์„œํ‚ท ๊ธฐ๋ฒ•’์„ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋Š”๋ฐ, ์ด๋Š” ์ „์ฒด ์ŠคํŠธ๋ฆผ์„ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š๋”๋ผ๋„ ๊ฒฐ๊ณผ๋ฅผ ๋„์ค‘์— ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ์ž๋ฐ”์—์„œ if (a && b)๋ผ๋Š” ์กฐ๊ฑด๋ฌธ์ด ์žˆ์„ ๋•Œ, a๊ฐ€ ๊ฑฐ์ง“์ด๋ฉด ๊ทธ ๋’ค์˜ ์กฐ๊ฑด์‹์€ ๋ณด์ง€ ์•Š๊ณ  ๋ฐ”๋กœ ์ „์ฒด์— ๋Œ€ํ•ด ๊ฑฐ์ง“์ด๋ผ๊ณ  ํŒ๋‹จํ•˜๊ฒŒ ๋œ๋‹ค. ์ด๋Ÿฐ ์ƒํ™ฉ์„ ์‡ผํŠธ์„œํ‚ท์ด๋ผ๊ณ  ํ•˜๋ฉฐ, ๋ชจ๋“  ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ์— ๋Œ€ํ•ด์„œ ํŒ๋‹จํ•˜์ง€ ์•Š๊ณ , ์›ํ•˜๋Š” ๊ฒฐ๊ณผ๋ฅผ ์ฐพ์•˜์œผ๋ฉด ๋ฐ”๋กœ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค. (limit ์—ญ์‹œ ์‡ผํŠธ์„œํ‚ท ์—ฐ์‚ฐ์ด๋‹ค)

 

 

๐ŸŒฑ findAny

Optional<Crew> backendCrew = crews.stream()
                .filter(crew -> crew.getCourse() == Course.BACKEND)
                .findAny()

์ŠคํŠธ๋ฆผ์—์„œ ์ž„์˜์˜ ์š”์†Œ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

filter + findAny๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด, ํ•ด๋‹น ์กฐ๊ฑด๊ณผ ์ผ์น˜ํ•˜๋Š” ์ž„์˜์˜ ์š”์†Œ๋ฅผ ๋ฆฌํ„ดํ•œ๋‹ค.

์ด๋•Œ, ์š”์†Œ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— Optional<T>์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

Optional์„ ํ™œ์šฉํ•˜๋ฉด ๊ฐ’์ด ์กด์žฌํ•˜๊ฑฐ๋‚˜, ํ˜น์€ ์กด์žฌํ•˜์ง€ ์•Š์Œ์„ ๋ช…์‹œ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

์—ฌ๊ธฐ์„œ ์ฒด์ด๋‹์„ ํ†ตํ•ด ifPresent๋‚˜ isPresent๋ฅผ ํ†ตํ•ด ๊ฐ’์ด ์กด์žฌํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ์— ๋Œ€ํ•ด ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค. (์ถ”ํ›„ ์ฑ•ํ„ฐ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉฐ ๋‚˜์˜จ๋‹ค!)

boolean isPresent = crews.stream()
                .filter(crew -> crew.getCourse() == Course.BACKEND)
                .findAny()
                .isPresent();

        // ์กด์žฌํ•œ๋‹ค๋ฉด ๋ณ„๋„์˜ ๋กœ์ง ์‹คํ–‰
        crews.stream()
                .filter(crew -> crew.getCourse() == Course.BACKEND)
                .findAny()
                .ifPresent(crew -> System.out.println(crew.getName()));

        // ๊ฐ’์ด ์กด์žฌํ•˜๋ฉด ๊ฐ€์ ธ์˜ค๊ณ , ์•„๋‹ˆ๋ผ๋ฉด NoSuchElementException
        Crew backendCrew = crews.stream()
                .filter(crew -> crew.getCourse() == Course.BACKEND)
                .findAny()
                .get();

        // ๊ฐ’์ด ์žˆ์œผ๋ฉด ๋ฐ˜ํ™˜ํ•˜๊ณ , ์•„๋‹ˆ๋ผ๋ฉด ์„ค์ •ํ•œ ๊ธฐ๋ณธ๊ฐ’ ๋ฐ˜ํ™˜
        Crew defaultCrew = crews.stream()
                .filter(crew -> crew.getCourse() == Course.BACKEND)
                .findAny()
                .orElse(new Crew());

๋Œ€์ถฉ ์œ„์˜ ์˜ˆ์‹œ์ฒ˜๋Ÿผ ๊ฐ’์ด ์กด์žฌํ•  ๋•Œ, ํ˜น์€ ์กด์žฌํ•˜์ง€ ์•Š์„ ๋•Œ ๋ณ„๋„์˜ ์กฐ๊ฑด์„ ์ฃผ์–ด์„œ ์กฐ๊ธˆ ๋” ์šฉ์ดํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค€๋‹ค.

 

 

๐ŸŒฑ findFirst

๋งŒ์•ฝ, ์ŠคํŠธ๋ฆผ ๋‚ด๋ถ€์˜ ์•„์ดํ…œ ์ˆœ์„œ๊ฐ€ ์ •ํ•ด์ ธ ์žˆ๋‹ค๋ฉด ์–ด๋–จ๊นŒ? findFirst()๋ฅผ ํ™œ์šฉํ•˜๋ฉด ์ผ์น˜ํ•˜๋Š” ์š”์†Œ ์ค‘ ๊ฐ€์žฅ ์ฒซ ๋ฒˆ์งธ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

Optional<Crew> backendCrew = crews.stream()
                .filter(crew -> crew.getCourse() == Course.BACKEND)
                .findFirst();

๊ทธ๋ ‡๋‹ค๋ฉด, ์ด๋Ÿฌํ•œ ์˜๋ฌธ์ด ๋“ค ๊ฒƒ์ด๋‹ค. findFirst์™€ findAny ๋ชจ๋‘ ๋™์ผํ•œ Optional<Crew>๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š”๋ฐ, ๋ฌด์Šจ ์ฐจ์ด๊ฐ€ ์žˆ์„๊นŒ?

 

๋ฐ”๋กœ, ๋ณ‘๋ ฌ์„ฑ์—์„œ ์ฐจ์ด๊ฐ€ ์žˆ๋‹ค.

๋ณ‘๋ ฌ ์‹คํ–‰์—์„œ๋Š” ์ฒซ ๋ฒˆ์งธ ์š”์†Œ๊ฐ€ ์–ด๋–ค ๊ฒƒ์ธ์ง€ ์•Œ๊ธฐ๊ฐ€ ์–ด๋ ต๊ธฐ ๋•Œ๋ฌธ์—, ์š”์†Œ์˜ ๋ฐ˜ํ™˜ ์ˆœ์„œ๊ฐ€ ์ƒ๊ด€์—†๋‹ค๋ฉด ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์—์„œ๋Š” (parallelStream) findAny()๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

// Stream.java
@Override
public final Optional<P_OUT> findFirst() {
    return evaluate(FindOps.makeRef(true));
}

@Override
public final Optional<P_OUT> findAny() {
    return evaluate(FindOps.makeRef(false));
}

์‹ค์ œ๋กœ ๋‘˜์˜ ๋‚ด๋ถ€ ๊ตฌํ˜„์„ ๋ณด๋ฉด, ์ธ์ž๋กœ mustFindFirst๋ผ๋Š” boolean๊ฐ’์„ ๋ฐ›๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋Š”๋ฐ, ๋‚ด๋ถ€์ ์œผ๋กœ ์–ด๋– ํ•œ ๋กœ์ง์„ ํ†ตํ•ด์„œ ์ฒซ ๋ฒˆ์งธ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๊ฒŒ ๋œ๋‹ค.

Params: mustFindFirst – whether the TerminalOp must produce the first element in the encounter order

์ฃผ์„์— ์ ํžŒ ์„ค๋ช…์„ ๋ณด๋ฉด, ์ฃผ์–ด์ง„ ์ˆœ์„œ์—์„œ ๊ฐ€์žฅ ์ฒ˜์Œ์œผ๋กœ ๋ฐœ๊ฒฌ๋œ ๊ฐ’์„ ๋ฆฌํ„ดํ•˜๊ฑฐ๋‚˜, ์•„๋‹ˆ๋ฉด ๊ทธ๋ƒฅ ์•„๋ฌด ์š”์†Œ๋‚˜ ๋ฆฌํ„ดํ•จ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

 


โœ”๏ธ ๋ฆฌ๋“€์‹ฑ

์ŠคํŠธ๋ฆผ ์š”์†Œ๋ฅผ ์กฐํ•ฉํ•˜์—ฌ ๋” ๋ณต์žกํ•œ ์งˆ์˜๋ฅผ ํ‘œํ˜„ํ•˜๊ธฐ ์œ„ํ•ด์„œ ๋ฆฌ๋“€์‹ฑ ์—ฐ์‚ฐ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด๋Š” ๋ชจ๋“  ์ŠคํŠธ๋ฆผ ์š”์†Œ๋ฅผ ์ฒ˜๋ฆฌํ•˜์—ฌ ํ•˜๋‚˜์˜ ๊ฐ’์œผ๋กœ ๋„์ถœํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•˜๋ฉฐ, ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ๋Š” ‘ํด๋“œ’๋ผ๊ณ ๋„ ๋งํ•œ๋‹ค.

๋ฆฌ์ŠคํŠธ์— ์กด์žฌํ•˜๋Š” ๋ชจ๋“  ์ˆซ์ž๋ฅผ ๋”ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด์ž.

// before
int sum = 0;
for(int number : numbers) {
    sum += number;
}

// after
sum = numbers.stream().reduce(0, (a, b) -> a + b);

// ๋ฉ”์„œ๋“œ ์ฐธ์กฐ
sum = numbers.stream().reduce(0, Integer::sum);

for-each ๋Œ€์‹ ์— reduce๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, ์ฝ”๋“œ๊ฐ€ ํ›จ์”ฌ ๊ฐ„๊ฒฐํ•ด์ง€๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

 

 

๐ŸŒฑ reduce 

T reduce(T identity, BinaryOperator<T> accumulator);

Stream ์ธํ„ฐํŽ˜์ด์Šค์— ์ •์˜๋œ reduce๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ตฌ์„ฑ๋œ๋‹ค.

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

sum = numbers.stream().reduce(0, (a, b) -> a + b);

์šฐ๋ฆฌ์˜ ์ฝ”๋“œ์—์„œ๋Š” ์ดˆ๊ธฐ๊ฐ’์œผ๋กœ 0์„, ๊ทธ๋ฆฌ๊ณ  ์ƒˆ๋กœ์šด ๊ฐ’์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•œ ์˜คํผ๋ ˆ์ดํ„ฐ๋Š” (a, b) → a + b๋ฅผ ์‚ฌ์šฉํ•˜์˜€๋‹ค.

๋ง์…ˆ์ด ์•„๋‹Œ ๊ณฑ์…ˆ, ๋บ„์…ˆ ๋“ฑ์œผ๋กœ๋„ ์ž์œ ๋กญ๊ฒŒ ์‘์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

 

reduce๋Š” ์–ด๋–ค ๋ฐฉ์‹์œผ๋กœ ์ŠคํŠธ๋ฆผ์˜ ๋ชจ๋“  ์ˆซ์ž๋ฅผ ๋”ํ•˜๊ฒŒ ๋ ๊นŒ?

์ดˆ๊ธฐ๊ฐ’์œผ๋กœ๋ถ€ํ„ฐ, ์ŠคํŠธ๋ฆผ์˜ ๊ฐ ์š”์†Œ๋ฅผ ํ•˜๋‚˜์”ฉ ์†Œ๋น„ํ•˜๋ฉฐ ๊ทธ ๊ฒฐ๊ณผ๊ฐ’์„ ๊ทธ ๋‹ค์Œ ์—ฐ์‚ฐ์„ ์œ„ํ•œ ๋ˆ„์ ๊ฐ’์œผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค.

๊ทธ๋ฆฌ๊ณ , ํ•ด๋‹น ๋ˆ„์ ๊ฐ’๊ณผ ๊ทธ ๋‹ค์Œ ์š”์†Œ๋ฅผ ํ•จ๊ป˜ ๋žŒ๋‹ค์‹์— ์กฐํ•ฉํ•˜์—ฌ ๊ณ„์† ๊ณ„์‚ฐํ•ด๋‚˜๊ฐ€๋Š” ํ˜•์‹์ด๋‹ค.

 

์ฐธ๊ณ ๋กœ, ์ดˆ๊ธฐ๊ฐ’์„ ์„ ์–ธํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค. ๋‹จ, ์ด ๊ฒฝ์šฐ๋Š” Optional ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

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

์ŠคํŠธ๋ฆผ์— ์•„๋ฌด ์š”์†Œ๋„ ์กด์žฌํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ, ์ดˆ๊ธฐ๊ฐ’๋„ ์—†๋‹ค๋ฉด ํ•ฉ๊ณ„๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— Optional์„ ํ†ตํ•ด null์— ๋Œ€ํ•œ ์กด์žฌ ์—ฌ๋ถ€๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค.

 

๋˜ํ•œ, reduce๋ฅผ ์ด์šฉํ•˜๋ฉด ์ตœ๋Œ“๊ฐ’๊ณผ ์ตœ์†Ÿ๊ฐ’๋„ ์‰ฝ๊ฒŒ ๊ตฌํ•  ์ˆ˜ ์žˆ๋‹ค.

int max = numbers.stream().reduce(0, Integer::max);
int min = numbers.stream().reduce(0, Integer::min);

Optional<Integer> max = numbers.stream().reduce(Integer::max);
Optional<Integer> min = numbers.stream().reduce(Integer::min);

 

 

๐ŸŒฑ reduce์˜ ์žฅ์ 

์ด๋Ÿฌํ•œ reduce๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ญ๊ฐ€ ์ข‹์„๊นŒ?

์šฐ์„ , ๋‚ด๋ถ€ ๋ฐ˜๋ณต์œผ๋กœ ์ธํ•ด ๋ฐ˜๋ณตํ•˜๋Š” ๋กœ์ง์ด ์ถ”์ƒํ™”๋˜๋ฉด์„œ, reduce ์ž์ฒด๋ฅผ ๋‚ด๋ถ€์ ์œผ๋กœ ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

์œ„์—์„œ๋ถ€ํ„ฐ ๊ณ„์† ๋ณด์•˜๋˜ ํ•ฉ์„ ๊ตฌํ•˜๋Š” reduce๋ฅผ ์ƒ๊ฐํ•ด๋ณด์ž.

์šฐ๋ฆฌ๊ฐ€ ์™ธ๋ถ€ ๋ฐ˜๋ณต์„ ํ†ตํ•ด ์š”์†Œ์˜ ํ•ฉ์„ ๊ตฌํ•œ๋‹ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ๊ฒƒ์ด๋‹ค.

List<Integer> numbers = List.of(1, 2, 3, 4);
int sum = 0;
for (Integer number: numbers) {
	sum += number;
}

์—ฌ๊ธฐ์„œ ๊ณต์œ  ๋ณ€์ˆ˜์ธ sum์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ์šฐ๋ฆฌ๊ฐ€ ์œ„ ๋กœ์ง์„ ๋ณ‘๋ ฌ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ๊ณต์œ  ๋ณ€์ˆ˜์— ๋Œ€ํ•œ ๋™๊ธฐํ™” ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ค˜์•ผ ํ•œ๋‹ค.

ํ•˜์ง€๋งŒ, stream์„ ์‚ฌ์šฉํ•˜๊ฒŒ ๋œ๋‹ค๋ฉด ๋‹จ์ˆœํžˆ parallelStream()์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๋งŒ์œผ๋กœ๋„ ๋ณ‘๋ ฌ์„ฑ์„ ์‰ฝ๊ฒŒ ์–ป์„ ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.

๋˜ํ•œ, reduce๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋œ๋‹ค๋ฉด ๊ณต์œ  ๋ณ€์ˆ˜๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์— ์œ„์™€ ๊ฐ™์ด ํ•ฉ์„ ๊ตฌํ•˜๋”๋ผ๋„ ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰์ด ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋œ๋‹ค.

(์ด์— ๋Œ€ํ•ด์„œ๋Š” ๊ฐœ๋ณ„๋กœ ํฌ์ŠคํŒ…์„ ์˜ฌ๋ ค๋ณด๋„๋ก ํ•˜๊ฒ ๋‹ค :D)

 

 

๐ŸŒฑ ์ŠคํŠธ๋ฆผ์˜ ์ƒํƒœ

map, filter ๋“ฑ์€ ์ž…๋ ฅ ์ŠคํŠธ๋ฆผ์—์„œ ์š”์†Œ๋ฅผ ๋ฐ›์•„ ์ถœ๋ ฅ ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ณด๋‚ด๊ธฐ ๋•Œ๋ฌธ์—, ๋‚ด๋ถ€์ ์œผ๋กœ ์ƒํƒœ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์ง€ ์•Š๋‹ค.

์š”์†Œ๊ฐ€ ์ฒ˜๋ฆฌ๋  ๋•Œ๋งˆ๋‹ค ๋ฐ”๋กœ๋ฐ”๋กœ ์ฒ˜๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ์ถœ๋ ฅํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์ถœ๋ ฅ ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ ๊ฐœ์ˆ˜์™€ ์ž…๋ ฅ ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ ๊ฐœ์ˆ˜๊ฐ€ ๋™์ผํ•˜๊ฒŒ ์œ ์ง€๋œ๋‹ค.

 

ํ•˜์ง€๋งŒ, sort, distinct ๋“ฑ์€ ์š”์†Œ๋ฅผ ์ •๋ ฌํ•˜๊ฑฐ๋‚˜ ์ค‘๋ณต์„ ์ œ๊ฑฐํ•˜๊ธฐ ์œ„ํ•ด์„œ ๊ณผ๊ฑฐ์˜ ์ƒํƒœ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์–ด์•ผ ํ•œ๋‹ค.

์ด๋Ÿฐ ๊ฒฝ์šฐ ‘์ƒํƒœ๊ฐ€ ์žˆ๋Š” ์ŠคํŠธ๋ฆผ’์ด๋ผ๊ณ  ํ•˜๋ฉฐ, ์ƒํƒœ๊ฐ€ ์žˆ๋Š” ์ŠคํŠธ๋ฆผ ๋ชจ๋“  ์š”์†Œ๊ฐ€ ๋ฒ„ํผ์— ์‚ฌ์ „์— ์ถ”๊ฐ€๊ฐ€ ๋˜์–ด ์žˆ์–ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์ŠคํŠธ๋ฆผ ๋ฐ์ดํ„ฐ์˜ ํฌ๊ธฐ๊ฐ€ ํฌ๋ฉด ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

 

๋˜ํ•œ, reduce, sum ๊ฐ™์€ ์—ฐ์‚ฐ์€ ๋‚ด๋ถ€์ ์œผ๋กœ ๊ฒฐ๊ณผ๋ฅผ ๋ˆ„์ ํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋‚ด๋ถ€ ์ƒํƒœ์˜ ํฌ๊ธฐ๋Š” ํ•œ์ •๋˜์–ด (bound) ์žˆ์–ด์•ผ ํ•œ๋‹ค.

์šฐ๋ฆฌ๋Š” ์œ„ ์ฝ”๋“œ์—์„œ Integer::max ๋“ฑ์„ ํ™œ์šฉํ•ด์„œ ๋‚ด๋ถ€์˜ ๊ฐ’์ด ์ตœ๋Œ€ Integer ๋ฒ”์œ„ ๋‚ด์˜ ๊ฐ’์ž„์„ ํ•œ์ •ํ•œ ์…ˆ์ด๋‹ค.

 

๊ทธ๋ž˜์„œ ์ƒํƒœ๊ฐ€ ์žˆ๋Š” ์ŠคํŠธ๋ฆผ์€ ์ƒํƒœ๊ฐ€ ์—†๋Š” ์ŠคํŠธ๋ฆผ๋ณด๋‹ค ์ฒ˜๋ฆฌ ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆด ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋‚ด๋ถ€ ํฌ๊ธฐ ์ œ์•ฝ์ด ํ•„์š”ํ•˜๋‹ค.

์ด๋Ÿฌํ•œ ์ ์„ ์ž˜ ๊ณ ๋ คํ•˜์—ฌ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•œ๋‹ค.


โœ”๏ธ ์ˆซ์žํ˜• ์ŠคํŠธ๋ฆผ

int ages = crews.stream() 
                .map(Crew::getAge)                      
                .reduce(0, Integer::sum);

์œ„ ์ฝ”๋“œ์—์„œ๋Š” Integer::sum์ด๋ผ๋Š” ๋ฉ”์„œ๋“œ ์ฐธ์กฐ ์ฝ”๋“œ๋กœ ์ธํ•ด์„œ int → Integer๋กœ์˜ ๋ฐ•์‹ฑ ๋น„์šฉ์ด ์†Œ์š”๋œ๋‹ค.

๐Ÿ’ก ๋ฐ•์‹ฑ์€ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ดˆ๊ธฐํ™”ํ•˜๋Š” ๊ณผ์ •์ด ํ•„์š”ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์ด ๊ณผ์ •์—์„œ ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น, ์ƒ์„ฑ์ž ํ˜ธ์ถœ ๋“ฑ์˜ ๋น„์šฉ์ด ๋ฐœ์ƒํ•œ๋‹ค.

 

์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด, ๊ธฐ๋ณธํ˜• ํŠนํ™” ์ŠคํŠธ๋ฆผ์„ ํ™œ์šฉํ•ด๋ณด์ž.

๊ธฐ๋ณธ ์ŠคํŠธ๋ฆผ์—์„œ ๊ธฐ๋ณธํ˜• ํŠนํ™” ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๊ฐ ํƒ€์ž…์— ๋งž์ถฐ mapToInt, mapToDouble, mapToLong 3๊ฐ€์ง€ ๋ฉ”์„œ๋“œ๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

int ages = crews.stream()
                .mapToInt(Crew::getAge)
                .sum();

์œ„ ์˜ˆ์ œ์—์„œ๋Š” mapToInt๋ฅผ ํ™œ์šฉํ•ด์„œ IntStream์„ ๋ฐ˜ํ™˜ํ•˜์—ฌ (๊ธฐ๋ณธํ˜• ํŠนํ™” ์ŠคํŠธ๋ฆผ) ํ•ด๋‹น ์ŠคํŠธ๋ฆผ์—์„œ ์ œ๊ณตํ•˜๋Š” sum ๋ฉ”์„œ๋“œ๋ฅผ ํ™œ์šฉํ•˜์—ฌ ํ•ฉ์„ ๊ณ„์‚ฐํ•˜์˜€๋‹ค. ์ด๋•Œ, ๋งŒ์•ฝ IntStream์ด ๋น„์–ด์žˆ์œผ๋ฉด sum์€ ๊ธฐ๋ณธ๊ฐ’์ธ 0์„ ๋ฐ˜ํ™˜ํ•˜๊ฒŒ ๋˜๋ฉฐ, sum ์™ธ์—๋„ ๋‹ค์–‘ํ•œ ์œ ํ‹ธ๋ฆฌํ‹ฐ ๋ฉ”์„œ๋“œ๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

 

๊ธฐ๋ณธํ˜• ํŠนํ™” ์ŠคํŠธ๋ฆผ์—์„œ ๊ธฐ๋ณธ ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ณต์›ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š”, boxed ๋ฉ”์„œ๋“œ๋ฅผ ํ™œ์šฉํ•˜๋ฉด ๋œ๋‹ค.

Stream<Integer> stream = crews.stream()
                .mapToInt(Crew::getAge)
                .boxed();

 

๊ธฐ๋ณธํ˜• ํŠนํ™” ์ŠคํŠธ๋ฆผ์—์„œ max๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ, ๊ฐ’์ด ์—†๋Š” ๊ฒฝ์šฐ์™€ ์ตœ๋Œ“๊ฐ’์ด 0์ธ ๊ฒฝ์šฐ ๋ชจ๋‘ 0์„ ๋„์ถœํ•œ๋‹ค.

์ด๋•Œ, ๋‘˜์„ ์–ด๋–ป๊ฒŒ ๊ตฌ๋ณ„ํ•  ์ˆ˜ ์žˆ์„๊นŒ? ์ด๋•Œ๋Š” Optional์„ ํ™œ์šฉํ•˜์ž.

OptionalInt ages = crews.stream()
        .mapToInt(Crew::getAge)
        .max();

// ์ตœ๋Œ“๊ฐ’์ด ์—†๋Š” ์ƒํ™ฉ์— ๋Œ€ํ•ด ๊ธฐ๋ณธ๊ฐ’ ์ •์˜
int ages = crews.stream()
        .mapToInt(Crew::getAge)
        .max()
        .orElse(10);

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด IntStream์˜ ์ตœ๋Œ“๊ฐ’์„ ํ™•์‹คํ•˜๊ฒŒ ์ฒดํฌํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ตœ๋Œ“๊ฐ’์ด ์—†๋Š” ์ƒํ™ฉ์— ๋Œ€ํ•ด์„œ๋„ ๋ช…์‹œ์ ์œผ๋กœ ์ •์˜๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.

 

ํŠน์ • ๋ฒ”์œ„์˜ ์ˆซ์ž๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•  ๋•Œ๋Š”, range๋ฅผ ํ™œ์šฉํ•ด๋ณด์ž.

// ์‹œ์ž‘-์ข…๋ฃŒ๊ฐ’์ด ๊ฒฐ๊ณผ์— ํฌํ•จํ•˜์ง€ ์•Š์Œ
IntStream range = IntStream.range(1, 100);

// ์‹œ์ž‘-์ข…๋ฃŒ๊ฐ’์ด ๊ฒฐ๊ณผ์— ํฌํ•จ๋จ
IntStream range = IntStream.rangeClosed(1, 100);

 

 


โœ”๏ธ ์ŠคํŠธ๋ฆผ ์ƒ์„ฑํ•˜๊ธฐ

๐ŸŒฑ ๊ฐ’ → ์ŠคํŠธ๋ฆผ : of

Stream<String> stream = Stream.of("Modern ", "Java ", "In ", "Action");

stream.map(String::toUpperCase)
		.forEach(System.out::println); // MODERN ,JAVA ,IN ,ACTION

// ์ŠคํŠธ๋ฆผ ๋น„์šฐ๊ธฐ
stream.empty();

๊ฐ๊ฐ์˜ ๊ฐ’๋“ค์— ๋Œ€ํ•ด์„œ ์ŠคํŠธ๋ฆผ์œผ๋กœ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค. 

 

 

๐ŸŒฑ null์ด ๋  ์ˆ˜ ์žˆ๋Š” ๊ฐœ์ฒด๋ฅผ ์ŠคํŠธ๋ฆผ์œผ๋กœ ์ƒ์„ฑ : ofNullable

// Before
String homeValue = System.getProperty("home");
Stream<String> homeValueStream
 = homeValue == null ? Stream.empty() : Stream.of(value);

// After
// ofNullable์„ ์‚ฌ์šฉํ•˜๋ฉด ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.
Stream<String> homeValueStream
	= Stream.ofNullable(System.getProperty("home"));

๊ธฐ์กด ์ฝ”๋“œ์—์„œ๋Š” ์ง์ ‘ null์„ ์ฒดํฌํ•ด์ค€ ๋‹ค์Œ์— null์ด๋ฉด ๋นˆ ์ŠคํŠธ๋ฆผ, ์•„๋‹ˆ๋ฉด ๊ฐ’์„ ๋„ฃ์€ ์ŠคํŠธ๋ฆผ์„ ํ•ด์คฌ์–ด์•ผ ํ–ˆ๋‹ค.

ofNullable์„ ํ™œ์šฉํ•˜๋ฉด ์•Œ์•„์„œ ๊ธฐ์กด ์ฝ”๋“œ์— ๋Œ€ํ•œ ๋ถ€๋ถ„์„ ๋‚ด๋ถ€์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•ด์ค€๋‹ค.

public static<T> Stream<T> ofNullable(T t) {
    return t == null ? Stream.empty()
                     : StreamSupport.stream(new Streams.StreamBuilderImpl<>(t), false);
}

์ด๋Ÿฐ ์‹์œผ๋กœ!

 

 

๐ŸŒฑ ๋ฐฐ์—ด → ์ŠคํŠธ๋ฆผ : Arrays.stream

int[] numbers = {2, 3, 5, 7, 11, 13};
int sum = Arrays.stream(numbers).sum();

์ด๋•Œ, int[] ๊ฐ™์€ primitive type์— ๋Œ€ํ•ด์„œ๋Š” ๊ธฐ๋ณธํ˜• ํŠนํ™” ์ŠคํŠธ๋ฆผ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

 

 

๐ŸŒฑ ํŒŒ์ผ → ์ŠคํŠธ๋ฆผ (nio API)

long uniqueWords = 0;

// try-with-resources
try(Stream<String> lines =
            Files.lines(Paths.get("data.txt"), Charset.defaultCharset())) {

    // split์„ ํ˜ธ์ถœํ•˜์—ฌ ๊ฐ ๋‹จ์–ด ๋ถ„๋ฆฌ
    // flatMap์„ ํ†ตํ•ด์„œ ํ•˜๋‚˜๋กœ ํ‰๋ฉดํ™”
    uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" ")))
            .distinct()
            .count();
} catch(IOException e){
}

 

๐ŸŒฑ ํ•จ์ˆ˜ → ๋ฌดํ•œ ์ŠคํŠธ๋ฆผ  : iterate

// UnaryOperator<T>
Stream.iterate(0, n -> n + 2)
        // ๋ฌดํ•œ์œผ๋กœ ๊ฐ’์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— (์–ธ๋ฐ”์šด๋“œ ์ŠคํŠธ๋ฆผ) ๊ฐœ์ˆ˜ ์ œํ•œ
        .limit(10)
        .forEach(System.out::println); // 0, 2, 4...

๋ฌดํ•œ ์ŠคํŠธ๋ฆผ์€ ํฌ๊ธฐ๊ฐ€ ๊ณ ์ •๋˜์ง€ ์•Š์€ ์ŠคํŠธ๋ฆผ์„ ์˜๋ฏธํ•œ๋‹ค. ๋ณดํ†ต limit์œผ๋กœ ์ŠคํŠธ๋ฆผ์˜ ์ตœ๋Œ€ ์‚ฌ์ด์ฆˆ๋ฅผ ์ œํ•œํ•˜๋Š” ๊ฒƒ์ด ์ผ๋ฐ˜์ ์ด๋‹ค.

๋งŒ์•ฝ ์ œํ•œํ•˜์ง€ ์•Š๊ฒŒ ๋œ๋‹ค๋ฉด ์ค‘๊ฐ„ ์—ฐ์‚ฐ์€ ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๋ฅผ ์†Œ๋น„ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—, ์•„๋ฌด ๊ฒฐ๊ณผ๋„ ๊ณ„์‚ฐ๋˜์ง€ ์•Š๋Š”๋‹ค.

๋˜ํ•œ, ๋ฌดํ•œ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ •๋ ฌํ•˜๊ฑฐ๋‚˜ reduce ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์—†๋‹ค.

public static<T> Stream<T> iterate(T seed,
            Predicate<? super T> hasNext,
            UnaryOperator<T> next) {
        ...
}

iterate ๋ฉ”์„œ๋“œ์˜ ๋‚ด๋ถ€์ ์œผ๋กœ Predicate๋ฅผ ์ง€์›ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ํŠน์ • ์กฐ๊ฑด์— ๋งž๋Š” ์ˆซ์ž๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ๋„ ๊ฐ€๋Šฅํ•˜๋‹ค.

 

// 100 ์ด๋‚ด์˜ ๊ฐ’์„ 0๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด์„œ 4์”ฉ ์ฆ๊ฐ€ํ•˜์—ฌ ์ถœ๋ ฅ
IntStream.iterate(0, n -> n < 100, n -> n + 4)
        .forEach(System.out::println);

// ํ˜น์€, takeWhile์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
// filter๋Š” ์–ธ์ œ ์ค‘๋‹จํ•ด์•ผ ํ• ์ง€ ์•Œ ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— filter๋กœ๋Š” ๋Œ€์ฒด๋˜์ง€ ์•Š๋Š”๋‹ค.
IntStream.iterate(0, n -> n + 4)
        .takeWhile(n -> n < 100)
        .forEach(System.out::println);

์œ„์— ์ž‘์„ฑํ•œ ๊ฒƒ์ฒ˜๋Ÿผ, ํŠน์ • ์กฐ๊ฑด์„ ์ค„ ๋•Œ filter๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค. 

์‡ผํŠธ ์„œํ‚ท ๊ธฐ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋Š” takeWhile ๊ธฐ๋Šฅ์„ ํ†ตํ•ด์„œ ์ข…๋‹จ ์ง€์ ์ด ํ™•์‹คํ•˜๋„๋ก ์กฐ๊ฑด์„ ์ฃผ์–ด์•ผ ํ•œ๋‹ค.

 

 

๐ŸŒฑ ํ•จ์ˆ˜ → ๋ฌดํ•œ ์ŠคํŠธ๋ฆผ : generate

generate๋Š” iterate์™€ ๋‹ค๋ฅด๊ฒŒ, ์ƒ์‚ฐ๋œ ๊ฐ’์— ๋Œ€ํ•œ ์—ฐ์†์„ฑ์„ ๋ณด์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค.

// 0~1 ์‚ฌ์ด์˜ ์ž„์˜์˜ ์ˆซ์ž 5๊ฐœ ์ƒ์„ฑ
Stream.generate(Math::random)
         .limit(5)
         .forEach(System.out::println);

generate ๋‚ด๋ถ€์—์„œ๋Š” Supplier๋ฅผ ๋ฐ›๋Š”๋‹ค. 

๋งˆ์ฐฌ๊ฐ€์ง€๋กœ, ๋ฐ•์‹ฑ ์—ฐ์‚ฐ์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๊ธฐ๋ณธํ˜• ํŠนํ™” ์ŠคํŠธ๋ฆผ์„ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

// 0~1 ์‚ฌ์ด์˜ ์ž„์˜์˜ ์ˆซ์ž 5๊ฐœ ์ƒ์„ฑ
DoubleStream.generate(Math::random)
            .limit(5)
            .forEach(System.out::println);

 

์•ž์„œ ๋งํ–ˆ๋˜ ๊ฒƒ์ฒ˜๋Ÿผ generate๋Š” ์ธ์ž๋กœ supplier๋ฅผ ๋ฐ›๋Š”๋‹ค.

supplier๋ฅผ ํ†ตํ•ด์„œ ์–ด๋– ํ•œ ์กฐ์ž‘์„ ํ†ตํ•ด ์ŠคํŠธ๋ฆผ์— ์กด์žฌํ•˜๋Š” ๋‹ค์Œ ๊ฐ’์„ ์ƒ์„ฑํ•  ๋•Œ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜๋„ ์žˆ๊ฒŒ ๋œ๋‹ค.

IntSupplier fib = new IntSupplier() {
    private int previous = 0;
    private int current = 1;

    public int getAsInt() {
        int oldPrevious = this.previous;
        int nextValue = this.previous + this.current;
        this.previous = this.current;
        this.current = nextValue;
        return oldPrevious;
    }
};
IntStream.generate(fib).limit(10).forEach(System.out::println);

๊ต์žฌ์—์„œ ์ œ์‹œ๋œ ํ”ผ๋ณด๋‚˜์น˜ ์ˆ˜์—ด์„ ์œ„ํ•œ Supplier๋‹ค.

Supplier ๋‚ด๋ถ€์—์„œ 'previous', 'current' ๊ฐ™์€ ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•œ ๋‹ค์Œ์—, ๋‹ค์Œ ์š”์†Œ๋“ค์„ ๊ณ„์‚ฐํ•  ๋•Œ ํ•ด๋‹น ๋ณ€์ˆ˜์˜ ๊ฐ’์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ์ด๋ ‡๊ฒŒ ๋˜๋ฉด, ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋˜์—ˆ์„ ๋•Œ ๊ธฐ์กด์˜ ์š”์†Œ๊ฐ€ ์˜ํ–ฅ์„ ์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ€๋ณ€ ์ƒํƒœ ๊ฐ์ฒด๊ฐ€ ๋œ๋‹ค.

 

๋งŒ์•ฝ, ๋ณ‘๋ ฌ๋กœ ์ฒ˜๋ฆฌํ•˜๊ฒŒ ๋œ๋‹ค๋ฉด previous์™€ current์˜ ๊ฐ’์ด ์ˆœ์ฐจ์ ์œผ๋กœ ์ฆ๊ฐ€ํ•˜๋Š” ํ˜•ํƒœ๊ฐ€ ์•„๋‹ˆ๊ฒŒ ๋  ๊ฒƒ์ด๋‹ค.

๊ทธ๋ž˜์„œ ๋ณ‘๋ ฌ ์ƒํƒœ์—์„œ๋Š” ์œ„์˜ ์ฝ”๋“œ์ฒ˜๋Ÿผ ์ƒํƒœ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ์–ด๋– ํ•œ ์ƒํƒœ์—๋„ ์˜์กดํ•˜์ง€ ์•Š๋„๋ก ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•œ๋‹ค.

๋ฐ˜๋ฉด์— iterate์˜ ๊ฒฝ์šฐ ์ƒํƒœ์— ๋Œ€ํ•œ ์กฐ์ž‘์€ ๋ถˆ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋น„๊ต์  ๋ณ‘๋ ฌ ์‹คํ–‰์— ์•ˆ์ „ํ•˜๋‹ค๊ณ  ๋ณผ ์ˆ˜ ์žˆ๋‹ค. (๋ถˆ๋ณ€์„ฑ ๋ณด์žฅ)

 


์ด๋ฒˆ ์ฑ•ํ„ฐ์˜ ๋‚ด์šฉ์„ ๊ฐ„๋žตํ•˜๊ฒŒ ์ •๋ฆฌํ•ด๋ณด์ž.

- filter, distinct, takeWhile, dropWhile, skip, limit์œผ๋กœ ์ŠคํŠธ๋ฆผ์„ ํ•„ํ„ฐ๋งํ•˜๊ฑฐ๋‚˜ ์ž๋ฅผ ์ˆ˜ ์žˆ๋‹ค.
- map, flatMap์œผ๋กœ ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๋ฅผ ์ถ”์ถœํ•˜๊ฑฐ๋‚˜ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ๋‹ค.
- findFirst, findAny๋กœ ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๋ฅผ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ๋‹ค.
- allMatch, noneMatch, anyMatch๋กœ ํ”„๋ฆฌ๋””์ผ€์ดํŠธ์™€ ์ผ์น˜ํ•˜๋Š” ์š”์†Œ๋ฅผ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ๋‹ค.
- reduce๋ฅผ ํ†ตํ•ด ๋ชจ๋“  ์š”์†Œ๋ฅผ ๋ฐ˜๋ณต์ ์œผ๋กœ ์กฐํ•ฉํ•˜์—ฌ ๊ฐ’์„ ๋„์ถœํ•  ์ˆ˜ ์žˆ๋‹ค.
- iterate, generate๋กœ ๋ฌดํ•œ ์ŠคํŠธ๋ฆผ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.
- ๋ฐ•์‹ฑ ๋น„์šฉ์„ ์ค„์ด๊ธฐ ์œ„ํ•œ ๊ธฐ๋ณธํ˜• ํŠนํ™” ์ŠคํŠธ๋ฆผ (IntStream, DoubleStream, LongStream)์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

[์ƒํƒœ ์—†๋Š” ์—ฐ์‚ฐ] filter, map
[์ƒํƒœ ์žˆ๋Š” ์—ฐ์‚ฐ] reduced, sorted, distinct
Comments