J2SE 1.5提供了另一种形式的for循环,借助这种形式的for循环,可以使用非常简单方式遍历数组和集合,本文介绍了for循环的正确使用方式和在一些场景中的for循环处理。
一. 传统的for循环写法
static void test4(){
int[] array = new int[]{1,2,3,4,5};
for(int i = 0;i < array.length ; i++){
A mA = new A(array[i]);
mA.print();
}
}
static class A {
private int index;
public A(int index){
this.index = index;
}
void print(){
System.out.println("index = "+ index);
}
}
这段代码看上去没有什么问题,相信大家在编码的过程中很多人也会这么写,但是当array数组的length达到数百万级别,A对象拥有几十种属性时,在手机终端上运行呢?? 下面修改下这个小程序:
public class MainActivity extends Activity implements OnClickListener
{
Button btn;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn = (Button)findViewById(R.id.btn);
btn.setOnClickListener(this);
}
@Override
public void onClick(View v)
{
long now = System.currentTimeMillis();
test4();
long now1 = System.currentTimeMillis();
btn.setText(String.valueOf(now1-now));
}
void test4(){
for(int i = 0;i < 1000000 ; i++){
A mA = new A(i);
mA.print();
}
}
class A {
private int index;
public A(int index){
this.index = index;
}
void print(){
System.out.println("index = "+ index);
}
}
}
修改循环次数为1000000次,点击button按钮开始执行,计算耗时时长,编译出apk在小米2s上运行后,发现耗时24543ms,众所周知手机终端严重受电量、内存和CPU的限制,这个级别的计算对手机来说还是有影响的(不讨论在大型服务器上执行),那么怎么去优化这个for循环呢?
首先分析循环体“int i = 0;i<1000000;i++",这个循环体本身就存在不合理的地方,每次循环时都要new int对象i,多次创建对象的引用,造成了对性能的影响,这里把 i 对象定义在for循环之外就可以了,这样可以保证每次使用的都是同一个引用对象,只不过指向的值不同而已。同理for循环里面也存在这个问题,每次循环都会执行A mA = new A(i);,同样把对mA对象的声明放在for之外即可,下面看下代码:
void test4(){
A mA = null;
int i = 0;
for(i = 0;i < 1000000 ; i++){
mA = new A(i);
mA.print();
}
}
然后重新编译新版本,在同一台手机上重新测试,发现耗时时长为22254ms,比上次少了2289ms,效果立竿见影,可见这些小细节被放大后会造成不可预期的后果,值得我们思考。
二 . for 与 try-catch
稍微修改下代码:
static void test4(){
A mA = null;
int i = 1;
for(i = 1;i <= 1000000 ; i++){
mA = new A(i);
try{
mA.print();
}catch(Exception e)
{
e.printStackTrace();
}
}
}
static class A {
private int index;
public A(int index){
this.index = index;
}
void print(){
System.out.println("c = "+ 100/index);
}
}
为防止A在执行print报错时,在for循环里添加了try-catch保护,下面编译后在真机上运行,计算耗时时长是21948ms,分析代码发现,每次for时都会进行try-catch保护,编译器在编译期间每次都要必须产生必要的代码和数据结构与系统的异常分发函数密切配合,大体来讲就是分析异常处理代码的结构,封装和标记每部分,注册异常处理器函数,无形当中就降低了代码的执行效率,下面把try-catch提到for外面:
void test4(){
A mA = null;
int i = 1;
try{
for(i = 1;i <= 1000000 ; i++){
mA = new A(i);
mA.print();
}
}catch(Exception e)
{
e.printStackTrace();
}
}
再次编译执行发现耗时只要20648ms,用时减少了1300ms,可见在同时处理for和try-catch的时候一定要注意这点。
三 .第二种for循环
Java的第二种for循环基本是这样的格式:
for (循环变量类型 循环变量名称 : 要被遍历的对象) 循环体
这种循环体在编译的时候依然后被解释成:
for (int 变量名甲 = 0; 变量名甲 < 要被遍历的对象.length; 变量名甲++)
所以这种循环体也要慎用......
题外话:为什么这种循环体不用foreach或者in来表示?这样作的主要原因,是为了避免因为引入新的关键字,造成兼容性方面的问题――在Java语言中,不允许把关键字当作变量名来使用,虽然使用“foreach”这名字的情况并不是非常多,但是“in”却是一个经常用来表示输入流的名字(例如java.lang.System类里,就有一个名字叫做“in”的static属性,表示“标准输入流”)。
综上所述,在编码过程中千万不要忽略这些小细节,养成能省就省的习惯是十分必要的,特别是在开发web应用的时候,初期用户量不多时感受不到,可是一旦用户量达到一定水平,问题爆发时必会措手不及。