ã¹ããªãŒã -ãªããžã§ã¯ãJavaã®äžçã®æ§ä¿¡è
ã«ãšã£ãŠã¯çãããæ©èœäž»çŸ©ã®ç¥ç§çãªäžçã åæã«ãã©ã ãã®äžçãè峿·±ããç°è³ªã§ããããã«ãããä»ã®äººããããèŠãŠãããªããå±éºã«ããããããšæãããŒã¿ã»ããã䜿çšããŠããã®ãããªããšãè¡ãããšãã§ããŸãã
仿¥ã¯ãStream APIã«ã€ããŠã話ããããŸã ç¥ãããŠããªãäžçã§ç§å¯ã®ããŒã«ãéãããšããŸãã Java 8ã¯ããªãåã«ç»å Žãããšããäºå®ã«ããããããã誰ãããããžã§ã¯ãã§ãã®æ©èœã®ãã¹ãŠã䜿çšããããã§ã¯ãããŸããã ãã®Pandoraã®ç®±ãéããŠããã®ãããªç¥ç§çãªçŸè±¡ã®äžã«å®éã«é ããŠãããã®ãèŠã€ããããã«ãç§ãã¡ã¯JetBrains-Tagir
lany Valeevã®éçºè
ã«å©ããããŸãã ã誀ã£ãã¹ããªãŒã ã®æžãæ¹ïŒãç¬èªã®StreamExã©ã€ãã©ãªãäœæããŸãããããã«ãããJavaã¹ããªãŒã ã®äœæ¥ãæ¹åãããŸãã 誰ãé¢çœããªã£ãã®ããã«ããããé¡ãããŸãïŒ
ãã®è³æã¯ã2016幎10æã«ãµã³ã¯ãããã«ãã«ã¯ã§éå¬ãããJokeräŒè°ã§ã®Tagir Valeevã®ã¬ããŒãã«åºã¥ããŠããŸããè¬æŒã®æ°ã¶æåã«ãç§ã¯ãã€ãã¿ãŒã§å°ããªã¢ã³ã±ãŒããè¡ããŸããã
ãããã£ãŠã
Parallel Streamã«ã€ããŠã¯ããŸã説æããŸããã ãšã«ãããããã«ã€ããŠè©±ããŸãããã
ãã®ã¬ããŒãã§ã¯ãããã€ãã®çã«ã€ããŠèª¬æããŸãã

Streamã€ã³ã¿ãŒãã§ãŒã¹ã«å ããŠãJavaã«ã¯ããã€ãã®ã€ã³ã¿ãŒãã§ãŒã¹ãããããšã¯èª°ããç¥ã£ãŠããŸãã
- ã€ã³ãã¹ããªãŒã
- ãã³ã°ã¹ããªãŒã
- ããã«ã¹ããªãŒã
ãããŠãããªããããã䜿çšããã®ããäœããã€ã³ããªã®ãããšããè«ççãªçåãçããããã§ãã

éèŠãªã®ã¯ãé床ãäžããããšã§ãã ããªããã£ãã¯åžžã«é«éã§ãã ãããã£ãŠãå°ãªããšããStreamã®ããã¥ã¡ã³ãã«ç€ºãããŠããŸãã
ããªããã£ãã¹ããªãŒã ãå®éã«é«éãã©ããã確èªããŸãã ãŸããåãStream APIã䜿çšããŠã©ã³ãã ã«çæãããã¹ãããŒã¿ãå¿
èŠã§ãã
int[ ] ints; Integer[ ] integers; @Setup public void setup() { ints = new Random(1).ints(1000000, 0, 1000) .toArray(); integers = new Random(1).ints(1000000, 0, 1000) .boxed().toArray(Integer[]::new); }
ããã§ã¯ã0ã1000ã®ç¯å²ã§100äžåã®æ°å€ãçæããŸãïŒå«ãŸãªãïŒã 次ã«ããããã
intã®ããªããã£ãé
åã«åéãããã®åŸ
æŽæ°ãªããžã§ã¯ãé
åã«ããã¯ããŸãã ãã®å Žåããžã§ãã¬ãŒã¿ãåãçªå·ã§åæåãããããååŸããçªå·ã¯ãŸã£ããåãã§ãã
æ°åã«å¯ŸããŠããã€ãã®æäœãå®è¡ããŸããã-äž¡æ¹ã®é
åã«ããäžæã®æ°åã®æ°ãèšç®ããŸãã
@Benchmark public long stream() { return Stream.of(integers).distinct().count(); } @Benchmark public long intStream() { return IntStream.of(ints).distinct().count(); }
ãã¡ãããçµæã¯åãã«ãªããŸãããåé¡ã¯ã©ã®Streamãã©ã®ãããéããªããã§ãã ããªããã£ãã¹ããªãŒã ã¯ããé«éã«ãªããšèããŠããŸãã ããããæšæž¬ããªãããã«ããã¹ãã宿œããåŸããããã®ã確èªããŸãã

