为什么以下代码:
StringBuilder sb22 = IntStream .range(1,101) .filter(x -> x > 50) .Boxed() .parallel() .collect(// object that is used in accumulator to do accumulating on StringBuilder::new,// use object from above and call append on it with each stream element as argument (sb,a) -> sb.append(":" + a),// (executes only when using parallel!) (sb1,sb2) -> { System.out.println(Thread.currentThread().getId() + " " + "sb1=" + sb1 + " AND " + "sb2=" + sb2); sb1.append("-"+sb2); });
产生这个结果:
------------------:51:52:53-:54:55:56-:57:58:59-:60:61:62-:63:64:65-:66:67:68-:69:70:71-:72:73-:74:75-:76:77:78-:79:80:81-:82:83:84-:85:86:87-:88:89:90-:91:92:93-:94:95:96-:97:98-:99:100
不应该首先将部分(——————)排除在输出之外?
此外,我理解收集中的组合器可能会被无序调用,因此可以改为:76:77:78-:79:80:81,例如:63:64:65-:79:80:81?
更新(@Holger回复后)
[51..100] _________________________________________________________________________________/\______________________________________________________________________ | | (empty) [51..100] ___________________________________/\__________________________________ ________________________________________/\______________________________________ | | | | (empty) (empty) [51..75] [76..100] ___________________/\______________ ___________________/\______________ ______________________/\________________ ______________________/\________________ | | | | | | | | (empty) (empty) (empty) (empty) [51..62] [63..75] [76..87] [88..100] _______/\______ ___________/\______ _______/\______ ___________/\______ ________/\_______ _____________/\_______ ________/\_______ _____________/\_______ | | | | | | | | | | | | | | | | (empty) (empty) (empty) (empty) (empty) (empty) (empty) (empty) [51..56] [57..62] [63..68] [69..75] [76..81] [82..87] [88..93] [94..100] ___/\__ ___/\__ ___/\__ _______/\__ ___/\__ ___/\__ ___/\__ _______/\__ ___/\___ ___/\___ ___/\___ ________/\__ ___/\___ ___/\___ ___/\___ ________/\___ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | (empty) (empty) (empty) (empty) (empty) (empty) (empty) (empty) (empty) (empty) (empty) (empty) (empty) (empty) (empty) (empty) [51..53] [54..56] [57..59] [60..62] [63..65] [66..68] [69..71] [72..75] [76..78] [79..81] [82..84] [85..87] [88..90] [91..93] [94..96] [97..100] ___/\__ ___/\__ ___/\___ ____/\__ | | | | | | | | (empty) (empty) (empty) (empty) [72..73] [74..75] [97..98] [99..100]
解决方法
工作负载拆分在任何处理之前发生,因此,Stream实现会将范围[1,101]拆分为要处理的子范围.此时,它不知道过滤器将完全删除前半部分,它不能在不评估谓词的情况下知道,并且应该已经并行发生,在工作负载分裂之后.
因此,每个子范围都以相同的方式处理,包括将结果收集到容器中并在之后组合这些容器,即使它们恰好是空的.规范并没有说当没有元素到达收集器时会跳过组合步骤,你不应该期望这样.虽然理论上可以跟踪是否有任何元素到达收集器,但这种跟踪只能用于特定情况,并且甚至不清楚是否将容器与空容器组合(如添加空List或附加空StringBuilder)比跟踪更昂贵.
当然,如果它保留了语义,例如,没有什么能阻止你优化你的组合器.代替(sb1,sb2) – > sb1.append(sb2),你可以使用(sb1,sb2) – > sb1.length()== 0? sb2:sb1.append(sb2)
您可以查看this Q&A,“Visualization of Java Stream parallelization”以获取更多详细信息.