4

While going through the collections code in JDK, wherever looping is used, it is done in reverse way like this:

for (int i = size-1; i >= 0; i--) {...}

Is there any performance related thing or just JDK has adopted it like this?

tkruse
  • 10,222
  • 7
  • 53
  • 80
anupamD
  • 862
  • 1
  • 8
  • 21
  • 5
    Certainly safer if you want to remove items – Hovercraft Full Of Eels Jan 29 '20 at 03:42
  • Which source code are you looking at? – Tom Hawtin - tackline Jan 29 '20 at 03:49
  • 2
    I'm not an expert but probably there are performance reasons as well. E.g. if you want to iterate trough a collection and remove items. When you remove an item, all items after need to moved 1 back. Of cource this includes items that are to be removed. When you start from the last item and decrement you will have less overhead in moving the items back because you will make sure that the moved items don't contain any items that you want to remove because you already removed them. This kind of optimization is bound to the algorithm though. It's hard to believe they used it everywhere. – akuzminykh Jan 29 '20 at 03:50
  • Without providing a few examples of specific location where this loop is used, it's really hard to answer your question. – Mad Physicist Jan 29 '20 at 03:56
  • @Tom. How did you perform the search? GitHub's search capability is quite limited, so hopefully you didn't do it there... – Mad Physicist Jan 29 '20 at 03:57
  • The only [Google search result](https://www.google.com/search?q=site:http://hg.openjdk.java.net+"for+(int+i+%3D+size-1%3B+i+>%3D+0%3B+i--)") I found is in the [`ArrayList` source for the `lastIndexOf` method](http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/tip/src/share/classes/java/util/ArrayList.java#l308). I think it should be clear why `lastIndexOf` would iterate in reverse order. – kaya3 Jan 29 '20 at 04:11
  • This is an interesting questions. But, as others have stated already, it lacks sources. There does not appear to be a pattern like that in the JDK and in many situations where it is used it has other reasons, related to the actual algorithm being implemented. That conflicts with your base assumption, which is why sources are really important for this question. Therefore, I am voting to close the question. – Zabuzard Jan 29 '20 at 07:13
  • Please feel free to [edit] it and add some sources. Then vote for re-opening. – Zabuzard Jan 29 '20 at 07:14
  • Related / Duplicate: https://stackoverflow.com/questions/1340589, https://stackoverflow.com/questions/5349425, https://stackoverflow.com/questions/13769594, https://stackoverflow.com/questions/1656506, https://stackoverflow.com/questions/4181941, https://stackoverflow.com/questions/16476125, https://stackoverflow.com/questions/36430287 – tkruse Jan 29 '20 at 12:03

2 Answers2

6

There does not seem to be any performance benefit in Java, in general. (Also, the assertion that the JDK code does reverse looping a lot seems to be unfounded.)

See all the linked similar/duplicate questions, such as Why does decrement loop runs faster than increment loop?

There are also some articles online benchmarking the performance of different loop types, based on different collection types. Like https://www.trinea.cn/android/arraylist-linkedlist-loop-performance-en/

tkruse
  • 10,222
  • 7
  • 53
  • 80
0

As mentioned by @tkruse, calling collection.size (); means you do the following every iteration:

  1. memory access
  2. function call
  3. (other instructions inside function)
  4. memory access
  5. return

Whereas, if you initialize i = collection.size () - 1; it will only be done once.

I am not familiar with Java bytecode but bytecode is somewhat similar to assembly code such that it will be translated to machine code. As such, it is worth mentioning that memory access is slower than register access and, in some cases, comparing to any value other than 0 can have an additional instruction per iteration.

Here's an example using NASM:

i++ version

    xor eax, eax        ;i = 0
  label:
    mov ebx, [size]     ;temp = size
    sub ebx, eax        ;temp - i (Note: A >= B == 0 >= B - A)
    jle end             ;temp <= 0 goto end
    inc eax             ;i ++
    jmp label           ;temp > 0 goto label
  end:

i-- version

    mov eax, [size]     ;i = size
    dec eax             ;i = size - 1
  label:
    cmp eax, 0          ;i < 0?
    jl end              ;i < 0 goto end
    dec eax             ;i --
    jmp label           ;i >= 0 goto label
  end:

alvinalvord
  • 394
  • 2
  • 11
  • That does not necessarily apply to Java due to how Java is executed, i.e. from bytecode with JIT and everything. Also, lots of optimizations can take place with code like that. Especially if it is simple enough to be unrolled or similar things. – Zabuzard Jan 29 '20 at 07:10