ããŒããããªããã£ãStreamãå¥åŠãªããšã«ã倱ãããŸããã ãã ããJava 1.9ã§ãã¹ããå®è¡ãããšãããªããã£ãã¯é«éã«ãªããŸãããããã§ãååæªæºã§ãã ãããªããã£ãã¹ããªãŒã ãããé«éã§ãããšãŸã çŽæããŠããã®ã«ããªããããªã®ãããšããçåãçããŸãã ãããçè§£ããã«ã¯ããœãŒã¹ã³ãŒããèŠãå¿
èŠããããŸãã ããšãã°ãdistinctïŒïŒã¡ãœãããšãããªããã£ãStreamã§ã®åäœæ¹æ³ãæ€èšããŠãã ããã ã¯ããããã§ãã¹ãŠãã¯ã£ããããŠããããã«èŠããŸãããStreamã¯ã€ã³ã¿ãŒãã§ã€ã¹ã§ãããããåœç¶ãå®è£
ã¯ãããŸããã å®è£
å
šäœã¯
java.util.streamããã±ãŒãžã«ããããããªãã¯ããã±ãŒãžã«å ããŠãå®éã«ã¯å®è£
ã§ããå€ãã®ãã©ã€ããŒãããã±ãŒãžããããŸãã Streamãå®è£
ããã¡ã€ã³ã¯ã©ã¹ã¯
ReferencePipelineã§ ãããã¯
AbstractPipelineãç¶æ¿ã
ãŸã ã ããã«å¿ããŠãããªããã£ãã¹ããªãŒã ã®å®è£
ãå®è¡ãããŸãã

