我为了练习Java 8 Streams,尝试将以下嵌套循环转换为Java 8 Stream API。它计算a^b的最大数字和(a,b<100),在我的Core i5 760上需要大约0.135秒。
public static int digitSum(BigInteger x)
{
int sum = 0;
for(char c: x.toString().toCharArray()) {sum+=Integer.valueOf(c+"");}
return sum;
}
@Test public void solve()
{
int max = 0;
for(int i=1;i<100;i++)
for(int j=1;j<100;j++)
max = Math.max(max,digitSum(BigInteger.valueOf(i).pow(j)));
System.out.println(max);
}
我的解决方案本来因为并行处理应该更快,但实际上花了0.25秒(没有parallel()
时是0.19秒):
int max = IntStream.range(1,100).parallel()
.map(i -> IntStream.range(1, 100)
.map(j->digitSum(BigInteger.valueOf(i).pow(j)))
.max().getAsInt()).max().getAsInt();
我的问题
- 我是否正确地进行了转换,还是有更好的方式将嵌套循环转换为流计算?
- 为什么流变体比旧方法慢得多?
- 为什么parallel()语句实际上会使时间从0.19秒增加到0.25秒?
我知道微基准测试很脆弱,且并行处理只适用于大问题,但对于CPU来说,即使0.1秒也是漫长的,对吗?
更新
我使用Eclipse Kepler中的JUnit 4框架进行测量(它显示执行测试所需的时间)。
a,b<1000而非100时的结果:
- 传统循环186s
- 顺序流193s
- 并行流55s
更新2
用sum+= c - '0';
替换sum+=Integer.valueOf(c+"");
(感谢Peter!)将并行方法的总时间缩短了10秒,达到了45秒。没想到这样会有如此大的性能影响!
此外,将并行度减少到CPU核心数(我的情况下为4)并没有太大作用,因为它只将时间缩短到了44.8秒(是的,它增加了a和b=0,但我认为这对性能影响不大)。
int max = IntStream.range(0, 3).parallel().
.map(m -> IntStream.range(0,250)
.map(i -> IntStream.range(1, 1000)
.map(j->.digitSum(BigInteger.valueOf(250*m+i).pow(j)))
.max().getAsInt()).max().getAsInt()).max().getAsInt();
sum+=Integer.valueOf(c+"");
жӣҝжҚўдёәsum+= c - '0';
пјҢеӣ дёәиҝҷж ·йҖҹеәҰжӣҙеҝ«гҖӮ - Peter LawreyCharSequence.chars()
方法中的流替换digitSum
中的循环。这样可以避免分配字符数组。 - Stuart Marks