简体   繁体   English

ArrayDeque 是否有删除/添加时移动元素的开销?

[英]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没有的开销——它只是更改对prevnext的引用。 Am I correct?我对么?

To sum, don't ArrayList and ArrayDeque work the same way?总而言之, ArrayListArrayDeque的工作方式不一样吗? 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可以访问任意位置,而ArrayDequeFIFO/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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM