1.案例描述

由于一次Java面试的笔试题,当时没有写出很好的解决方案,特此专门撰写一篇博客来加以记录,方便日后的查看

面试题目如下:从性能上优化如下代码并说明优化理由?

 for (int i = 0; i < 1000; i++){
for (int j = 0; j < 100; j++){
for (int k = 0; k < 10; k++){
System.out.println(i+""+j+""+k);
}
}
}

2.案例分析

从给出的代码可知,不论如何优化,testFunction执行的次数都是相同的,该部分不存在优化的可能。那么,代码的优化只能从循环变量i、j、k的实例化、初始化、比较、自增等方面的耗时上进行分析。
首先,我们先分析原题代码循环变量在实例化、初始化、比较、自增等方面的耗时情况:

变量 实例化(次数) 初始化(次数) 比较(次数) 自增(次数)
i 1 1 1000 1000
j 1 1000 1000 * 100 1000*100
k 1 1000*100 1000 * 100 * 10 1000 * 100 * 10

(注:由于单次耗时视不同机器配置而不同,上表相关耗时采用处理的次数进行说明)
该代码的性能优化就是尽可能减少循环变量i、j、k的实例化、初始化、比较、自增的次数,同时,不能引进其它可能的运算耗时。)

3.解决过程

从案例分析,对于原题代码,我们提出有两种优化方案:

 for (int i = 0; i < 10; i++){
for (int j = 0; j < 100; j++){
for (int k = 0; k < 1000; k++){
System.out.println(i+""+j+""+k);
}
}
}

该方案主要是将循环次数最少的放到外面,循环次数最多的放里面,这样可以最大程度的(注:3个不同次数的循环变量共有6种排列组合情况,此种组合为最优)减少相关循环变量的实例化次数、初始化次数、比较次数、自增次数,方案耗时情况如下:

变量 实例化(次数) 初始化(次数) 比较(次数) 自增(次数)
i 1 1 10 10
j 10 10 10*100 10 * 100
k 10*100 10*100 10 * 100 * 1000 10 * 100 * 1000

3.2 优化方案二 
代码如下:

 int i, j, k;
for (i = 0; i < 10; i++){
for (j = 0; j < 100; j++){
for (k = 0; k < 1000; k++){
System.out.println(i+" "+j+" "+k);
}
}
}

该方案在方案一的基础上,将循环变量的实例化放到循环外,这样可以进一步减少相关循环变量的实例化次数,方案耗时情况如下:

变量 实例化(次数) 初始化(次数 比较(次数) 自增(次数)
i 1 1 10 10
j 1 10 10*100 10*100
k 1 10*100 10*100*1000 10*100*1000

4 解决结果 
那么,提出的优化方案是否如我们分析的那样有了性能上的提升了呢?我们编写一些测试代码进行验证,数据更能说明我们的优化效果。 
4.1 测试代码

 public static void testFunction(int i, int j, int k) {
System.out.print(""); // 注:该方法不影响整体优化,这里只有简单输出
} public static void testA() {
long start = System.nanoTime();
for (int i = 0; i < 1000; i++)
for (int j = 0; j < 100; j++)
for (int k = 0; k < 10; k++)
testFunction(i, j, k);
System.out.println("testA time>>" + (System.nanoTime() - start));
} public static void testB() {
long start = System.nanoTime();
for (int i = 0; i < 10; i++)
for (int j = 0; j < 100; j++)
for (int k = 0; k < 1000; k++)
testFunction(k, j, i);
System.out.println("testB time>>" + (System.nanoTime() - start));
} public static void testC() {
long start = System.nanoTime();
int i;
int j;
int k;
for (i = 0; i < 10; i++)
for (j = 0; j < 100; j++)
for (k = 0; k < 1000; k++)
testFunction(k, j, i);
System.out.println("testC time>>" + (System.nanoTime() - start));
}

4.2 测试结果 
1、测试机器配置:Pentium(R) Dual-Core CPU E5400 @2.70GHz 2.70GHz, 2GB内存; 
2、循环变量i、j、k循环次数分别为10、100、1000,进行5组测试,测试结果如下:

  第1组 第2组 第3组 第4组 第5组
原方案 171846271 173250166 173910870 173199875 173725328
方案一 168839312 168466660 168372616 168310190 168041251
方案二 168001838 169141906 168230655 169421766 168240748

从上面的测试结果来看,优化后的方案明显性能优于原方案,达到了优化的效果。但优化方案二并没有如我们预期的优于方案一,其中第2、4、5组的数据更是比方案一差,怀疑可能是循环次数太少,以及测试环境相关因素影响下出现的结果。

3、重新调整循环变量i、j、k循环次数分别为20、200、2000,进行5组测试,测试结果如下:

  第1组 第2组 第3组 第4组 第5组
原方案 1355397203 1358978176 1358128281 1350193682 1354786598
方案一 1343482704 1348410388 1343978037 1347919156 1340697793
方案二 1342427528 1343897887 1342662462 1342124048 1336266453

从上面的测试结果来看,优化后的方案基本符合我们的预期结果。

5 总结 
从案例分析和解决过程中的三个表的分析可知,优化方案一和优化方案二的性能都比原代码的性能好,其中优化方案二的性能是最好的。在嵌套For循环中,将循环次数多的循环放在内侧,循环次数少的循环放在外侧,其性能会提高;减少循环变量的实例化,其性能也会提高。从测试数据可知,对于两种优化方案,如果在循环次数较少的情况下,其运行效果区别不大;但在循环次数较多的情况下,其效果就比较明显了。

关于对For循环嵌套优化的问题的更多相关文章

  1. Python 三级菜单与优化(一层循环嵌套)

    优化的思路是使用单层循环嵌套完成三级菜单,这个优化思路我非常喜欢,我喜欢在编程的时候用最少的东西写出同样的效果,通常这样会绕来绕去,但非常有趣!!! 需求: 1.运行程序输出第一级菜单: 2.选择一级 ...

  2. SQL连接操作符介绍(循环嵌套, 哈希匹配和合并连接)

    今天我将介绍在SQLServer 中的三种连接操作符类型,分别是:循环嵌套.哈希匹配和合并连接.主要对这三种连接的不同.复杂度用范例的形式一一介绍. 本文中使用了示例数据库AdventureWorks ...

  3. for循环嵌套练习题or99乘法表

    //输出1-10之间的和 public static void whileTest(){ //定义变量用于存储不断变化的和 int sum = 0; //定义变量,用于记录不断变化的被加数 int x ...

  4. Java基础_循环嵌套_打印乘法口诀、菱形,各种图形,计算二元一次和三元一次方程组_7

    循环嵌套 打印乘法口诀 for(int j=1;j<=9;j++){ for(int i=1;i<=j;i++){ System.out.print(i+"*"+j+& ...

  5. C#用链式方法表达循环嵌套

    情节故事得有情节,不喜欢情节的朋友可看第1版代码,然后直接跳至“三.想要链式写法” 一.起缘 故事缘于一位朋友的一道题: 朋友四人玩LOL游戏.第一局,分别选择位置:中单,上单,ADC,辅助:第二局新 ...

  6. for循环与for循环嵌套

    今天温习了下分支语句跟for循环,主要讲解了for循环嵌套,这里开始有点迷糊了,整理下思路在做练习 for循环嵌套用我自己的大白话来说就是一个外圈的for程序里面一个套着一个小的for程序,如果在范围 ...

  7. tonado框架的列表嵌套 (template中for循环嵌套)

            学习了tonado框架,渐渐开始明白模板的转换,以后肯定还会遇到很多问题... 功能描述:       页面显示读取的数据库父导航名称,再通过嵌套列表将子导航名称和地址查询出来,返回到 ...

  8. php for循环嵌套

    <?php     //2.打印一个50*50的 'o' 的正方形方整, 使用for的嵌套     // oooooo     // oooooo     // oooooo //for循环嵌套 ...

  9. For 循环嵌套 0309

                                                                                                     For ...

随机推荐

  1. 【SQL】关于无法附加文件的错误

    [SQL]关于无法附加文件的错误 1.错误信息如下: 2.估计是权限问题右击属性,把权限开一下 3.然后就附加成功了~~ ——————————————————————————————————————— ...

  2. sorl6.0+jetty+mysql搭建solr服务

    1.下载solr 官网:http://lucene.apache.org/solr/ 2.目录结构如下 3.启动solr(默认使用jetty部署) 在path路径下将 bin文件夹对应的目录加入,然后 ...

  3. Web缓存杂谈

    一.概述 缓存通俗点,就是将已经得到的‘东东’存放在一个相对于自己而言,尽可能近的地方,以便下次需要时,不会再二笔地跑到起始点(很远的地方)去获取,而是就近解决,从而缩短时间和节约金钱(坐车要钱嘛). ...

  4. Log4net入门(帮助类篇)

    在前几篇Log4net入门文件的讲述过程中,我们在使用log4net的类中都要编写如下一行代码: private static log4net.ILog log = log4net.LogManage ...

  5. Microsoft Build 2016 Day 2 记录(多图慎入)

    Microsoft Build 2016 Day 1 记录 Microsoft Build 2016 进行到了第二天,我觉得这一天的内容非常精彩,因为主要和开发者相关

  6. Vertica集群单节点宕机恢复方法

    Vertica集群单节点宕机恢复方法 第一种方法: 直接通过admintools -> 5 Restart Vertica on Host 第二种方法: 若第一种方法无法恢复,则清空宕机节点的c ...

  7. ORACLE 11gR2 DG(Physical Standby)日常维护02

    环境:RHEL 6.5 + Oracle 11.2.0.4 三.监控DG的状态 3.1监控DG备库的状态 3.2监控主库传输日志链路的状态 四.备库切换为snapshot standby 4.1备库切 ...

  8. C#基础回顾(二)—页面值传递、重载与重写、类与结构体、装箱与拆箱

    一.前言 -孤独的路上有梦想作伴,乘风破浪- 二.页面值传递 (1)C#各页面之间可以进行数据的交换和传递,页面之间可根据获取的数据,进行各自的操作(跳转.计算等操作).为了实现多种方式的数据传递,C ...

  9. javascript学习笔记一

    今天看的javascript 应用开发实践指南 看了js库 jquery ,明确了要深入学习jquery的想法. 对于javascript原生态的ajax写法(兼容性只需考虑ie6),封装为函数: f ...

  10. 利用Python进行数据分析 基础系列随笔汇总

    一共 15 篇随笔,主要是为了记录数据分析过程中的一些小 demo,分享给其他需要的网友,更为了方便以后自己查看,15 篇随笔,每篇内容基本都是以一句说明加一段代码的方式, 保持简单小巧,看起来也清晰 ...