[英]Does ArrayDeque have overhead of shifting elements on remove/add?
I came across this question, where 1st (accepted) answer says this part:我遇到了这个问题,第一个(接受的)答案说了这部分:
ArrayDeque doesn't have the overhead of node allocations that LinkedList does nor the overhead of shifting the array contents left on remove that ArrayList has.
ArrayDeque 没有 LinkedList 没有的节点分配开销,也没有 ArrayList 具有的移动数组内容的开销。
I agree with node overhead , but not with the part about shifting elements .我同意节点开销,但不同意关于移动元素的部分。 I know StackOverflow can also have wrong information, but this answer has many votes, so it must be my ignorance.
我知道StackOverflow也可能有错误的信息,但这个答案有很多票,所以一定是我的无知。 So can someone tell me this:
所以有人可以告诉我这个:
Howcome ArrayDeque
doesn't have overhead of shifting elements ?为什么
ArrayDeque
没有移动元素的开销? ArrayDeque
(as its name states) still uses an ARRAY . ArrayDeque
(顾名思义)仍然使用ARRAY 。 Meaning it works same as any other array .这意味着它的工作原理与任何其他数组相同。 If I had 10 elements and I remove head , those 9 will have to be shifted 1 place to the left.
如果我有 10 个元素并且我删除了head ,那么这 9 个元素必须向左移动 1 个位置。 And that is overhead that
LinkedList
doesn't have - it just changes reference to prev and next .这是
LinkedList
没有的开销——它只是更改对prev和next的引用。 Am I correct?我对么?
To sum, don't ArrayList
and ArrayDeque
work the same way?总而言之,
ArrayList
和ArrayDeque
的工作方式不一样吗? They both shift elements if structure gets changed.如果结构发生变化,它们都会移动元素。 The only difference is that
ArrayList
can access any arbitrary location , while ArrayDeque
works as FIFO/LIFO .唯一的区别是
ArrayList
可以访问任意位置,而ArrayDeque
以FIFO/LIFO的方式工作。 Can someone please correct me if I am wrong?如果我错了,有人可以纠正我吗? I don't want to learn something wrong here.
我不想在这里学错东西。
It's great to have the intuition that removing from the front of an array will require everything to be shifted back.从数组的前面删除将需要将所有内容移回原位,这种直觉很棒。 However, in the specific case of the
ArrayDeque
, the implementation is designed in a way that doesn't require this.但是,在
ArrayDeque
的特定情况下,实现的设计方式不需要这样做。
An array-based deque is typically implemented using a data structure called a circular buffer .基于数组的双端队列通常使用称为循环缓冲区的数据结构来实现。 The idea is that we maintain an array of elements, but pretend that the ends of the array are glued together to form a ring.
这个想法是我们维护一个元素数组,但假装数组的末端粘在一起形成一个环。
The ArrayDeque
internally maintains an array of 16 elements, which we can view like this: ArrayDeque
内部维护了一个16个元素的数组,我们可以这样查看:
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| | | | | | | | | | | | | | | | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
We maintain two different pointers, a head pointer and a tail pointer , keeping track of the position of the first element of the deque and the position one past the last element of the deque.我们维护两个不同的指针,一个头指针和一个尾指针,跟踪双端队列第一个元素的 position 和双端队列最后一个元素之后的 position。 Initially, they'll point to the start of the array:
最初,它们将指向数组的开头:
head
|
v
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| | | | | | | | | | | | | | | | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
^
|
tail
Whenever we do an addFirst
, we back up the head pointer by one step, then write the element to the location we find.每当我们执行
addFirst
时,我们都会将头指针后退一步,然后将元素写入我们找到的位置。 Since we're pretending that the two ends of the array are linked together, backing up one step here moves the head pointer to the last position:由于我们假装数组的两端链接在一起,因此在此处后退一步会将头指针移动到最后一个 position:
head
|
v
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| | | | | | | | | | | | | | | | X |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
^
|
tail
To do an addLast
, we write to the tail position, then advance it forward:要执行
addLast
,我们写入尾部 position,然后向前推进:
head
|
v
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| X | | | | | | | | | | | | | | | X |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
^
|
tail
Here's what it would look like if we did two more addFirst
s:这是如果我们再执行两个
addFirst
的样子:
head
|
v
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| X | | | | | | | | | | | | | X | X | X |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
^
|
tail
And here's what it would look like if we did two more addLast
s:如果我们再执行两个
addLast
,它会是这样的:
head
|
v
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| X | X | X | | | | | | | | | | | X | X | X |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
^
|
tail
We read the elements of the deque by starting at the head pointer and proceeding forward until we reach the tail pointer.我们从头指针开始读取双端队列的元素,然后继续前进直到到达尾指针。 So in this case, we start reading from the slot pointed at by
head
, not the first position in the array.所以在这种情况下,我们从
head
指向的插槽开始读取,而不是数组中的第一个 position。
Arranging things this way makes it very fast (O(1)) to remove the first element of the deque.以这种方式安排事物可以非常快地 (O(1)) 删除双端队列的第一个元素。 We just clear out the entry pointed at by
head
and move head
forward a step, like this:我们只是清除
head
指向的条目,然后将head
向前移动一步,就像这样:
head
|
v
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| X | X | X | | | | | | | | | | | | X | X |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
^
|
tail
Notice that nothing needs to get shifted around, since we're just pretending that we start at a later position in the array.请注意,不需要移动任何东西,因为我们只是假装我们从数组中较晚的 position 开始。
However, were you to remove from the middle of the ArrayDeque
, then, like removing from the middle of most other array-based structures, yes, you'd have to shift elements around.但是,如果您要从
ArrayDeque
的中间移除,那么就像从大多数其他基于数组的结构的中间移除一样,是的,您必须四处移动元素。 But, then again, ArrayDeque
isn't optimized for that use case;但是,话又说回来,
ArrayDeque
并未针对该用例进行优化; it's specifically designed to be a double-ended queue, as the name suggests.顾名思义,它专门设计为双端队列。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.