为了练习Java 8流,我尝试将以下嵌套循环转换为Java 8流API。它计算a ^ b(a,b <100)的最大数字总和,并在我的Core i5
760上花费约0.135s。
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.25s(不带的时间为0.19s parallel()
):
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();
我的问题
我知道微基准测试很脆弱,而并行性仅在遇到大问题时才值得,但对于CPU来说,甚至0.1s都是永恒的,对吗?
更新资料
我使用Eclipse Kepler中的Junit 4框架进行了测量(它显示了执行测试所花费的时间)。
我的a,b <1000而不是100的结果:
更新2 更换sum+=Integer.valueOf(c+"");
用sum+= c - '0';
(感谢彼得!)剃掉平行法的10整秒,使其达到45秒。没想到会有如此大的性能影响!
另外,减少并行处理的CPU核心数量(在我的例子中为4)并没有多大用处,因为它仅将时间减少到44.8s(是的,它增加了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();
我根据您的代码创建了一个快速而肮脏的微型基准测试。结果是:
循环:3192
拉姆达:3140
拉姆达平行:868
因此,循环和lambda是等效的,并且并行流显着提高了性能。由于您的基准测试方法,我怀疑您的结果不可靠。
public static void main(String[] args) {
int sum = 0;
//warmup
for (int i = 0; i < 100; i++) {
solve();
solveLambda();
solveLambdaParallel();
}
{
long start = System.nanoTime();
for (int i = 0; i < 100; i++) {
sum += solve();
}
long end = System.nanoTime();
System.out.println("loop: " + (end - start) / 1_000_000);
}
{
long start = System.nanoTime();
for (int i = 0; i < 100; i++) {
sum += solveLambda();
}
long end = System.nanoTime();
System.out.println("lambda: " + (end - start) / 1_000_000);
}
{
long start = System.nanoTime();
for (int i = 0; i < 100; i++) {
sum += solveLambdaParallel();
}
long end = System.nanoTime();
System.out.println("lambda parallel : " + (end - start) / 1_000_000);
}
System.out.println(sum);
}
public static int digitSum(BigInteger x) {
int sum = 0;
for (char c : x.toString().toCharArray()) {
sum += Integer.valueOf(c + "");
}
return sum;
}
public static int 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)));
}
}
return max;
}
public static int solveLambda() {
return IntStream.range(1, 100)
.map(i -> IntStream.range(1, 100).map(j -> digitSum(BigInteger.valueOf(i).pow(j))).max().getAsInt())
.max().getAsInt();
}
public static int solveLambdaParallel() {
return IntStream.range(1, 100)
.parallel()
.map(i -> IntStream.range(1, 100).map(j -> digitSum(BigInteger.valueOf(i).pow(j))).max().getAsInt())
.max().getAsInt();
}
我也用jmh运行它,它比手动测试更可靠。结果与上面的结果一致(每个呼叫微秒):
Benchmark Mode Mean Units
c.a.p.SO21968918.solve avgt 32367.592 us/op
c.a.p.SO21968918.solveLambda avgt 31423.123 us/op
c.a.p.SO21968918.solveLambdaParallel avgt 8125.600 us/op
转换是否正确,或者是否有更好的方法将嵌套循环转换为流计算? 为什么流变体比旧变体慢得多? 为什么parallel()语句实际上将时间从0.19s增加到0.25s? 我知道微基准是脆弱的,并行性只有在遇到大问题时才值得,但对于CPU来说,即使是0.1秒也是永恒的,对吗? 更新 null
我试图理解新的Java8流,并且花了好几天的时间在Java8流中的集合上传输嵌套的foreach循环。 是否可以重构以下嵌套的foreach循环,包括Java-8-Streams中的if条件? 如果是,它会是什么样子。 非常感谢你的帮助。 溴
我希望以下假设得到证实。以下嵌套for循环的时间复杂度相同: 我的假设是,时间复杂度是相同的,因为5 x 1_000_000调用doSomething与1_000_000 x 5调用doSomething是相同的。是这样吗?如果是,如果涉及到流,这也是真的吗? 上述两种说法的时间复杂度相同吗?
问题内容: 我有一个对象A的列表。此列表中的每个对象A都包含对象B的列表,而对象B包含对象C的列表。对象C包含一个属性名称,我想使用它使用Java 8进行过滤。 如何使用流在Java 8中编写以下代码以避免嵌套循环: 问题答案: 您可以使用两个,然后使用a,然后可以选择第一个,或者如果没有结果返回:
我有一个对象列表A。此列表中的每个对象A都包含对象B的列表,对象B包含对象C的列表。对象C包含一个属性名称,我想使用java 8进行过滤。 如何使用流在java8中编写下面的代码以避免嵌套循环:
如何使用java8流和过滤器过滤嵌套循环? 假设我有一个汽车列表(