In Java (but similarly in PHP) the ArrayDeque
implementation always has its capacity as a power of 2:
http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/java/util/ArrayDeque.java#l126
For HashMap
this choice is clear - to have a uniform element distribution based on a trimmed 32-bit hash. But Deque
inserts/removes elements sequentially.
Also, ArrayList
doesn't restrict its capacity to a power of two, just ensures it's at least the number of elements.
So, why does the Deque
implementation require its capacity to be a power of 2?
Deque Interface: It is a Doubly Ended Queue in which you can insert the elements from both sides. It is an interface that implements the Queue. ArrayDeque implements both Queue and Deque. It is dynamically resizable from both sides.
Queue Interface: It is an Interface that is a FirstIn – FirstOut Data Structure where the elements are added from the back. Deque Interface: It is a Doubly Ended Queue in which you can insert the elements from both sides. It is an interface that implements the Queue. ArrayDeque implements both Queue and Deque.
Array deques have no capacity restrictions and they grow as necessary to support usage. They are not thread-safe which means that in the absence of external synchronization, ArrayDeque does not support concurrent access by multiple threads. Null elements are prohibited in the ArrayDeque.
Let us go through each of the operations by implementing alongside by providing clean java program as follows: In order to add an element to the ArrayDeque, we can use the methods add (), addFirst (), addLast (), offer (), offerFirst (), offerLast () methods.
Finally i found it!!!
The reason not just in performance and bits-mask operations (yes, they are faster, but not significantly). The real reason is to allow loop back the elements
capacity if we use sequential adding-removing operations. In other words: reuse released cells after remove()
operation.
Consider the following examples (initial capacity is 16):
Only add()
:
add 15 elements => head=0, tail=15
add more 5 elements => doubleCapacity()
=> head=0, tail=20, capacity=32
add()
-remove()
-add()
:
add 15 elements => head=0, tail=15
remove 10 elements => tail loops back to removed indexes => head=10, tail=15
add more 5 elements => the capacity remains 16, the elements[]
array is not rebuilt or reallocated! => new elements are added into the place of the removed elements to the beginning of the array => head=10, tail=4 (looped back to the start of the array from 15->0->1->2->3->4). Note the values 16-19 are inserted to the indexes 0-3
So, in this case using power of two and concise bit operations makes much more sense. With such approach the operations like if ( (tail = (tail + 1) & (elements.length - 1)) == head)
allow to assign and verify easily that the looped tail
does not overlap with the head
(yeah, the stupid snake where actually the tail bites the head :) )
The code snippet to play around:
ArrayDeque<String> q = new ArrayDeque<>(15); // capacity is 16
// add 15 elements
q.add("0"); q.add("1"); q.add("2"); q.add("3"); q.add("4");
q.add("5"); q.add("6"); q.add("7"); q.add("8"); q.add("9");
q.add("10"); q.add("11");q.add("12");q.add("13");q.add("14");
// remove 10 elements from the head => tail LOOPS BACK in the elements[]
q.poll();q.poll();q.poll();q.poll();q.poll();q.poll();q.poll();q.poll();q.poll();q.poll();
// add 5 elements => the elements[] is not reallocated!
q.add("15");q.add("16");q.add("17");q.add("18");q.add("19");
q.poll();
I guess, for performance reasons. For example, let's look at implementation of addLast
function:
public void addLast(E e) {
if (e == null)
throw new NullPointerException();
elements[tail] = e;
if ( (tail = (tail + 1) & (elements.length - 1)) == head)
doubleCapacity();
}
So, instead of tail = (tail + 1) % elements.length
it is possible to write tail = (tail + 1) & (elements.length - 1)
(&
works faster, than %
). Such constructions are used many times in ArrayDeque
's source code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With