ãããã£ãŠãIntPipelineã«ç§»åããŠãdistinctïŒïŒã®å®è£
ã確èªããŸãã
@Override public final IntStream distinct() {
ããªããã£ãåã®ãããã³ã°ãããããã®äžã§ç°ãªãStreamåŒã³åºãã圢æãããŠããããšãããããŸãããã®åŸãããªããã£ãStreamã圢æãããŸãã
ãããã£ãŠãããã眮ãæãããšã2çªç®ã®ã±ãŒã¹ã§åãä»äºããããããã«å€ãã®ä»äºããããŸãã ããªããã£ãæ°ã®ã¹ããªãŒã ãååŸããŠããã¯ãã
distinctïŒïŒããã³
mapToIntïŒïŒãåŒã³åºããŸãã
MapToIntèªäœ
ã¯ã»ãšãã©äœã
é£ã¹ãŸããããããã±ãŒãžã«ã¯ã¡ã¢ãªãå¿
èŠã§ãã æåã®ã±ãŒã¹ã§ã¯ããªããžã§ã¯ãããã§ã«ãããããã¡ã¢ãªãäºåã«å²ãåœãŠãããŠããŸããã2çªç®ã®ã±ãŒã¹ã§ã¯ãã¡ã¢ãªãå²ãåœãŠãå¿
èŠããããããã§GCãåäœãå§ããŸãã 衚瀺ããããã®ïŒ

æåã®ã±ãŒã¹ã§ã¯ããã¹ãã§ã¯48 kBã®ã¡ã¢ãªã䜿çšããŸããããã¯äž»ã«
HashSetãµããŒãã«äœ¿çšããã
distinctå
ã§äœ¿çšãããŠãæ¢ã«ååšããæ°å€ã確èªããŸãã 2çªç®ã®ã±ãŒã¹ã§ã¯ãããªãå€ãã®ã¡ã¢ãªãçŽ13ã¡ã¬ãã€ããå²ãåœãŠãŸãã
ããããäžè¬çã«ãç§ã¯ããªããå®å¿ããããã§ããããã¯èŠåã®å¯äžã®äŸå€ã§ãã äžè¬ã«ãããªããã£ãã¹ããªãŒã ã®åäœã¯ã¯ããã«é«éã§ãã ãããJDKã§è¡ãããçç±-Javaã«ã¯ããªããã£ãçšã®ç¹å¥ãªã³ã¬ã¯ã·ã§ã³ããªãããã§ãã ããªããã£ã
intã« distinctïŒïŒãå®è£
ããã«ã¯ãããªããã£ãåã®ã³ã¬ã¯ã·ã§ã³ãå¿
èŠã§ãã åæ§ã®æ©èœãæäŸããã©ã€ãã©ãªããããŸãã
ããããããã¯JDKã«ã¯åœãŠã¯ãŸããã
intã«ããªããã£ã
HashSetãå®è£
ããå Žåã
longãš
doubleã®äž¡æ¹ã«
HashSetãäœæããå¿
èŠããããŸãã ãã©ã¬ã«ã¹ããªãŒã ãããããã©ã¬ã«ã¹ããªãŒã ã¯
é åºä»ãããã³
é åºãªãã§ãã é åºä»ãã«ã¯
LinkedHashSetããããé åºãªãã§ã¯Concurrentãå¿
èŠã§ãã ãããã£ãŠã倧éã®ã³ãŒããå®è£
ããå¿
èŠããããŸããã誰ããããæžããŠããŸããã 10çªç®ã®Javaã§ãªãªãŒã¹ãããå¯èœæ§ã®ãã
Genericã®å°éåã誰ããæåŸ
ããŠããŸãã
å¥ã®çãèŠãŠã¿ãŸãããã

åŒãç¶ãã©ã³ãã ãªæ°åã§éãã§ã100äžã®æ°åãåããŸãããã ç¯å²ã¯ãã倧ãããªããŸã-0ãã50,000ïŒå«ãŸãªãïŒã«ãªãã®ã§ãæ°åãç¹°ãè¿ãããé »åºŠã¯å°ãªããªããŸãã äºåã«ãããããœãŒãããããªããã£ãé
åã«å
¥ããŸãã
private int[] data; @Setup public void setup() { data = new Random(1).ints(1_000_000, 0, 50_000) .sorted().toArray(); }
次ã«ãdistinctã䜿çšããŠãäžæã®æ°å€ã®åèšãèšç®ããŸãã
@Benchmark public int distinct() { return IntStream.of(data).distinct().sum(); }
ããã¯æãåçŽãªå®è£
ã§ãããåäœããçµæãæ£ããèšç®ããŸãã ããããç§ãã¡ã¯ãã£ãšéãäœããèãåºãããã§ãã ããã«ããã€ãã®ãªãã·ã§ã³ãæäŸããŸãïŒ
@Benchmark public int distinct() { return IntStream.of(data).distinct().sum(); } @Benchmark public int sortedDistinct() { return IntStream.of(data).sorted().distinct().sum(); } @Benchmark public int boxedSortedDistinct() { return IntStream.of(data).boxed().sorted().distinct() .mapToInt(x -> x).sum(); }
第2ã®å®æœåœ¢æ
ã§ã¯ã
distinctïŒïŒã®åã«å床ãœãŒããã第3ã®ããŒãžã§ã³ã§ã¯ãåŒãç¶ãpackãsortã
distinctïŒïŒãå®è¡ããããªããã£ãé
åã«ãã£ã¹ãããŠããéèšããŸãã
çåãçããŸãïŒããªããœãŒãããã®ã§ããïŒ ããããç§ãã¡ãšäžç·ã«ãã¹ãŠãæŽçãããŸããã ãã®å Žåãåèšã®çµæã¯ãœãŒãã«äŸåããŸãããã
次ã«ãåžžèã«åºã¥ããŠãæåã®ãªãã·ã§ã³ãæãéãã2çªç®ãé
ãã3çªç®ãæãé·ããšæ³å®ã§ããŸãã
ãã ãã
distinctïŒïŒã¯ããã±ãŒãžåãè¡ããããªããã£ãåã«ã€ãªããããšãæãåºãããšãã§ããŸãããããã£ãŠãäžèšã®äŸã¯æ¬¡ã®ããã«è¡šãããšãã§ããŸãã
@Benchmark public int distinct() { return IntStream.of(data).boxed().distinct() .mapToInt(x -> x).sum(); } @Benchmark public int sortedDistinct() { return IntStream.of(data).sorted().boxed().distinct() .mapToInt(x -> x).sum(); } @Benchmark public int boxedSortedDistinct() { return IntStream.of(data).boxed().sorted().distinct() .mapToInt(x -> x).sum(); }
ãããã®äŸã¯äºãã«ããé¡äŒŒããŠããŸããããããã«ããŠããããã±ãŒãžã³ã°ã¯ââ远å äœæ¥ãšããŠè¡ããŸãã çµæãèŠãŠã¿ãŸãããïŒ

äºæ³ã©ããã2çªç®ã®ãªãã·ã§ã³ã¯åäœãé
ããªããŸãããæåŸã®ãªãã·ã§ã³ã¯ãå¥åŠãªããšã«ãæãéãåäœããŸãã ç¥ç§çãªè¡åã§ã¯ãããŸãããïŒ
äžè¬ã«ãããã€ãã®èŠå ãäžåºŠã«åºå¥ã§ããŸãã ãŸããããŒã¿ãæ¢ã«ãœãŒããããŠããå ŽåãJavaã§ã®ãœãŒãã¯é«éã§ãã ã€ãŸããæ°åãæ£ããé åºã«ãªã£ãŠããããšãããããšãããã«éåºããŸãã ãããã£ãŠããœãŒãã®ãœãŒãã¯ååã«å®äŸ¡ã§ãã ãã ããStream'eã®sort
ïŒïŒæäœã¯ãæ¢ã«ãœãŒããããŠãããšããç¹æ§ã远å ããŸãã æåã«é
åããã§ã«äžŠã¹ãŠããŸããããStreamã¯ããã«ã€ããŠç¥ããŸããã ç§ãã¡ã ããããã«ã€ããŠç¥ã£ãŠããŸãã ãããã£ãŠã
distinctïŒïŒããœãŒããããStreamãæ€åºãããšãããå¹ççãªã¢ã«ãŽãªãºã ãå«ãŸããŸãã 圌ã¯ãã¯ã
HashSetãåéããŠéè€ããçªå·ãæ¢ãã®ã§ã¯ãªããæ¬¡ã®åçªå·ãåã®çªå·ãšåã«æ¯èŒããŸãã ã€ãŸããçè«çã«ã¯ãå
¥åããŒã¿ãæ¢ã«ãœãŒããããŠããå ŽåããœãŒãã圹ç«ã¡ãŸãã ãã®åŸã2çªç®ã®ãã¹ãã3çªç®ã®ãã¹ããããé
ãçç±ã¯æããã§ã¯ãããŸããã ãããçè§£ããã«ã¯ã
boxedïŒïŒã¡ãœããã®å®è£
ãèŠãå¿
èŠããããŸãïŒ
ãããŠãã³ãŒãã§ããã眮ãæãããšïŒ
@Benchmark public int distinct() { return IntStream.of(data).mapToObj(Integer::valueOf) .distinct().mapToInt(x -> x).sum(); } @Benchmark public int sortedDistinct() { return IntStream.of(data).sorted().mapToObj(Integer::valueOf) .distinct().mapToInt(x -> x).sum(); } @Benchmark public int boxedSortedDistinct() { return IntStream.of(data).mapToObj(Integer::valueOf).sorted() .distinct().mapToInt(x -> x).sum(); }
ãŸãã
mapToObjïŒïŒã¯ãStreamããœãŒããããŠããç¹æ§ãåé€ããŸãã 3çªç®ã®ã±ãŒã¹ã§ã¯ããªããžã§ã¯ãããœãŒããã
distinctïŒïŒãæ¯æŽããŸããããã«ãããåäœãéããªããŸãã ãŸãã
mapToObjïŒïŒããããã®éã«ããå Žåããã®ãœãŒãã¯ç¡æå³ã«ãªããŸãã
ç§ã«ã¯å¥åŠã«æããã
boxedïŒïŒãããå°ãé·ãèšè¿°ããStreamãœãŒã仿§ãä¿æã§ããŸãã ããã§ãJava 1.9ã«ããããå°å
¥ããŸããã

ã芧ã®ããã«ãããããé©çšããåŸãçµæã¯éåžžã«è峿·±ããã®ã«ãªããŸãã 2çªç®ã®ãªãã·ã§ã³ã¯ãããªããã£ãã§ãœãŒãããããããåªå
ãããŸãã ãªããžã§ã¯ãããœãŒãããããã3çªç®ã®ãªãã·ã§ã³ã¯2çªç®ã«å°ã倱ãããŸãããåæã«æåã®ãªãã·ã§ã³ãããåªããŠããŸãã
ã¡ãªã¿ã«ãããŒãžã§ã³9ã§ãã¹ããå®è¡ããå Žåã-XXïŒ+ UseParallelGCãªãã·ã§ã³ã䜿çšããŸãããããŒãžã§ã³8ã§ã¯ããã©ã«ãã§ãããããŒãžã§ã³9ã§ã¯ããã©ã«ãã§G1ã§ããããã§ãã ãã®ãªãã·ã§ã³ãåé€ãããšãçµæã¯å€§ããç°ãªããŸãã

ãããã£ãŠãããŒãžã§ã³9ã«åãæ¿ãããšãåäœãé
ããªãå¯èœæ§ãããããšã«æ³šæããŠãã ããã
次ã®çã«ç§»ããŸãããã

次ã®ã¿ã¹ã¯ãå®è¡ããŸãã ãã®å®è£
ã«ã¯ã20é¢äœã䜿çšããŸãã äºåé¢äœã¯ã20åã®é¢ãæã€ãã®ãããªæ£å€é¢äœã§ãã

Stream APIã䜿çšããŠãããè¡ããŸãã
ãã©ã¡ãŒã¿ãèšå®ããååŸããå€ãèŠçŽããŸãã ãã®ã¢ãããŒãã§ã¯ãçµæãå®å
šã«æ£ãããšã¯éããŸããã 5ã€ã®
æ¬äŒŒä¹±æ°ã®åèšãå¿
èŠã§ãã ãããŠãç¹°ãè¿ãã«ãªãããšããããŸãïŒ
distinctïŒïŒã远å ãã
ãš ãç¹°ãè¿ããã¹ããŒããããããããã圹ã«ç«ã¡ãŸãããåèšã§4æ¡ä»¥äžã«ãªããŸãã

ããŒãžã§ã³ãããå°ãæ¬æ Œçã«ããããšã¯ç§ãã¡ã«æ®ã£ãŠããŸãïŒ
intïŒïŒãåããå¿
èŠãªæ°åã®æ°ãèšå®ãããç¹å®ã®æ¹æ³ã§çæãããæ°åãå¿
èŠã§ããããšã瀺ããŸãã ç¡éã®ã¹ããªãŒã ãååŸããŸãããã®å Žåã
distinctïŒïŒã¯ç¹°ãè¿ãã®æ°ããã§ãã¯ãã
limitïŒïŒã¯ 5ã€ã®æ°ãåãåã£ãåŸãæ°ã®çæã忢ããŸãã
次ã«ããã®ã¿ã¹ã¯ã䞊ååããŠã¿ãŸãããã ãããè¡ãã®ã¯ç°¡åã§ã¯ãããŸããããéåžžã«ç°¡åã§ãã
parallelïŒïŒã远å
ããã®ã«ååã§ã䞊åStreamããããŸãã äžèšã®äŸã¯ãã¹ãŠã³ã³ãã€ã«ãããŸãã äžèšã®äŸã®éãã¯äœã ãšæããŸããïŒ éãããããšæ³å®ã§ããŸãã ããããã ãšããã°ãããã¯ããªãã®ããã§ã¯ãããŸãããããã¥ã¡ã³ãã«ã¯ããã«ã€ããŠã»ãšãã©äœãæžãããŠããããå®éãå€ãã®äººãåãããã«èããŠããããã§ãã ãã ããå®éã«ã¯éãã¯ãããŸããã ãã¹ãŠã®ã¹ããªãŒã ã«ã¯ããã©ã¬ã«ãŸãã¯ã¬ã®ã¥ã©ãŒãšããŠèšè¿°ããããŒã«å€æ°ãããããŒã¿æ§é ããããŸãã ãŸããStreamãå®è¡ããåã«
parallelïŒïŒãèšè¿°
ããå Žåã¯åžžã«ããã®ç¹å¥ãªå€æ°ãtrueã«èšå®ãããã®åŸãç«¯æ«æäœã¯ãã®å€æ°ããã£ãå€ã§ããã䜿çšããŸãã
ç¹ã«ã次ã®ããã«æžãå ŽåïŒ
new Random().ints(1, 20+1).parallel().distinct().limit(5) .sequential().sum();
å¥åã®ïŒïŒãšå¶éïŒïŒã®ã¿ã䞊è¡ããŠå®è¡ãããåèšïŒïŒãé çªã«å®è¡ããããšèãããããããŸããã å®ã¯ããã§ã¯ãããŸãã
ãsequentialïŒïŒã¯ãã§ãã¯ããã¯ã¹ãã¯ãªã¢ããStreamå
šäœãé æ¬¡å®è¡ãããããã§ãã
9çªç®ã®ããŒãžã§ã³ã§ã¯ã人ã誀解ãããªãããã«ããã¥ã¡ã³ããæ¹åãããŸããã ãã®ããã«å¥ã®ãã±ãããäœæãããŸããã

é æ¬¡ã¹ããªãŒã ã®å®è¡ã«ãããæéãèŠãŠã¿ãŸãããã

ã芧ã®ãšãããå®è¡ã¯éåžžã«é«éã§ã-286ããç§ã
æ£çŽã«èšããšã䞊ååãããé«éã«ãªããšã¯æããŸããã 倧ããªã³ã¹ã-ã¿ã¹ã¯ãäœæããããã»ããµã«åæ£ããŸãã 200ããç§ããé·ãããå¿
èŠããããŸã-ãªãŒããŒãããã倧ããããŸãã
䞊åStreamã®å®è¡æéã¯äœåã§ããïŒ 10åã20åããŸãã¯ç¡éã«éåžžã«é·ãæéã§ããïŒ ãã¹ãã¯çŽ6,000幎éå®è¡ããããããå®çšçãªèгç¹ããã¯åŸè
ãé©åã§ãã

ããããããªãã®ã³ã³ãã¥ãŒã¿ãŒäžã§ããã¹ãã¯æ°å幎å€ããå°ãªããå®è¡ãããŸãã ãã®åäœã®çç±ãçè§£ããã«ã¯ãå°ãæãäžããå¿
èŠããããŸãã ããã¯ãã¹ãŠãããã€ãã®å®è£
ãåãããã¡ã³ã·ãŒ
å¶éïŒïŒæäœã«é¢ãããã®ã§ãã ã·ãŒã±ã³ã¹ã䞊ååŠçãããã³ãã®ä»ã®ãã©ã°ã«ãã£ãŠåäœãç°ãªãããã§ãã ãã®å Žåã
java.util.stream.StreamSpliterators.UnorderedSliceSpliterators <TãT_SPLIT>ãæ©èœããŸãã ã³ãŒãã¯è¡šç€ºããŸããããã§ããã ãç°¡åã«èª¬æããããšããŸãã
é äžåã®çç± ä¹±æ°ã®ãœãŒã¹ã¯ããã®ã¹ããªãŒã ãé åºä»ããããŠããªãããšã瀺ããŠããããã§ãã ãããã£ãŠã䞊ååäžã«é åºã倿ŽããŠããèª°ãæ°ä»ããªãã§ãããã ãããŠãé äžåã®å¶éãå®è£
ããã®ã¯ç°¡åã«æããŸãã-ã¢ãããã¯å€æ°ã远å ããã€ã³ã¯ãªã¡ã³ãããŸãã ãããå¢ãããŸãããããŸã éçã«éããŠããŸãã-
å¥ã®ïŒïŒã«å¥ã®çªå·ãäžããŠå ç®åšã«æž¡ãããã«äŸé ŒããŸãã ã¢ãããã¯å€æ°ã5ã«çãããªããšããã«ãèšç®ã忢ããŸãã
ãã®å®è£
ã¯ãJDKéçºè
åãã§ãªããã°æ©èœããŸãã 圌ãã¯ããã®ãããªå®è£
ã§ã¯ããã¹ãŠã®ã¹ã¬ãããåãã¢ãããã¯å€æ°ã䜿çšãããšããäºå®ã®ããã«ãç«¶åãå€ããããšå€æããŸããã ãããã£ãŠã圌ãã¯1ã€ã®çªå·ã§ã¯ãªããå128ãååŸããããšã«ããŸãããã€ãŸããåã¹ã¬ããã¯ã¢ãããã¯å€æ°ã128å¢ãããããé«ããœãŒã¹ãã128ã®çªå·ãååŸããŸãããã«ãŠã³ã¿ãŒã¯æŽæ°ãããªããªãã128ã®åŸã®ã¿æŽæ°ãè¡ãããŸãã ããšãã°10,000ã«å¶éãããå Žåãããã¯è³¢æãªæ±ºå®ã§ããããã®ãããªå°ããªå¶éãããå Žåã¯ä¿¡ããããªãã»ã©æãã§ãã çµå±ã5ã€ä»¥äžã¯å¿
èŠãªãããšãäºåã«ããã£ãŠããŸãã ãã®ãœãŒã¹ãã128åã®æ°åãååŸããããšã¯ã§ããŸããã éåžžãæåã®20åã®æ°åã䜿çšãã21ã§
å¥ã®ïŒïŒã䜿çšããŠãã1ã€æ°åãæå®ããŸãã 圌ã¯ããã¥ãŒãããããããååŸããããšããŸãã圌ã¯ãããäžããŸãã ããšãã°ã
distinctïŒïŒã¯çªå·10ãååŸããŸãããããããããã¯ãã§ã«ãã£ãããšdistinctïŒïŒãèšããããã«èŠæ±ããŸãã 圌ã¯3çªãååŸããŸããã圌ã¯ãã§ã«ãããæã£ãŠããŸããã ãŸãã
distinctïŒïŒããã¥ãŒãã®ãã¹ãŠã®é¢ããã§ã«èŠãŠããã圌ã¯ãã¥ãŒããçµãã£ãããšãç¥ããªãããã誰ããã®ããã»ã¹ã忢ããŸããã ããã¯ç¡éã«çºçããã¯ãã§ããã
int ïŒïŒã®ããã¥ã¡ã³ããèŠããšãStreamã¯ç¡éã§ã¯ãªããäºå®äžç¡å¶éã§ãã ç¹ã«
Long.MAX_VALUEèŠçŽ
ãå«ãŸããŠãããããæç¹ã§çµäºããŸãã

ç§ã«ã¯å¥åŠã«æããŸããããããŒãžã§ã³9ã§ãã®åé¡ãä¿®æ£ããŸããã

ãããã£ãŠãããã©ãŒãã³ã¹é害ãçºçããŸããããã¯éåžžã«é©åã§ãçŽ20ã25åã§ãã ãã ããç¹å®ã®äŸã§ãã®åé¡ãä¿®æ£ãããšããŠããããããŸã£ããä¿®æ£ãããããšãæå³ãããã®ã§ã¯ãªãããšãèŠåããŸãã ããã¯ããã©ãŒãã³ã¹ã®åé¡ã§ãããStreamãæ£ããå®è£
ãããŠãããšããåé¡ã§ã¯ãããŸããã
ããã¥ã¡ã³ãã§ã¯ã
limitïŒ5ïŒãæå®ããå ŽåããœãŒã¹ããæ£ç¢ºã«5ã€ã®æ°å€ãèªã¿åããšã¯æžãããŠããŸããã
findFirstãããå Žåãããã¯1ã€ã®æ°åãèªãããšãæå³ãããã®ã§ã¯ãããŸããã奜ããªã ãèªãããšãã§ããŸãã ãããã£ãŠãç¡éã®ã¹ããªãŒã ã«ã¯æ³šæããå¿
èŠããããŸãã ãªããªãã5åã§ã¯ãªã18åã®æ°åãå¶éãšããŠäœ¿çšãããšãåãåé¡ã«åã³çŽé¢ããå¯èœæ§ãããããã§ãã 18ã®æ°å€ããã§ã«èªã¿åãããŠãããããä»ã®3ã€ã®äžŠåã¹ã¬ããããã1ã€ãèŠæ±ãã21ã«ãªããŸãããããã£ãŠããã®ãããªæäœã¯äžŠååããªãã§ãã ããã ãã©ã¬ã«ã¹ããªãŒã ã䜿çšãããšãæããã§ã-ç絡æäœãããå Žåã¯ãäºæ³ãããã¯ããã«å€ãã®æžç®ãè¡ãããŸãã

é æ¬¡ã¹ããªãŒã ã§ã¯ããã®äŸã«ã¯å¥åŠãªç¹ããããŸãã

äŸã¯å°ã人工çãããããŸããããããã¯ããã€ãã®ã¢ã«ãŽãªãºã ã«çŸãããããããŸããã æŽæ°ã®é
åãåé¿ãããããããªãããŒãªæ¹æ³ã§åé¿ãããã 0èŠçŽ ããå§ããŸãããããã®èŠçŽ ã®å€ã¯ãååŸããæ¬¡ã®èŠçŽ ã®ã€ã³ããã¯ã¹ã§ãã Stream APIã䜿çšããŠåé¿ãããã®ã§ãStream.iterateïŒïŒã¡ãœãããèŠã€ããŸããããã¯ãã¿ã¹ã¯çšã«äœæãããããã§ãã

æåã®StreamèŠçŽ ã¯é
åå
ã®ã€ã³ããã¯ã¹ã§ããã2çªç®ã¯æé·é¢æ°ã§ãã åã®èŠçŽ ããæ¬¡ã®èŠçŽ ãå®è¡ãã颿°ã ãã®äŸã§ã¯ãèŠçŽ ãã€ã³ããã¯ã¹ãšããŠäœ¿çšããŸãã ãã ããæåã®èŠçŽ 0ã¯ã€ã³ããã¯ã¹ã§ãããå¿
èŠãªãããã
skipïŒ1ïŒã§
ã¹ãããããŸãã æ¬¡ã«ãStreamãé
åã®é·ãã«å¶éããŠè¡šç€ºããããå¥ã®ã¢ã«ãŽãªãºã ãå®è¡ããŸããããšãã°ãå¥ã®ã¢ã«ãŽãªãºã ã䜿çšãããŸãã
ãã¹ãŠãæ£ããåäœãããã£ããã¯ãããŸããã ããããããã«ã¯æŽæ°ãããã®ã§ããªã
IntStreamã䜿çšã
ãªãã®ã§ããïŒ ãã®ã€ã³ã¿ãŒãã§ã€ã¹ã«ã¯ãå埩åŠçãšãã®ä»ã®ãã¹ãŠã®æäœããããŸãã
IntStreamãäœæ
ãããš ãæ¬¡ã®æ
å ±ãåãåããŸãã
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException at test.Iterate.lambda$0(Iterate.java:9) at test.Iterate$$Lambda$1/424058530.applyAsInt(Unknown Source) at java.util.stream.IntStream$1.nextInt(IntStream.java:754) ⊠at test.Iterate.main(Iterate.java:12)
åé¡ã¯ãããã¯
IntStream.iterateïŒïŒã®å®è£
ã®è©³çްã§ãããã
Stream.iterateïŒïŒã«ã¯ãã®è©³çްããªãããšã§ãã çªå·ãäžãããããã³ã«ãããã«ä»¥äžãèŠæ±ãããŸãã ããã¯å€æ°ã«ä¿åããã以åã®çªå·ãè¿ãããŸãã ãããã£ãŠã-1ãååŸããããšãããšããœãŒã¹ããã€ã³ããã¯ã¹-1ãæã€é
åã®å€ãååŸããããšããŸãããããã¯ãšã©ãŒã«ã€ãªãããŸãã ç§ã«ã¯å¥åŠã«æããã®ã§ãä¿®æ£ããŸããã

ããããããã¯åãªãå¥åŠãªå®è£
ã§ãããåäœã仿§ã«æºæ ããŠãããããããããã°ãšåŒã¶ããšã¯ã§ããŸããã

ã¹ããªãŒã ã¯æ¬åœã«ããã倧奜ãã§ãïŒ
Map<String, String> userPasswords = Files.lines(Paths.get("/etc/passwd")) .map(str -> str.split(":")) .collect(toMap(arr -> arr[0], arr -> arr[1]));
ãã¡ã€ã«ãååŸããŠããœãŒã¹ãšããŠäœ¿çšã§ããŸãã æååã®ã¹ããªãŒã ã«å€ããããé
åã«å€ããããèŠéãããããŸãã ãã¹ãŠãçŸããããã¹ãŠãäžè¡ã§ãã®ããã«ãªã£ãŠããŸã-ç§ãã¡ã¯çæµã§ãïŒ

ãããããã®ã³ãŒãã«ã¯äœããæ¬ ããŠãããšæããŸãããïŒ ãã¶ã
ãã©ã€ãã£ãã ïŒ ã»ãšãã©ããããå®å
šã§ã¯ãããŸããã
try-with-resourcesãäžåå
ã§ã ã ãã¡ã€ã«ãéããå¿
èŠããããŸããããããªããšããã¡ã€ã«èšè¿°åãäžè¶³ããå¯èœæ§ããããŸãããWindowsã§ã¯ããã«æªãããšã«ãåŸã§äœãããŸããã
å®éãã³ãŒãã¯ãã§ã«æ¬¡ã®ããã«ãªã£ãŠããã¯ãã§ãã

ãããŠä»ããã¹ãŠãããã»ã©ãã©è²ã«èŠããŸããã tryãæ¿å
¥ããå¿
èŠãããããã®ããã«å¥ã®å€æ°ãååŸããŸããã ããã¯ãã³ãŒããèšè¿°ããããã®æšå¥šãããæ¹æ³ã§ããã€ãŸããç§ã¯ãããæãã€ããŸããã§ããã Streamããã¥ã¡ã³ãã¯ããã®æ¹æ³ã§ãããè¡ããšèšã£ãŠããŸãã
åœç¶ãäžéšã®äººã
ã¯ããã奜ãŸãããããä¿®æ£ããããšããŠããŸãã ããšãã°ãã«ãŒã«ã¹ã»ãšããŒã¯ããã詊ã¿ãŸããã 圌ã¯çŽ æŽããã人ã§ããã®ãããªã¢ã€ãã¢ãæäŸããŸãã 圌ã¯StackOverfowã«é¢ããè°è«ã«
çåãæããããŸãã ã ããã¯ãã¹ãŠå¥åŠã§ãããStreamããã€åäœãçµäºãããã¯ããã£ãŠããŸããç«¯æ«æäœããããåŒã³åºããåŸã¯ã絶察ã«å¿
èŠãããŸããã éããŠã¿ãŸãããã
ã¹ããªãŒã ã¯ã€ã³ã¿ãŒãã§ãŒã¹ã§ãããããå©çšããŠå®è£
ã§ããŸãã JDKããæäŸãããStreamãžã®ããªã²ãŒããäœæãããã¹ãŠã®ç«¯æ«æäœãåå®çŸ©ããŸããããå
ã®ç«¯æ«æäœãåŒã³åºããŠStreamãéããŸãã
ãã®ã¢ãããŒãã¯æ©èœãããã¹ãŠã®ãã¡ã€ã«ãæ£ããéããŸããïŒ ãã¹ãŠã®ç«¯æ«æäœã®ãªã¹ããæ€èšããŠãã ãã-ãããã¯2ã€ã®ã°ã«ãŒãã«åãããŠããŸãã1ã€ã¯ãéåžžã®ãæäœïŒå
éšãã€ãã¹ïŒããã1ã€ã¯ïŒå¥åŠãªããïŒãBizarreãïŒå€éšãã€ãã¹ïŒãšåŒã°ããŸãã

ãå¥åŠãªãæäœã¯å
šäœåãå°ç¡ãã«ããŸãã ãã¡ã€ã«ã®Streamè¡ãäœæããå€ãAPIã«è»¢éãããå Žåãæ³åããŠãã ãããå€ãAPIã¯Streamã§ã¯äœãç¥ããªãããã€ãã¬ãŒã¿ãŒã«ã€ããŠã¯ç¥ã£ãŠããŸãã åœç¶ããã®Streamããã€ãã¬ãŒã¿ãååŸããŸããããã¡ã€ã«å
šäœãã¡ã¢ãªã«ããŒãããå¿
èŠã¯ãããŸããã Streamsã¯é
å»¶ããŠãããããããããé
å»¶ãããããã«ããŸãã ã€ãŸããå®éã«ã¯ãç«¯æ«æäœã¯ãã§ã«åŒã³åºãããŠããŸãããéããŠãããã¡ã€ã«ã¯ãŸã å¿
èŠã§ãã ãã®åŸãã€ãã¬ãŒã¿ã³ã³ãã©ã¯ãã¯ããã®ã€ãã¬ãŒã¿ãéããå¿
èŠãããããšãããã®åŸã€ãã¬ãŒã¿ãäžèŠã§ããããšãæå³ããŸããã
iterator.nextïŒïŒã 1ååŒã³åºããŠããããããã§ããŸãã , , . , .
Spliterator â , .
tryAdvance() ,
hasNext() next() , . â , . , , , . , :

, , â Stream . â .
, , Stream .
flatMap :

, , . , Stream. , Stream , Stream.
flatMap() , Stream . ,
try-with-resources . :
Map<String, String> userPasswords = Stream.of(Files.lines(Paths.get("/etc/passwd"))) .flatMap(s -> s) .map(str -> str.split(":")) .collect(toMap(arr -> arr[0], arr -> arr[1]));
Stream.of(Files.lines(âŠ)) â Stream'a, Stream.
flatMap() Stream.
flatMap() , Stream . .
try .
, ? , . , . - «:» arr[1]
ArrayIndexOutOfBoundsException , ,
flatMap() . , .
JPoint , Stream
flatMap() , Stream .

, Stream, Stream ,
limit() findFirst() â Stream . Stream, . Stream ( ).
flatMap() , , Stream,
flatMap() , . . ,
tryAdvance() :

. Stream
flatMap() Stream 1 000 000 000 . Stream'a
spliterator() tryAdvance() â Stream. â
OutOfMemoryError .
tryAdvance() ,
flatMap() â Stream â . JPoint, .
, :
Files.lines(Paths.get<("/etc/passwd")) .spliterator().tryAdvance(...);
,
spliterator().tryAdvance(), Stream . ,
BufferedReader . , . ,
tryAdvance() .
flatMap() , , ,
spliterator . . . â
flatMap() , , . , ,
flatMap .
(
flatMap ). JDK, . , Stream ,
spliterator().tryAdvance(), . . JDK ? , Stream . ,
flatMap() Stream.

. ? , . , Stream, , . , ? .

0 1 000 000. 1 000 000 , 1 000 000. Stream :

@Benchmark public long count() { return IntStream.rangeClosed(0, 1_000_000).boxed() .filter(x -> x == 1_000_000).count(); } @Benchmark public Optional<Integer> findAny() { return IntStream.rangeClosed(0, 1_000_000).boxed() .filter(x -> x == 1_000_000).findAny(); }
rangeClosed() , .
Boxed() , , . . Stream . , , â
count() ,
findAny() . , . , ?
0 1 000 000, - , . . ,
branch predictor .., .
. - ( ), - ( ). . , :
FindAny() , â 25%. , . , ,
IntStream.rangeClosed() . â
ArrayList . , :
List<Integer> list; @Setup public void setup() { list = IntStream.rangeClosed(0, 1_000_000).boxed() .collect(Collectors.toCollection(ArrayList::new)); } @Benchmark public long count() { return list.stream().filter(x -> x == 1_000_000).count(); } @Benchmark public Optional<Integer> findAny() { return list.stream().filter(x -> x == 1_000_000).findAny(); }
, , ( ):

. 65%. . , , Stream,
forEachRemaining, , .
tryAdvance() â
tryAdvance() , ,
tryAdvance() .
,
forEachRemaining spliterator . , ( ),
tryAdvance() , , . , , spliterator JIT- , . ,
modCount() , - ,
ConcurrentModificationExceptions .
forEachRemaining modCount() .
tryAdvance() . ,
tryAdvance() . . Stream , .
, , Stream:
@Benchmark public Optional<Integer> findAny1() { return IntStream.range(0, 1_000_000) .boxed().filter(x -> x == 0).findAny(); } @Benchmark public Optional<Integer> findAny1Flat() { return IntStream.of(1_000_000).flatMap(x -> IntStream.range(0, x)) .boxed().filter(x -> x == 0).findAny(); }
1 000 000, :

83 , . , Stream'a, Stream , , . 54 000 , , .
:

, - . , ,
forEachRemaining, - «» â , , - , . -
forEach , . , , â
Consumer , . , . -
forEachRemaining ?
Exception ?
Exception Control Flow, (
http://c2.com/cgi/wiki?DontUseExceptionsForFlowControl ):
⊠- ?
â :
static class FoundException extends RuntimeException { Object payload; FoundException(Object payload) { this.payload = payload; } } public static <T> Optional<T> fastFindAny(Stream<T> stream) { try { stream.forEach(x -> { throw new FoundException(x); }); return Optional.empty(); } catch (FoundException fe) { @SuppressWarnings({ "unchecked" }) T val = (T) fe.payload; return Optional.of(val); } }
,
RuntimeException ,
checked . â
fastFindAny (). , Stream â , Stream. â ,
forEach . ,
forEachRemaining . ,
Exception . , , , .
Exception , â
Optinal .
findAny() ? â ? . , , - .
â
findAny() «»
fastFindAny() .
@Benchmark public Optional<Integer> findAny() { return IntStream.rangeClosed(0, 1_000_000).boxed() .filter(x -> x == 1_000_000).findAny(); } @Benchmark public Optional<Integer> fastFindAny() { return fastFindAny(IntStream.rangeClosed(0, 1_000_000) .boxed().filter(x -> x == 1_000_000)); }

, «»
count() , 25-65% . â
flatMap() â Stream :

, . 4 000 , 2 . â Stream (
IntStream.rangeClosed ) . 20 . â
Exception . , , : - â Exception , . , Stack Trace.
Exception Stack Trace . , , â JDK . , :
protected Constructor ,
RuntimeException , ,
Stack Trace . :
static class FoundException extends RuntimeException { Object payload; FoundException(Object payload) { super("", null, false, false);
FoundException . :

- â 200-300 . overhead , , .
Stream. , , ?
@Benchmark public Optional<Integer> findAnyPar() { return IntStream.range(0, 100_000_000).parallel().boxed() .filter(x -> x == 10_000_000).findAny(); } @Benchmark public Optional<Integer> fastFindAnyPar() { return fastFindAny(IntStream.<i>range</i>(0, 100_000_000) .parallel().boxed().filter(x -> x == 10_000_000)); }
forEach , Stream . .
Benchmark . 4- HT :

, â . ãã®çç±ã¯äœã§ããïŒ , Stream:
:
:
@Param({ "0", "20", "40" }) private int sleep; @Setup(Level.Invocation) public void setup() throws InterruptedException { Thread.sleep(sleep); }
setup â . , :

, , , , â .
, :
AtomicInteger i = new AtomicInteger(); Optional<Integer> result = fastFindAny( IntStream.range(0, 100_000_000).parallel() .boxed().peek(e -> i.incrementAndGet()) .filter(x -> x == 10_000_000)); System.out.println(i); System.out.println(result); Thread.sleep(1000); System.out.println(i);
peek() , . , , 1, , . , . , -:

20 000 000 , 50 000 000 . Stream, , . â , . Core-Libs-Dev, â :

, , . , 9 . , Stream , .
, . , , .
, . Stream , , Stream â .
JVM Java 9 , ,
Joker 2017 :