当我像这样写循环时,场景背后会发生什么

时间:2015-04-13 09:28:10

标签: java jpa for-loop

当我写这样的for loop

时,我想知道编译器在幕后做了什么

1

for(ChildObject child : parentObject.getChildObjects()){
     //do something
}

而不是

2

List<ChildObject> myList = parentObject.getChildObjects();
for(ChildObject child : myList){
     //do something
}

语句parentObject.getChildObjects()是一个JPA语句,获取类型是LAZY。

注意:我已阅读for-each循环(http://docs.oracle.com/javase/1.5.0/docs/guide/language/foreach.html)的java文档,但他们没有提到上述情况。

所以我知道循环是如何工作的,但是不知道从parentObject.getChildObjects()语句返回的列表发生了什么。

我的疑惑:

  1. 第一种情况是编译器将parentObject.getChildObjects()语句中返回的列表保存在某个临时变量中,这对于程序员是隐藏的,还是每次调用该语句?

  2. 如果每次调用语句parentObject.getChildObjects();它如何跟踪下一个元素?

3 个答案:

答案 0 :(得分:2)

For-Each循环正在使用所有实现Iterable - 接口的类,因为它需要iterator() - 将for-each语法转换为迭代器循环的操作:

public class ChildObject {
    [...]
}

public class ParentObject implements Iterable<ChildObject> {

    private List<ChildObject> childObjects;

    [...]

    @Override
    public Iterator<ChildObject> iterator() {
        return childObjects.iterator();
    }
}

public static void main(String[] args){

    ParentObject p = new ParentObject();

    [...]

    for(ChildObject child : p){
        System.out.println(child);
    }

}

所以

for(ChildObject child : p)

将转变为:

for(Iterator<ChildObject> it = p.iterator(); it.hasNext();){
    ChildObject child = it.next();
    [...]
}

这就是为什么你如何对你的前循环进行编码无关紧要。

P.S。如果迭代像int[] array这样的原始数组,编译器会将您的代码转换为简单的迭代数组循环

for(int i = 0; i < array.length; i++)

有关详情,请查看您问题的评论。

答案 1 :(得分:1)

由于触及内存使用等问题,我认为值得一提的是,在实践中,您不太可能注意到差异,因为编译器可能会有额外的astoreaload指令投入将主要在运行时进行优化。

重要的区别在于,对于案例2,您可能会意外地执行以下操作:

List<ChildObject> myList = parentObject.getChildObjects;
for(Iterator<ChildObject> it = myList.iterator; it.hasNext();){
    ...
}
...
ChildObject ob = myList.get(0);

现在这不是你应该做的事情,这就是为什么案例1几乎总是首选。有关这方面的更多信息,请参阅第二版Effective Java中的第45项(最小化局部变量的范围)。

答案 2 :(得分:0)

dit提到的是幕后发生的事情。只是为了补充他的答案:

在两个case 1 & 2编译器中都会创建一个iterator并保存它。

两种方法的区别在于:

案例1:

for loop将转换为

for(Iterator<ChildObject> it = parentObject.getChildObjects.iterator; it.hasNext();){

}

注意: parentObject.getChildObjects,返回实现List接口的IterableParentObjectChildObject都不会实现Iterable

这很重要,因为在&#34; dit&#34;例如,他的类正在实现Iterable接口。

  

在堆栈上:仅保存Iterator

案例2:

for loop将转换为

List<ChildObject> myList = parentObject.getChildObjects;
for(Iterator<ChildObject> it = myList.iterator; it.hasNext();){

}
  

在堆栈上:保存iterator并保存myList

<强>结论:

如果考虑memory usage,情况1优于2,否则两种方法都相同。