每个循环,还是一个迭代器?
哪种是遍历集合的最有效的方法?
List<Integer> a = new ArrayList<Integer>();
for (Integer integer : a) {
integer.toString();
}
要么
List<Integer> a = new ArrayList<Integer>();
for (Iterator iterator = a.iterator(); iterator.hasNext();) {
Integer integer = (Integer) iterator.next();
integer.toString();
}
请注意,尽管最后一个问题的答案之一接近了,但这不是这个,这个,这个或这个的完全重复。 这不是一个愚蠢的原因是,其中大多数比较循环,你在循环内部调用get(i)
,而不是使用迭代器。
正如Meta上的建议,我将发布我对这个问题的回答。
如果您只是在集合上漫游以读取所有值,则使用迭代器或新的for循环语法没有区别,因为新语法只是在水下使用迭代器。
但是,如果你想循环使用旧的“c-style”循环:
for(int i=0; i<list.size(); i++) {
Object o = list.get(i);
}
然后,根据底层的数据结构,新的for循环或迭代器可以更加高效。 原因是对于某些数据结构, get(i)
是O(n)操作,它使循环成为O(n2)操作。 传统的链表是这种数据结构的一个例子。 所有迭代器都有一个基本要求, next()
应该是O(1)操作,从而生成循环O(n)。
要验证迭代器在新的for循环语法中是否在水下使用,请比较以下两个Java片段中生成的字节码。 首先是for循环:
List<Integer> a = new ArrayList<Integer>();
for (Integer integer : a)
{
integer.toString();
}
// Byte code
ALOAD 1
INVOKEINTERFACE java/util/List.iterator()Ljava/util/Iterator;
ASTORE 3
GOTO L2
L3
ALOAD 3
INVOKEINTERFACE java/util/Iterator.next()Ljava/lang/Object;
CHECKCAST java/lang/Integer
ASTORE 2
ALOAD 2
INVOKEVIRTUAL java/lang/Integer.toString()Ljava/lang/String;
POP
L2
ALOAD 3
INVOKEINTERFACE java/util/Iterator.hasNext()Z
IFNE L3
其次,迭代器:
List<Integer> a = new ArrayList<Integer>();
for (Iterator iterator = a.iterator(); iterator.hasNext();)
{
Integer integer = (Integer) iterator.next();
integer.toString();
}
// Bytecode:
ALOAD 1
INVOKEINTERFACE java/util/List.iterator()Ljava/util/Iterator;
ASTORE 2
GOTO L7
L8
ALOAD 2
INVOKEINTERFACE java/util/Iterator.next()Ljava/lang/Object;
CHECKCAST java/lang/Integer
ASTORE 3
ALOAD 3
INVOKEVIRTUAL java/lang/Integer.toString()Ljava/lang/String;
POP
L7
ALOAD 2
INVOKEINTERFACE java/util/Iterator.hasNext()Z
IFNE L8
正如您所看到的,生成的字节码实际上是相同的,所以使用任何一种形式都没有性能损失。 因此,您应该选择最符合美学要求的循环形式,因为大多数人将是for-each循环,因为它具有较少的样板代码。
差异不在于性能,而在于能力。 当直接使用引用时,显式使用迭代器类型(例如List.iterator()和List.listIterator(),但在大多数情况下它们返回相同的实现)有更多的权力。 你也有能力在循环中引用迭代器。 这允许您执行诸如从集合中删除项目而不会收到ConcurrentModificationException的情况。
例如
还行吧:
Set<Object> set = new HashSet<Object>();
// add some items to the set
Iterator<Object> setIterator = set.iterator();
while(setIterator.hasNext()){
Object o = setIterator.next();
if(o meets some condition){
setIterator.remove();
}
}
这不是,因为它会抛出一个并发修改异常:
Set<Object> set = new HashSet<Object>();
// add some items to the set
for(Object o : set){
if(o meets some condition){
set.remove(o);
}
}
为了扩展Paul自己的答案,他已经证明,该特定编译器(大概是Sun的javac?)上的字节码是相同的,但不同的编译器不能保证生成相同的字节码,对吗? 要查看两者之间的实际区别,让我们直接找到源代码并检查Java Language Specification,特别是14.14.2,“增强的for语句”:
增强for
说法是相当于基本for
形式的语句:
for (I #i = Expression.iterator(); #i.hasNext(); ) {
VariableModifiers(opt) Type Identifier = #i.next();
Statement
}
换句话说,JLS要求两者相同。 理论上这可能意味着字节码的边际差异,但实际上增强的for循环需要:
.iterator()
方法 .hasNext()
.next()
使局部变量可用 换句话说,为了所有实际目的,字节码将是相同的或几乎相同的。 很难想象任何编译器的实现都会导致两者之间的显着差异。
链接地址: http://www.djcxy.com/p/18029.html