zoukankan      html  css  js  c++  java
  • 由遍历集合所联想到的一些问题


    1、以下一段再平常不过的遍历代码,但是与我一样,好多新手都会在这个地方出问题,例如

    for(int i=0; i<list.size();i++)
      // 执行;

    之前我在刚工作的时候在这个地方犯错,我们在自测的时候都没问题。初始化数据之后给客户演示时,突然蹦出个空指针异常NullPointerException。

    于是我们通过debug发现,这个list为空,平常我们开发都会手工插入一些数据到表,自然不会报空,演示的时候数据表为空,自然就报错了。
    改进:

    for(int i=0; list!=null&&i<list.size(); i++)
        //执行;

    不会报错了吧!

    但是有经验的开发人员会发现for循环,每次都会去对循环条件进行判断,于是继续改进

    if(list!=null && !list.isEmpty())
        for(int i=0; i<list.size(); i++)
            // 执行;    

    还有问题吗

    我们会发现循环还是每次都会去执行list.size();方法,list.size()方法也就是一句代码return size;看似简单的一句代码,但是我们知道每次执行一个方法JVM都会为该方法申请一个栈帧,过程虽短,但是也是一项不小的开销。
    于是继续改进

    if(list!=null && !list.isEmpty())
        for(int i=0,len=list.size(); i<len; i++)
            // 执行;    

    这样效率真的会高一些吗,实验是最好的答案

    List<Integer> list = new ArrayList<Integer>();
    
    for(int i=0; i<30000000;i++)
    list.add(i);
    
    // 普通循环
    long t1 = System.currentTimeMillis();
    for(int j=0; j<100; j++)
      for(int i=0; i<list.size(); i++) {
        Integer it1 = list.get(i);
      }
    System.out.println(System.currentTimeMillis()-t1);
    
    // 改进len
    t1 = System.currentTimeMillis();
    for(int j=0; j<100; j++)
      for(int i=0, len=list.size(); i<len; i++) {
        Integer it2 = list.get(i);
      }
    System.out.println(System.currentTimeMillis()-t1);
    
    // foreach方式遍历
    t1 = System.currentTimeMillis();
    for(int j=0; j<100; j++)
      for(Integer it4: list) {
    
      }
    System.out.println(System.currentTimeMillis()-t1);
    
    结果
    普通for结果     22335
    使用len结果    14369
    使用foreach结果 29163

    经过多次测试发现,普通for结果比len会多出三分之一左右,foreach效果比普通for会略差,而且经过多次测试发现一个有趣的现象,循环位于不同的位置,结果也会有所差距,貌似JVM对循环过的集合会进行优化,下一次再次循环这个集合,速度会有所加快,这个大家也可以去做一下实验,集合过大的时候可能报堆溢出,可以通过修改。-Xms128M -Xmx512M

    PS:以上遍历方法不考虑多线程不会有什么问题,如果在多线程下遍历将len提出来,就容易出错了,因为len就获取一次。

    2、接下来考虑一个问题,多线程下需要同步遍历某个公共集合,如何实现
    我们可能会想到用同步块synchronized

    synchronized(list) {
         if(list!=null && !list.isEmpty())
            for(int i=0,len=list.size(); i<len; i++)
                执行;
    }    

    这样的代码真的好吗?我们可以想一想,多线程的情况下,你将公共的list一锁,其他的线程想插入list删除list只能干等着,更严重的是如果你在遍历的时候还执行一些长时间的操作,例如关闭连接或者启动监听等等。
    于是我们就想如何能够让等待的时间尽量的短,我们完全可以先将集合克隆下来(至于深浅克隆根据具体情况而论),再去执行,当然如果循环执行时间很短,完全没这个必要。

    LifecycleListener[] interested = null;
    // 用克隆减少时间,因为如果一个一个去触发事件响应,得使用大量时间,而又因为线程锁的存在,使得其他线程必须继续等待
    synchronized (listeners) {
        interested = (LifecycleListener[])listeners.clone();
    }
    
    // 逐个触发事件响应
    for(int i=0; i<interested.length; i++) {
        interested[i].lifecycleEvent(event);
    }

    为什么上面没有使用len中间变量,因为数组的length并不会占用执行时间。

  • 相关阅读:
    华硕ASUS A3V 拆解图 http://m.linktone.com/report/pdjj/14001781862.shtml
    华硕A3V 迅驰 配置详情
    Dvbbs 更换论坛置顶图片
    Flash 图片轮换效果
    动网官方最新dvbbs7.1sp1商业版下载,附存储过程解密代码!
    在VC中为应用程序添加图形超链接功能
    VC常见数据类型转换详解
    查询Access逻辑字段遇到的问题 武胜
    几个不错的开源的.net界面控件 转自http://zchuang2004.spaces.live.com/blog/cns!8C4AEEE059DED8B1!157.entry 武胜
    C#正则表达式整理备忘 转载(http://www.cnblogs.com/KissKnife/archive/2008/03/23/1118423.html) 武胜
  • 原文地址:https://www.cnblogs.com/TimBing/p/4374412.html
Copyright © 2011-2022 走看看