-1

A "foreach" in Java is, for example

for (Mouse mouse: mouses) {
    [...]
}

We can't do:

Mouse mouse;
for (mouse: mouses) {
    [...]
}

I quote geeksforgeeks: Since the i variable goes out of scope with each iteration of the loop, it is actually re-declaration each iteration

In this way the variable would be declared only once. I don't know if this could have a very little optimization, but this is what I do in "normal" cycles, in every language.

Also, in this way the last element would be available also outside the cycle. This is for example the default in Python.


As another related question, there's some advantage to do

for (final Mouse mouse: mouses) {
    [...]
}

in terms of speed, or mouse can't simply be reassigned inside the loop?

Marco Sulla
  • 15,299
  • 14
  • 65
  • 100
  • 2
    *In this way the variable is declared only once.* In both ways, the variable is declared only once. – Zereges May 12 '19 at 21:51
  • 1
    @Zereges are you sure? It seems to me that the variable is re-declared in every loop. If not, we can't add the `final` keyword. – Marco Sulla May 12 '19 at 21:53
  • `mouse` needs to be reassigned in every iteration to next element from a collection, no matter how it is declared. `final` just prevents it from reassigning inside a loop. – zolv May 12 '19 at 21:55
  • @zolv I quote [geeksforgeeks](https://www.geeksforgeeks.org/final-keyword-java/): `Since the i variable goes out of scope with each iteration of the loop, it is actually re-declaration each iteration` – Marco Sulla May 12 '19 at 21:58
  • No, no speed advantage, as with everything java: don't think / worry about performance. – luk2302 May 12 '19 at 21:58
  • @MarcoSulla Well, you're mixing up declaration and assignment. `final` means that a variable cannot be reassigned once assigned. – MC Emperor May 12 '19 at 21:59
  • 1
    Can I say that the vote to close because is "opinion based" is very strange? I'm discussing about performance, that something that can be measured. – Marco Sulla May 12 '19 at 22:00
  • Regarding the first question: because the JLS says so: https://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.14.2 – luk2302 May 12 '19 at 22:00
  • 2
    Reusing variable will have no performance benefits and make code less readable. And either no memory benefits, after `for` ends same memory slot can (and will be) reused for another variable. The only impact is a bit bigger debug information section. – user882813 May 12 '19 at 22:02
  • @luk2302 I wanted to post an answer with this, but standard (javac 1.8.0_211) produces different bytecode for enhanced for loop and (what should be an equivalent) standard for loop – Zereges May 12 '19 at 22:04
  • @user882813 Have you some benchmarks? – Marco Sulla May 12 '19 at 22:05
  • @Zereges comparing normal and enhanced for loops was not really the question here. – luk2302 May 12 '19 at 22:06
  • 1
    @MarcoSulla If this question is about performance — you will have no or no mesurable performance gain, and your code is less readable. The thumb rule is that you make variables as local as possible, that is, with the smallest scope. – MC Emperor May 12 '19 at 22:06
  • @MCEmperor I agree with readability, but not for scope. `Python` makes the element available outside the `for` loop and you can access it to see the last element. It was useful to me in many occasion. – Marco Sulla May 12 '19 at 22:09
  • @user882813 `The only impact is a bit bigger debug information section` Explain. – Marco Sulla May 12 '19 at 22:11
  • @MCEmperor `you will have no or no mesurable performance gain` There's some benchmark in other languages that allows it? – Marco Sulla May 12 '19 at 22:13
  • And the downvote... – Marco Sulla May 12 '19 at 22:14
  • 1
    @Zereges The Java Language Specification [would beg to differ](https://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.14.2)... TL;DR, an enhanced for loop of the type specified by the OP expands to a regular for loop **with the `mouse` variable being redeclared each time inside the loop's body**. – Kröw May 12 '19 at 22:37
  • 1
    @zolv That isn't actually correct. If `mouse` were just being reassigned, `final` likely wouldn't be allowed, as that would break consistency with the meaning of the term. But anyway, the variable is actually redeclared as if in the top of the body of the loop each time the loop runs. – Kröw May 12 '19 at 22:40
  • @Kröw @MarcoSulla It depends how do we define "declaration". I think declaring means reserving new memory for a variable (just `mouse` reference, not for an item). Does it happen in each iteration to `mouse` so it is occupying different address each time? No, so it it is reassigning. Even if it is marked as final, memory for `mouse` reference is reused as it is scoped in `for` loop – zolv May 13 '19 at 08:14
  • 1
    @zolv please read [here](https://stackoverflow.com/questions/56103745/foreach-why-cant-the-element-variable-be-declared-outside?noredirect=1#comment98842020_56103745) – Marco Sulla May 13 '19 at 11:35
  • @zolv What you described is known as "memory allocation;" not at all variable declaration. Also, whether or not memory is allocated on a lower level is JVM dependent, so you can't claim that it does not happen each time (unless you specify state which JVM exhibits the behavior that you describe). – Kröw May 14 '19 at 01:46
  • @zolv The enhanced for loop construct is really just syntactic sugar for a regular for loop, so, *practically speaking*, the variable declared in it is declared each time the loop runs, as the OP has claimed. Again, this is specified by the language specification. – Kröw May 14 '19 at 01:47

3 Answers3

3

According to the Java spec, the for-each (or enhanced for) loop you wrote would expand into:

for(java.util.Iterator i = mouses.iterator(); i.hasNext(); ) {
   Mouse mouse = (Mouse) i.next();
   [...]
}

(JLS)

So to avoid "redeclaring" your mouse variable inside the loop, you would need to mimic the expanded version of the for loop, with the declaration for mouse on the outside:

Mouse mouse;
for(Iterator<Mouse> i = mouses.iterator(); i.hasNext(); ) {
   mouse = (Mouse) i.next();
   [...]
}

This would, theoretically, avoid repeated deallocation and allocation of memory (or whatever the JVM you're running on uses for references) for the variable mouse, but due to compile-time and run-time optimizations, it is very likely that changing your code like this will have little to no difference (or you may even lose some speed due to running a regular for loop over an enhanced one).

Kröw
  • 504
  • 2
  • 13
  • 31
  • How could I benchmark it? Specifically, I want to test the loop oly, not the data that I need to create to do the loop. – Marco Sulla May 12 '19 at 22:42
  • You could try to benchmark the difference between the two types of loops by running some tests and taking the time between each run-through. I'd run the loop many thousands of times to get a more accurate reading, then probably divide the time by the number of times run to get a more accurate reading (not sure if that would help much). For the most part, I think the timing would be the same (or almost immeasurably different, if at all), even for very, *very* large collections of Mice objects. – Kröw May 12 '19 at 22:45
  • Well, in the regular for loop structure, the first expression and variable declaration is only evaluated once (and first): `for(Iterator i = mouses.iterator(); i.hasNext(); )` the `Iterator i = mouses.iterator()` part only gets evaluated once, so you'd gain no benefit from declaring it outside of the loop, in terms of avoiding re-declaration overhead. – Kröw May 12 '19 at 22:57
  • There is a difference in doing what you just showed, however. In your code, the `Iterator i` would have a scope outside of the for loop; you could access `i` after the loop is finished executing. If you include the variable declaration inside the for loop however, you can't access the variable outside of the loop. – Kröw May 12 '19 at 22:58
  • 1
    Well, have you some suggestion about benchmark? In python I have simply to do `python3 -m timeit -s "Do something before for preparation" "statement to benchmark"` – Marco Sulla May 12 '19 at 23:06
  • @MarcoSulla https://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java stop worrying about micro-performance. If you think that is relevant use another language. Your first goal is code readability, I guarantee you if you manually expand a for loop and I see that during my review I will "shout" at you and revert the commit. – luk2302 May 13 '19 at 07:08
  • 1
    @luk2302 I'm only doing a test, I'll never expand a for loop in this way. – Marco Sulla May 13 '19 at 11:36
  • @luk2302 Code readability should be the *lowest* concern of any developer who's writing code in practice, by far. The purpose of writing a program is to tell a computer to perform an action. Accommodating for people who failed to learn how to read code, is a terrible goal, *especially* when it involves a loss in speed. **If someone can't read a piece of code that was written without a lack of readability as a goal, then the problem lies with that someone, not with the piece of code.** – Kröw May 14 '19 at 01:56
  • @luk2302 I'd argue that Java was developed for code that would actually be used in a professional field, rather than tinkering, (hence the JIT compilers, it's attribute of being a compiled language, etc.). If you cherish readability over programming, then a more readable language like Python, or better yet [Inform](http://inform7.com/), (I hear it's basically plain English), is probably a better area to delve into. – Kröw May 14 '19 at 02:01
  • @Kröw I wholeheartedly disagree with everything you said. Everybody can write unreadable code. Writing readable code **is** one of the biggest concerns in *any* major software dev project, that is not debatable, that is just a fact. Wether or not you think this is a bad idea is a different topic, but that is one where I think your opinion is just 100% wrong and simply ill-informed. No Readability => No Maintainability => Bad! – luk2302 May 15 '19 at 07:55
  • @luk2302 You can’t just claim that “writing readable code is one of the biggest concerns in any major software dev project.” That is ***completely*** up to debate. I’d refute that with the belief that better companies, whose developers know the languages they use, well, don’t concern themselves with trying to make their code more readable for others. – Kröw May 15 '19 at 20:29
  • @luk2302 Also, to help you better understand my previous comments, I want to note that I never said that developers shouldn’t write readable code. I said they shouldn’t *concern themselves with writing* readable code; they should make the functionality and performance, etc., priorities, and should learn how to read code written with those goals in mind, more effectively. – Kröw May 15 '19 at 20:33
  • There is no “repeated deallocation and allocation”. The creations of a variable are just a formal act. You may consider the absence of (de)allocations an optimization, but as elaborated in [my answer](https://stackoverflow.com/a/58997383/2711488) it’s impossible to implement a JVM in a “non-optimized” fashion as the necessary information is not even there in the compiled program. Note that most compiled languages use stack frames. In C++, scope can have an impact as you can define arbitrary actions in copy constructors/assignment operators or destructors, but regarding storage, it’s the same. – Holger Nov 22 '19 at 15:45
2

You wrote:

I quote geeksforgeeks: “Since the i variable goes out of scope with each iteration of the loop, it is actually re-declaration each iteration”

This is formally correct but its impact also is only of formal nature. It has been described in the linked article already; the result of this definition is that you are allowed to put the final modifier at the variable.

This in turn implies that you are allowed to capture the variable’s value in an inner class or lambda expression when it is declared final or when no other assignment happens in the loop body (which makes it effectively final).

Besides that, it produces the same bytecode for the method. The declarations of local variables, including their name and scope and whether they are final, are merely a compile-time artifact. They may get stored in debug information, but these are not mandatory and must not influence the operations of the virtual machine.

The stack is organized in frames, memory blocks large enough to provide space for all local variables of a method that may exist at the same time (having overlapping scopes), which are reserved when the method is entered already.

See The Java® Language Specification, § 15.12.4.5:

A method m in some class S has been identified as the one to be invoked.

Now a new activation frame is created, containing the target reference (if any) and the argument values (if any), as well as enough space for the local variables and stack for the method to be invoked and any other bookkeeping information that may be required by the implementation…

and The Java® Virtual Machine Specification, § 2.6

A frame is used to store data and partial results, as well as to perform dynamic linking, return values for methods, and dispatch exceptions.

A new frame is created each time a method is invoked. A frame is destroyed when its method invocation completes, […]

The sizes of the local variable array and the operand stack are determined at compile-time and are supplied along with the code for the method associated with the frame (§4.7.3). Thus the size of the frame data structure depends only on the implementation of the Java Virtual Machine, and the memory for these structures can be allocated simultaneously on method invocation.

You might say, but theoretically, a JVM could implement it differently, as long as the observable behavior stays compatible. But as said at the beginning, the bytecode of these constructs does not differ. The are no actions associated with the declaration of a variable nor with it going out of scope, so an implementation can’t perform allocations or deallocations at these points as it doesn’t even know whether and where these points exist.

When you use the following program:

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() {}
}

You will get the following output (or something similar):
E.g. on 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
…

In other words, identical bytecode for all variants.

Also, in this way the last element would be available also outside the cycle. This is for example the default in Python.

That would be the an actual difference. Whether this would be an improvement is debatable. Since the variable would not be initialized when the collection is empty, we can’t use the variable o after the loop in the iteratorLoopExtendedScope example above. We would need an initialization before the loop to guaranty that the variable is definitely assigned in every case, but then, the code would do more than the standard for-each loop, not less…

Community
  • 1
  • 1
Holger
  • 285,553
  • 42
  • 434
  • 765
  • Thank you for the great explanation. However, I consider using `final` in `for` loops quite useless... who is so mad to change the itering variable? And if he want to do it, well, it will remove `final`... And if you need it for a nested class, well, do not use a nested class! It's the kind of things that make Java unreadable. And I hope `Eclipse` will add an option to skip adding `final` to for loops :D – Marco Sulla Nov 25 '19 at 22:54
  • @MarcoSulla no-one requires you to declare the loop variable as `final`. As explained, it makes no difference anyway. If you don’t assign it, it’s *effectively final*, which means, you may refer to it from a lambda expression (or nested class) even when not declared `final`. Eclipse won’t insert `final` modifiers unless you told it to do so. – Holger Nov 26 '19 at 07:36
  • Yes, I know, but I activated auto-formatting options of Eclipse, and there's no way to disable `final` auto-insertion only for `for` loops :D – Marco Sulla Nov 26 '19 at 20:53
0

As of another related question, final has no effect on the performance. That is just a (compile-time) check that the variable isn't re-assigned during it's scope. Compiling

class Foo
{
    void foo(int[] arr)
    {
        for (/*final*/ int a : arr)
        {
            System.out.println(a);
        }
    }
}

produces exactly the same bytecode with or without the emphasized final (as of javac 1.8.0_211)

Zereges
  • 5,139
  • 1
  • 25
  • 49