Join GitHub today
GitHub is home to over 40 million developers working together to host and review code, manage projects, and build software together.
Sign upFix Queue bug #4300 #4302
Fix Queue bug #4300 #4302
Conversation
When removeIndex removed first value in queue which was also in the last position of values array, head would be at values.length, which is not a correct position for head to be in. Fixed by adding a check which would wrap head back to zero in this case.
|
Better to be verbose than to hide in one liners, also LibGDX formatter. |
| @@ -142,8 +141,7 @@ public T removeFirst () { | |||
|
|
|||
| final T result = values[head]; | |||
| values[head] = null; | |||
| head++; | |||
This comment has been minimized.
This comment has been minimized.
gjroelofs
Sep 16, 2016
•
Member
Personally, I'd refrain from this pattern as it just makes the code harder to read without any performance benefits.
This comment has been minimized.
This comment has been minimized.
Darkyenus
Sep 17, 2016
Author
Contributor
I did it to be in line with libGDX's pattern of bringing object fields into method variables first before accessing them multiple times, for performance reasons (for Android, mainly, IIRC) (performance reason being, that due to multithreading, these two variants may have different semantics. It is true that JVM is free to change the semantics slightly for optimizations, I am not sure if it happens on Dalvik (or at least its earlier versions). Since this pattern is so common, I expect that it is just for this reason).
I'll let others decide on which "style" should be used, as similar pattern of inlining inc/dec operations is fairly common in libGDX's collections (just maybe not inside if()?).
This comment has been minimized.
This comment has been minimized.
gjroelofs
Sep 18, 2016
Member
The main comment is on the if(++head ..) format which is an inc/dec operator inside the if loop.
This comment has been minimized.
This comment has been minimized.
Darkyenus
Sep 19, 2016
Author
Contributor
I have assumed so. My argument is that:
this.head++;
if(this.head == values.length) ...
requires two reads from the object, while:
if(++this.head == values.length) ...
has to read just once, which could be slightly faster, in some circumstances.
But it is not a big deal either way. I'll wait for core contributors to decide.
This comment has been minimized.
This comment has been minimized.
RBogie
Sep 19, 2016
Member
So, I just want to jump in on this discussion.
First of all, I want to say that for readability, I prefer having the increment out of the if.
However, I mostly want to jump in because of the argument that this would create multiple reads. Fact is, you simply can't rely on what bytecode is generated for such small syntactic differences. I did test it out for the current java version on my system (javac 1.8.0_74), and when using an instance field it actually generated the exact same bytecode:
aload_0 //Load this pointer on stack
dup //Duplicate this pointer on stack
getfield #2 // Field test:I
iconst_1 // Push 1 to stack
iadd // Add top 2 elements
putfield #2 // Field test:I
aload_0 // Load this pointer on stack
getfield #2 // Field test:I
However, when using a static field, it would read the field only once, but duplicate it once more on the stack. It then depends on the jit compiler whether one is faster than the other. However, in the field instance case, performance will be exactly the same.
This comment has been minimized.
This comment has been minimized.
gjroelofs
Sep 20, 2016
Member
@Darkyenus Apologies, I misinterpreted your explanation as I saw no assignment. To me, this level of optimization is rather done by the compiler if it comes a the expense of readability.
| System.arraycopy(values, head, values, head + 1, index - head); | ||
| values[head] = null; | ||
| this.head++; |
This comment has been minimized.
This comment has been minimized.
| @@ -290,6 +313,6 @@ private void assertEqualsAndHash (Queue<?> q1, Queue<?> q2) { | |||
|
|
|||
| private void assertValues (Queue<Integer> q, Integer... values) { | |||
| for (int i = 0, n = values.length; i < n; i++) | |||
| if (values[i] != q.get(i)) fail(q + " != " + new Array(values)); | |||
| if (values[i] == null ? q.get(i) != null : !values[i].equals(q.get(i))) fail(q + " != " + new Array(values)); | |||
This comment has been minimized.
This comment has been minimized.
gjroelofs
Sep 16, 2016
Member
Probably better not to have ternary statements inside if statements. Better to split it up.
Also, make sure you're using the LibGDX formatter.
This comment has been minimized.
This comment has been minimized.
Darkyenus
Sep 17, 2016
Author
Contributor
This is IntelliJ autogenerated null-safe equals check. I can split it if needed, but as it is just in Test, I didn't deem that necessary.
This comment has been minimized.
This comment has been minimized.
|
Fair points, thanks for comments, amended! |
|
Also; thanks for the fix :-) |
|
Thanks! |
Darkyenus commentedSep 11, 2016
Also adds a new test for this edge-case (verbatim from issue) and cleans up Queue and QueueTest (in separate commits for easy review).