例如,Java中的“ foreach”是
NOT NULL
我们不能:
for (Mouse mouse: mouses) {
[...]
}
我引用Enforcing constraints “two tables away”:Mouse mouse;
for (mouse: mouses) {
[...]
}
这样,变量将仅声明一次。我不知道这样做是否可以进行很少的优化,但这是我在每种语言的“正常”周期中所做的事情。
此外,通过这种方式,最后一个元素也将在循环之外可用。例如,这是Since the i variable goes out of scope with each iteration of the loop, it is actually re-declaration each iteration
中的默认设置。
作为另一个相关问题,有一些好处要做
Python
就速度而言,还是for (final Mouse mouse: mouses) {
[...]
}
不能简单地在循环内重新分配?
答案 0 :(得分:3)
根据Java规范,您编写的for-each(或增强的for)循环将扩展为:
for(java.util.Iterator i = mouses.iterator(); i.hasNext(); ) {
Mouse mouse = (Mouse) i.next();
[...]
}
(JLS)
因此,为避免在循环内“重新声明” mouse
变量,您需要模仿for循环的扩展版本,并在外部声明mouse
:
Mouse mouse;
for(Iterator<Mouse> i = mouses.iterator(); i.hasNext(); ) {
mouse = (Mouse) i.next();
[...]
}
从理论上讲,这将避免对变量mouse
进行重复的重新分配和内存分配(或您正在运行的用于引用的JVM),但是由于编译时和运行时的优化,很有可能像这样更改您的代码几乎没有差别(或者甚至可能由于在增强代码上运行常规for循环而失去一些速度)。
答案 1 :(得分:2)
您写道:
我引用geeksforgeeks:“由于i变量在循环的每次迭代中都超出范围,因此实际上每次迭代都需要重新声明”
这在形式上是正确的,但其影响也只是形式上的。在链接的文章中已经有描述。此定义的结果是允许您将final
修饰符放在变量中。
这反过来意味着,在声明为final
或循环体内没有其他赋值的情况下,允许您在内部类或lambda表达式中捕获变量的值(这有效地使它成为最终)。
除此之外,它为该方法生成相同的字节码。局部变量的声明,包括它们的名称和范围以及它们是否为final
,仅是编译时的构件。它们可能存储在调试信息中,但这不是强制性的,并且不得影响虚拟机的操作。
堆栈按帧进行组织,内存块足够大,可以为可能同时存在的方法的所有局部变量(具有重叠范围)提供空间,该方法在方法保留时会保留已经输入。
请参见The Java® Language Specification, § 15.12.4.5:
已将某个类
m
中的方法S
确定为要调用的方法。现在,将创建一个新的激活框架,其中包含目标引用(如果有)和参数值(如果有),以及足够的空间用于局部变量和堆栈,以供方法使用以及实施可能需要的任何其他簿记信息……
和The Java® Virtual Machine Specification, § 2.6
框架用于存储数据和部分结果,以及执行动态链接,方法的返回值和调度异常。
每次调用方法时都会创建一个新框架。框架的方法调用完成后会销毁,[…]
局部变量数组和操作数堆栈的大小在编译时确定,并与与帧关联的方法的代码(§4.7.3)一起提供。因此,帧数据结构的大小仅取决于Java虚拟机的实现,并且可以在方法调用时同时分配这些结构的内存。
您可能会说,但是从理论上说,只要可观察的行为保持兼容,JVM可以以不同的方式实现它。但是正如开头所说,这些结构的字节码没有区别。这些操作与变量的声明没有关联,也没有超出范围,因此,实现甚至无法知道这些点是否存在以及在何处存在,因此无法在这些点执行分配或取消分配。
使用以下程序时:
class VarScopes {
public static void forEachLoop(Collection<?> c) {
for(Object o: c) {
System.out.println(o);
}
}
public static void iteratorLoop(Collection<?> c) {
for(Iterator<?> it = c.iterator(); it.hasNext();) {
Object o = it.next();
System.out.println(o);
}
}
public static void iteratorLoopExtendedScope(Collection<?> c) {
Iterator<?> it;
Object o;
for(it = c.iterator(); it.hasNext();) {
o = it.next();
System.out.println(o);
}
}
public static void main(String[] args) throws IOException, InterruptedException {
decompile();
}
private static void decompile() throws InterruptedException, IOException {
new ProcessBuilder(
Paths.get(System.getProperty("java.home"), "bin", "javap").toString(),
"-cp", System.getProperty("java.class.path"),
"-c", MethodHandles.lookup().lookupClass().getName())
.inheritIO()
.start()
.waitFor();
}
private VarScopes() {}
}
您将获得以下输出(或类似内容):
例如在repl.it
Compiled from "VarScopes.java"
public class VarScopes {
public static void forEachLoop(java.util.Collection<?>);
Code:
0: aload_0
1: invokeinterface #1, 1 // InterfaceMethod java/util/Collection.iterator:()Ljava/util/Iterator;
6: astore_1
7: aload_1
8: invokeinterface #2, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z
13: ifeq 33
16: aload_1
17: invokeinterface #3, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
22: astore_2
23: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
26: aload_2
27: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
30: goto 7
33: return
public static void iteratorLoop(java.util.Collection<?>);
Code:
0: aload_0
1: invokeinterface #1, 1 // InterfaceMethod java/util/Collection.iterator:()Ljava/util/Iterator;
6: astore_1
7: aload_1
8: invokeinterface #2, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z
13: ifeq 33
16: aload_1
17: invokeinterface #3, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
22: astore_2
23: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
26: aload_2
27: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
30: goto 7
33: return
public static void iteratorLoopExtendedScope(java.util.Collection<?>);
Code:
0: aload_0
1: invokeinterface #1, 1 // InterfaceMethod java/util/Collection.iterator:()Ljava/util/Iterator;
6: astore_1
7: aload_1
8: invokeinterface #2, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z
13: ifeq 33
16: aload_1
17: invokeinterface #3, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
22: astore_2
23: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
26: aload_2
27: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
30: goto 7
33: return
…
换句话说,所有变体的字节码相同。
此外,通过这种方式,最后一个元素也将在循环之外可用。例如,这是
Python
中的默认设置。
那将是实际的差异。这是否会有所改善尚待商.。由于在集合为空时不会初始化变量,因此在上面的o
示例中,在循环之后我们无法使用变量iteratorLoopExtendedScope
。我们需要在循环之前进行初始化,以确保在每种情况下都确实分配了变量,但是,与标准for-each循环相比,代码可以做到更多,而不是更少……
答案 2 :(得分:0)
根据另一个相关问题,final
对性能没有影响。这只是(编译时)检查,以确保变量在其作用域内未重新分配。编译
class Foo
{
void foo(int[] arr)
{
for (/*final*/ int a : arr)
{
System.out.println(a);
}
}
}
无论是否带有强调的final
(从javac 1.8.0_211
开始),产生的字节码都完全相同