'flatMap()' โ‰  'Flat Map' in Parallel-Stream
๐Ÿ“„

'flatMap()' โ‰  'Flat Map' in Parallel-Stream

Created
Dec 31, 2021 11:20 AM
Tags
java
issue

Introduce

Java์˜ Stream์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ๋‹ค์Œ๊ณผ ๊ฐ™์ด Stream Pipeline์—์„œ List ๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋‹ค.
ย 
public class Main { public static void main(String[] args) { Stream.of("0123456789".split("")) // ["0", "1", ..., "9"] .parallel() // parallel stream .map(Main::genList) // Generate List .forEach(System.out::println); } private static List<String> genList(String contents) { return Arrays.asList( String.valueOf(new char[3]).replaceAll("\0", contents).split("")); } }
// output (parallel) [5, 5, 5] [2, 2, 2] [6, 6, 6] [3, 3, 3] [4, 4, 4] [8, 8, 8] [9, 9, 9] [7, 7, 7] [0, 0, 0] [1, 1, 1]
ย 
์œ„ ์ฝ”๋“œ๋Š” map() ์„ ํ†ตํ•ด List ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ์ด๋ฅผ forEach() ์—์„œ ํ•˜๋‚˜ํ•˜๋‚˜ ์ถœ๋ ฅํ•˜๋Š” ์ฝ”๋“œ์ด๋ฉฐ, println() ํ•จ์ˆ˜์—๋Š” ์ƒ์„ฑํ•œ List ํƒ€์ž…์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ๋“ค์–ด๊ฐ€๊ฒŒ ๋˜๊ฒ ๋‹ค.
ย 
์ด ๋•Œ, ํ•„์š”์— ์˜ํ•ด List ๋‚ด๋ถ€์˜ Elements๋ฅผ ๋ฐ”๋กœ Stream Pipeline์—์„œ ์‚ฌ์šฉํ•˜๊ณ ์ž ํ•œ๋‹ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ• ๊นŒ?
ย 
๋ญ ์—ฌ๋Ÿฌ ๋ฐฉ๋ฒ•์ด ์žˆ๊ฒ ์œผ๋‚˜... map() ๋Œ€์‹  flatMap() ์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ•œ๋‹ค๋ฉด ์ƒ๊ฐ๊ณผ๋Š” ๋‹ค๋ฅธ ๊ฒฐ๊ณผ๋ฅผ ๋งˆ์ฃผํ•  ๊ฒƒ์ด๋‹ค.
ย 
public class Main { public static void main(String[] args) { Stream.of("0123456789".split("")) .parallel() .flatMap(Main::genStringListStream) // <-- #2 .forEach(System.out::println); // <-- #3 } private static List<String> genList(String contents) { return Arrays.asList( String.valueOf(new char[10]).replaceAll("\0", contents).split("")); } private static Stream<String> genStringListStream(String contents) { return genList(contents).parallelStream(); } }
ย 
์œ„ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ์–ด๋– ํ•œ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜ฌ๊นŒ. 5 2 1 1 0 8 ... ๋ญ ์ด๋ ‡๊ฒŒ ์ถœ๋ ฅ์ด ๋ ๊นŒ? ์ „ํ˜€ ์•„๋‹ˆ๋‹ค. ๊ฒฐ๊ณผ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.
ย 
// output 5 5 5 2 2 2 6 6 6 8 8 8 9 9 9 7 7 7 3 3 3 4 4 4 0 0 0 1 1 1
ย 
๋ถ„๋ช…ํžˆ Stream<String> ํƒ€์ž…์œผ๋กœ ๋ฐ˜ํ™˜ํ–ˆ์Œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ , List ๋‹จ์œ„๋กœ ์ถœ๋ ฅ์ด ๋˜์–ด๋ฒ„๋ฆฐ๋‹ค. ๋ฌผ๋ก  ์ด ๋•Œ println() ํ•จ์ˆ˜์— ๋“ค์–ด๊ฐ€๋Š” ๋ฐ์ดํ„ฐ๋Š” String ํƒ€์ž…์ด๋‹ค.

Why

์™œ ์ด๋Ÿฌํ• ๊นŒ? ์ƒ๊ฐํ•ด๋ณด์ž.
ย 
์ผ๋‹จ flatMap() ๋ฉ”์„œ๋“œ์˜ ์ŠคํŽ™์„ ๋ณด๋ฉด, ์ธ์ž(Arguments)๋กœ ์ „๋‹ฌ๋˜๋Š” ํ•จ์ˆ˜(Function)๋Š” ๋ฐ˜๋“œ์‹œ Stream ํƒ€์ž…์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ๋ช…์‹œ๊ฐ€ ๋˜์–ด ์žˆ์œผ๋ฉฐ, ๋”ฐ๋ผ์„œ genStringListStream() ํ•จ์ˆ˜ ์—ญ์‹œ ์ด์— ๋งž์ถฐ String ํƒ€์ž…์˜ Stream ์„ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ์ฝ”๋“œ๋ฅผ ๊ตฌํ˜„ํ–ˆ๋‹ค.
ย 
์ด๊ฒŒ ์–ด๋– ํ•œ ๊ฒƒ์„ ์˜๋ฏธํ• ๊นŒ.
ย 
์ž, Stream ์€ Elements Sequence๋ฅผ ๋‹ค๋ฃจ๊ธฐ ์œ„ํ•ด ์กด์žฌํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  flatMap() ๋ฉ”์„œ๋“œ๋Š” ์ธ์ž๋กœ ์ „๋‹ฌ๋˜๋Š” ํ•จ์ˆ˜์˜ ๋ฐ˜ํ™˜์ธ Stream. ๋‹ค์‹œ๋งํ•ด Elements Sequence ๋‚ด๋ถ€์˜ Element๋ฅผ ์ดํ›„์˜ Stream Pipeline์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก Flatten ํ•˜๋Š” ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•  ๊ฒƒ์ด๋‹ค.
ย 
๊ทธ๋Ÿฌ๋‚˜ ์‹ค์ œ๋กœ๋Š” ๊ทธ๋ ‡๊ฒŒ ๋™์ž‘ํ•˜์ง€ ์•Š๋Š”๋‹ค. flatMap() ์˜ ๊ตฌํ˜„ ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€๋ฐ,
ย 
@Override public final <R> Stream<R> flatMap( Function<? super P_OUT, ? extends Stream<? extends R>> mapper ) { // ... @Override public void accept(P_OUT u) { try (Stream<? extends R> result = mapper.apply(u)) { if (result != null) { if (!cancellationRequestedCalled) { result.sequential().forEach(downstream); // <-- #1 } else { Spliterator<? extends R> s = result.sequential().spliterator(); do { } while (!downstream.cancellationRequested() && s.tryAdvance(downstream)); } } } } // ... }
ย 
์ฝ”๋“œ์˜ #1 ๋ถ€๋ถ„์„ ๋ณด๋ฉด ์•Œ ์ˆ˜ ์žˆ๋“ฏ์ด, ์‹ค์ œ๋กœ Flatten์„ ํ•ด์ฃผ๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ๊ทธ์ € Stream์— ๋Œ€ํ•ด forEach() ๋ฅผ ํ•˜๋„๋ก ๊ตฌํ˜„ํ–ˆ์„ ๋ฟ์ด๋‹ค.
ย 
๊ทธ๋ž˜๋„ Parallel Stream์œผ๋กœ ๋ช…์‹œํ–ˆ์œผ๋‹ˆ Parallelํ•˜๊ฒŒ ๋™์ž‘ํ•˜์ง€ ์•Š์„๊นŒ?
ย 
๋ฌผ๋ก  ์ด ์—ญ์‹œ ์•„๋‹ˆ๋‹ค. #1์—์„œ sequential() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋ณด์ผํ…๋ฐ, ์ด ๋ฉ”์„œ๋“œ๋Š” Parallel์„ ๋น„ํ™œ์„ฑํ™” ์‹œํ‚ค๋Š” ์ž‘์—…์„ ๋‹ด๋‹นํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์—... (๋ฌธ์„œ, ์ฝ”๋“œ)
ย 
์ •๋ฆฌํ•ด์„œ ์‰ฝ๊ฒŒ ๋งํ•˜์ž๋ฉด, ์ฝ”๋“œ์˜ #2 ๋ถ€๋ถ„์—์„œ๋Š” String ๋‹จ์œ„๋กœ Stream Pipeline์ด ์ง„ํ–‰๋˜์—ˆ์œผ๋‚˜, #3 ๋ถ€๋ถ„์—์„œ๋Š” List ๋‹จ์œ„๋กœ Stream์ด ์ง„ํ–‰๋œ๋‹ค๋Š” ๋ง์ด๋‹ค.
ย 
๋”ฐ๋ผ์„œ ์œ„์™€ ๊ฐ™์ด Element ๋‹จ์œ„๊ฐ€ ์•„๋‹Œ List ๋‹จ์œ„๋กœ Parallel Stream์ด ์ง„ํ–‰๋˜๋ฉฐ, ์ด๋Š” ์›ํ–ˆ๋˜ ๋ฐฉ์‹์ธ Element ๋‹จ์œ„์˜ Parallel Stream๊ณผ๋Š” ๊ฑฐ๋ฆฌ๊ฐ€ ๋งค์šฐ ๋งค์šฐ ๋งค์šฐ ๋งค์šฐ ๋งค์šฐ ๋ฉ€๋‹ค.
ย 
์ฐธ๊ณ ๋กœ ๋‹ค์Œ ๋‘ ์ฝ”๋“œ ์—ญ์‹œ ์›ํ•˜๋Š” ๊ฒฐ๊ณผ๋ฅผ ๋ณด์—ฌ์ฃผ์ง€ ๋ชปํ•œ๋‹ค.
ย 
public class Main { public static void main(String[] args) { Stream.of("0123456789".split("")) .parallel() .flatMap(Main::genStringListStream) .parallel() .forEach(System.out::println); } private static List<String> genList(String contents) { return Arrays.asList( String.valueOf(new char[10]).replaceAll("\0", contents).split("")); } private static Stream<String> genStringListStream(String contents) { return genList(contents).parallelStream(); } }
// output 5 5 5 6 6 6 8 8 8 9 9 9 7 7 7 2 2 2 3 3 3 4 4 4 0 0 0 1 1 1
ย 
public class Main { public static void main(String[] args) { Stream.of("0123456789".split("")) .parallel() .flatMap(Main::genListStream) .forEach(System.out::println); } private static List<String> genList(String contents) { return Arrays.asList( String.valueOf(new char[10]).replaceAll("\0", contents).split("")); } private static Stream<List<String>> genListStream(String contents) { return Stream.of(genList(contents)).parallel(); } }
// output [2, 2, 2] [5, 5, 5] [6, 6, 6] [8, 8, 8] [9, 9, 9] [7, 7, 7] [0, 0, 0] [1, 1, 1] [3, 3, 3] [4, 4, 4]

Conclusion

์•ˆํƒ€๊น๊ฒŒ๋„ ํ˜„์žฌ๋กœ์„œ ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์€... ๊ฒฐ๊ตญ ์ƒ์„ฑํ•œ List ์— ๋Œ€ํ•œ Stream ์„ ์ƒ์„ฑํ•˜๊ณ , ์ด์— ๋Œ€ํ•ด Parallel Stream์„ ์ง„ํ–‰ํ•˜๋„๋ก ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ• ํ•˜๋‚˜ ๋ฟ์ด๋‹ค.
ย 
์•„๋ฌดํŠผ, ์ด์™€ ๊ฐ™์ด Java Stream์˜ flatMap() ๋ฉ”์„œ๋“œ๋Š” ์‹ค์ œ๋กœ Flat-Map์„ ์ง„ํ–‰ํ•˜์ง€ ์•Š์Œ์„ ์—ผ๋‘ํ•ด ๋‘๊ณ  ๊ฐœ๋ฐœ์„ ํ•˜๋„๋ก ํ•˜์ž. ์ฆ‰, flatMap() ์€ Flat-Map์ด ์•„๋‹ˆ๋‹ค.
ย