【问题标题】:Collections Iteration: For Syntax vs Iterator : Reusing Temporary Variables集合迭代:语法与迭代器:重用临时变量
【发布时间】:2014-03-31 11:25:35
【问题描述】:

问题:

就效率而言,必须为每个for 循环重新创建一个引用变量似乎是完全错误的。 有什么办法可以做到:

       Item i;
       for (i : list2)

另外,创建临时引用变量有那么大的不同吗?

请参阅下面的原始代码旧迭代器样式


原码

for (Item i : DataStore.SomeList) {
    if (someBoolean)continue;

    if (someBoolean2) 
       finalList.add(i);

    else if (someBoolean3)
        list1.add(i);
    else
        list2.add(i);
 }

 for (Item i : list1)
    finalList.add(i);

  for (Item i : list2)
    finalList.add(i);

旧的迭代器语法

Item i;
Iterator<Item> it =  DataStore.SomeList.iterator();

while(it.hasNext())
{

if (someBoolean)continue;

if (someBoolean2) 
    finalList.add(i);

else if (someBoolean3)
    list1.add(i);
else
    list2.add(i);
}

it =  list1.iterator();     
while(it.hasNext())finalList.add(it.next());

it =  list2.iterator();     
while(it.hasNext())finalList.add(it.next());

更新

使用 JAD 反编译类。由于 JAD 很旧,它添加了一些演员表,但差异是可见的。

反编译的 For 循环

        Item i;
        for(Iterator iterator1 = list1.iterator(); iterator1.hasNext(); finalList.add(i))
            i= (Item)iterator1.next();

        Item i;
        for(Iterator iterator2 = list2.iterator(); iterator2.hasNext(); finalList.add(i))
            i= (Item)iterator2.next();

反编译的迭代器

for(Iterator it = list1.iterator(); it.hasNext(); finalList.add((Application)it.next()));
for(Iterator it = list2.iterator(); it.hasNext(); finalList.add((Application)it.next()));

【问题讨论】:

  • 您是否考虑过查看您的两个示例生成的字节码?我想你会find 他们很像。
  • 请不要过早优化 - 本地(在堆栈上)创建的变量是 Java 中较小的问题之一... :-)
  • @Elliott Frisch 我已经使用 JAD 反编译了代码,所以我知道 FOR 已转换为迭代器。不过,我会检查编译器是否重用了迭代器。
  • 一般来说变量的创建不会影响你的效率。不过,用于生成列表的算法会。

标签: java for-loop iterator


【解决方案1】:

问题:
就效率而言,必须为每个 for 循环重新创建一个引用变量似乎是完全错误的。

错了。变量实际上并不存在。它们是一种抽象,允许您命名事物。如果一个变量有一个将来可能使用的值,它将位于内存或寄存器中的地址。在其有用范围之后,编译器可能(并且可能会)重用以前用于该变量的地址或寄存器。

有什么办法可以做到:

   Item i;
   for (i : list2)

不行,快速迭代中的变量必须是const,所以不能提前声明。

另外,创建临时引用变量有那么大的不同吗?

正如我所说,变量实际上并不存在,所以不,是声明一个新变量还是重用一个旧变量完全没有区别。

我建议你参加编译器课程,它们对这类事情很有启发性。

【讨论】:

  • 如果我们谈论的是汇编/c,我认为人们试图减少创建新变量或将值从一个寄存器复制到另一个寄存器?我的理论很生疏,但是根据字节码,您确定无论多小都不会产生任何影响吗?我猜 JIT 编译器优化了,但想知道......
  • 当然,您会希望避免不必要地复制值,但声明变量不会复制任何内容。声明变量时根本不会生成字节码,只是向编译器提供提示。
【解决方案2】:

Java 通常会有效地处理临时引用变量。

我认为处理超大列表时更大的效率问题是将其分成单独的列表,然后将它们添加到最后。下面的方法可能更有效。

int index0 = 0;
int index1 = 0;
for (Item i : DataStore.SomeList) {
    if (someBoolean)continue;

    if (someBoolean2) {
       finalList.add(index0);
       index0++;
       index1++;
    }

    else if (someBoolean3){
       finalList.add(index1);
       index1++;
    }
    else
        finalList.add(i);
}

【讨论】:

  • 另外我应该指出,根据列表的类型,这可能会更加低效,具体取决于列表如何处理列表末尾的中间版本中的添加。
【解决方案3】:

就效率而言,必须为每个 for 循环重新创建一个引用变量似乎是完全错误的

除非您的应用程序对速度优化有极端需求,否则每次迭代都创建临时引用并丢弃它们没有任何缺点。 JVM 非常擅长处理这些事情。

创建临时引用变量有那么大的不同吗?

不,你不应该关心这种类型的微优化,它几乎不值得麻烦。专注于编写可读和清晰的代码,而不是担心这些事情。此外,如果 JVM 更有效的话,它很可能会内联它们。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-05-24
    • 1970-01-01
    • 2017-11-29
    • 2012-07-07
    • 2013-02-07
    • 2017-09-02
    • 2016-10-22
    • 2012-03-23
    相关资源
    最近更新 更多