background image

  比如最简单的 List 遍历,会有这样的写法:

for

(

int

 i=

0

;i

  同样是对 List 的操作,如果要在遍历同时进行增加和删除操作,代码如下:

for

(

int 

i=

0

,j=l.size();i=

0

;i--){l.remove(i);}。经过测试,如果采用 ArrayList,两种写法在循环次数较

少时没有太大的区别,循环次数为

1000

,均为 1ms 以内,次数为

10000

,前一种为 60ms

左右,后一种为 1ms 以内,,而次数上到

100000

,前一种为 6000ms 左右,后一种为

15ms,随着循环次数的增多,后一种较前一种的效率优势明显提高。
  这是由 Collection 库 ArrayList 的实现决定的,以下是 jdk1.

3

的 ArrayList 源码:

public

 Object remove(

int

 index)

{
RangeCheck(index);
modCount++;
Object oldValue = elementData[index];

int

 numMoved = size - index - 

1

;

if

 (numMoved > 

0

)

System.arraycopy(elementData, index+

1

, elementData, index,numMoved);

elementData[--size] = 

null

;

// Let gc do its workreturn oldValue;

 
}
>
}
 
  从中我们可以看出,numMoved 代表了需要进行 arraycopy 操作的数量,它是由
remove 的位置决定的,如果 index=

0

,也就是删除第一个元素,则需要 arraycopy 后面的

所有数据,而如果 index=size-

1

,则只需将最后一个元素设为

null

即可。所以从后面向前

循环 remove 是比较好的写法。
  如果 List 中的确存在较多的 add 或 remove 操作,且容量较大(如存储几万个对象),
则应该采用 LinkedList 作为实现。LinkedList 内部采用双向链表作为数据结构,比
ArrayList 占用较多内存空间,且随机访问操作较慢(需要从头或尾循环到相应位置),但
插入删除操作很快(仅需进行链表操作,无须大量移动或拷贝)。
  对于 List 操作如果循环规模较小,其实对性能影响非常小(ms 级),远远不是性能瓶
颈所在。但心中有着优化的意识,并力求写出简洁高效的程序应该是我们每个程序员的追
求。而且一旦在循环规模较大时,如果有了这些意识,也就能有效的消除性能隐患。
  再举一个与优化无关但确实可能成为性能杀手(可以说是 bug)的循环的例子。下面是
源代码:

for

(; totalRead < m_totalBytes; totalRead += readBytes)

{
readBytes = m_request.getInputStream().read(m_binArray, totalRead, m_totalBytes - totalRead);
}
 
  这个代码意图很清楚,就是将一个 InputStream 流读到一个

byte

数组中去。它使用

read 方法循环读取 InputStream,该方法返回读取的字节数。正常情况下,该循环运行良好,
当 totalRead=m_totalBytes 时,结束循环,

byte

数组被正常填充。但如果仔细看一下

InputStream 的 read 方法的说明,了解一下其返回值就会发现,返回值可能为-

1

,即已读