Array Deque Source Code Analysis of Dead java Sets

Keywords: Java less

problem

(1) What is a double-ended queue?

(2) How does Array Deque achieve double-ended queues?

(3) Is Array Deque thread-safe?

(4) Is Array Deque bounded?

brief introduction

Double-ended queue is a special kind of queue. It can enter and exit elements at both ends, so it is named double-ended queue.

Array Deque is a double-ended queue implemented in an array manner, which is non-thread-safe.

Inheritance system

It can be seen from the inheritance system that Array Deque implements the Deque interface, which inherits from the Queue interface, which is an enhancement of Queue.

public interface Deque<E> extends Queue<E> {
    // Add elements to the queue head
    void addFirst(E e);
    // Add elements to the end of the queue
    void addLast(E e);
    // Add elements to the queue head
    boolean offerFirst(E e);
    // Add elements to the end of the queue
    boolean offerLast(E e);
    // Remove elements from the queue head
    E removeFirst();
    // Remove elements from the end of the queue
    E removeLast();
    // Remove elements from the queue head
    E pollFirst();
    // Remove elements from the end of the queue
    E pollLast();
    // View Queue Header Elements
    E getFirst();
    // Look at the end of the queue element
    E getLast();
    // View Queue Header Elements
    E peekFirst();
    // Look at the end of the queue element
    E peekLast();
    // Remove the specified element from the queue head by traversing backwards
    boolean removeFirstOccurrence(Object o);
    // Remove the specified element by traversing forward from the end of the queue
    boolean removeLastOccurrence(Object o);

    // Method in the *** Queue ***
    
    // Add element, equal to addLast(e)
    boolean add(E e);
     // Add element, equal to offer Last (e)
    boolean offer(E e);
    // Remove the element, equal to removeFirst()
    E remove();
    // Remove the element, equal to pollFirst()
    E poll();
    // View the element, equal to getFirst()
    E element();
    // View the element, equal to peekFirst()
    E peek();

    // *** Stack Method***

    // On the stack, equal to addFirst(e)
    void push(E e);
    // Out of the stack, equal to removeFirst()
    E pop();

    // Method in *** Collection ***
    
    // Delete the specified element, equal to removeFirstOccurrence(o)
    boolean remove(Object o);
    // Check whether an element is included
    boolean contains(Object o);
    // Element number
    public int size();
    // iterator
    Iterator<E> iterator();
    // reverse iterator
    Iterator<E> descendingIterator();
}

The following new methods have been added to Deque:

(1) * First, which means to manipulate elements from the queue head;

(2) * Last, which means to manipulate elements from the end of the queue;

(3) push(e), pop(), the way to manipulate elements by stack;

Source code analysis

Main attributes

// An array of storage elements
transient Object[] elements; // non-private to simplify nested class access
// Queue head position
transient int head;
// Queue tail position
transient int tail;
// Minimum initial capacity
private static final int MIN_INITIAL_CAPACITY = 8;

From the attributes, we can see that ArrayDeque uses arrays to store elements and uses head and tail pointers to identify the head and tail of the queue, with a minimum capacity of 8.

Main construction methods

// Default construction method with initial capacity of 16
public ArrayDeque() {
    elements = new Object[16];
}
// Initialization of specified number of elements
public ArrayDeque(int numElements) {
    allocateElements(numElements);
}
// Initialize elements in set c into arrays
public ArrayDeque(Collection<? extends E> c) {
    allocateElements(c.size());
    addAll(c);
}
// Initialize arrays
private void allocateElements(int numElements) {
    elements = new Object[calculateSize(numElements)];
}
// Computing capacity, the logic of this code is to compute the closest n-th power greater than numElements and no less than 8
// For example, 3 is 8, 9 is 16, 33 is 64.
private static int calculateSize(int numElements) {
    int initialCapacity = MIN_INITIAL_CAPACITY;
    // Find the best power of two to hold elements.
    // Tests "<=" because arrays aren't kept full.
    if (numElements >= initialCapacity) {
        initialCapacity = numElements;
        initialCapacity |= (initialCapacity >>>  1);
        initialCapacity |= (initialCapacity >>>  2);
        initialCapacity |= (initialCapacity >>>  4);
        initialCapacity |= (initialCapacity >>>  8);
        initialCapacity |= (initialCapacity >>> 16);
        initialCapacity++;

        if (initialCapacity < 0)   // Too many elements, must back off
            initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements
    }
    return initialCapacity;
}

Through the construction method, we know that the default initial capacity is 16 and the minimum capacity is 8.

Join the team

There are many ways to join the team. Here we mainly analyze two ways, addFirst(e) and addLast(e).

// Enter from the queue
public void addFirst(E e) {
    // No null element allowed
    if (e == null)
        throw new NullPointerException();
    // Reduce the head pointer by 1 and the array length by 1
    // This is to prevent boundary overflow at the end of the array
    // If it comes to an end, go from the end to the front
    // Equivalent to recycling arrays
    elements[head = (head - 1) & (elements.length - 1)] = e;
    // If the head and tail are close together, the capacity will be expanded.
    // The expansion rule is also simple, directly twice the size.
    if (head == tail)
        doubleCapacity();
}
// Enter from the end of the queue
public void addLast(E e) {
    // No null element allowed
    if (e == null)
        throw new NullPointerException();
    // Place the element in the tail pointer position
    // You can see that the tail pointer points to the next position of the last element of the queue
    elements[tail] = e;
    // tail pointer plus 1, starting from the beginning if it reaches the end of the array
    if ( (tail = (tail + 1) & (elements.length - 1)) == head)
        doubleCapacity();
}

(1) There are two ways to enter the queue, from the head of the queue or from the tail of the queue;

(2) If the capacity is insufficient, it will be doubled directly.

(3) Circulate the head and tail pointers in the range of arrays by modularization.

(4) X & (len - 1) = x% len, which is faster to use;

Capacity expansion

private void doubleCapacity() {
    assert head == tail;
    // Position of Head Pointer
    int p = head;
    // Old array length
    int n = elements.length;
    // The distance between the head pointer and the end of the array
    int r = n - p; // number of elements to the right of p
    // The new length is twice as long as the old one.
    int newCapacity = n << 1;
    // Judging whether or not spillover occurs
    if (newCapacity < 0)
        throw new IllegalStateException("Sorry, deque too big");
    // New Array
    Object[] a = new Object[newCapacity];
    // Copy the elements after the old array head into the new array
    System.arraycopy(elements, p, a, 0, r);
    // Copy the elements between the old array subscript 0 and head to the new array
    System.arraycopy(elements, 0, a, r, p);
    // Assignment to a new array
    elements = a;
    // head points to 0, tail points to the position represented by the length of the old array
    head = 0;
    tail = n;
}

The migration elements may be somewhat circumscribed here. See the picture below to understand.

Team out

There are also many ways to get out of the team. We mainly look at two ways, pollFirst() and pollLast().

// Out of the queue
public E pollFirst() {
    int h = head;
    @SuppressWarnings("unchecked")
    // Take the queue head element
    E result = (E) elements[h];
    // If the queue is empty, return null
    if (result == null)
        return null;
    // Leave the queue empty
    elements[h] = null;     // Must null out slot
    // Queue head pointer moved one bit to the right
    head = (h + 1) & (elements.length - 1);
    // Returns the element obtained
    return result;
}
// Get out of line at the end of the queue
public E pollLast() {
    // Tail pointer moved one bit to the left
    int t = (tail - 1) & (elements.length - 1);
    @SuppressWarnings("unchecked")
    // Take the element at the current tail pointer
    E result = (E) elements[t];
    // Return null if the queue is empty
    if (result == null)
        return null;
    // Dispose the current tail pointer to null
    elements[t] = null;
    // Tail points to the new tail pointer
    tail = t;
    // Returns the element obtained
    return result;
}

(1) There are two ways to get out of the queue, from the head of the queue or from the tail of the queue;

(2) Circulate the head and tail pointers in the range of arrays by modularization.

(3) Haha did not shrink after leaving the team.^^

Stack

When we introduced Deque earlier, we said that Deque can be used directly as a stack, so how does Array Deque work?

public void push(E e) {
    addFirst(e);
}

public E pop() {
    return removeFirst();
}

Is it very simple, as long as the queue head is operated in the stack and out of the stack?

summary

(1) Array Deque is a double-ended queue implemented by array.

(2) Array Deque's entry into the queue is achieved by using arrays with head and tail pointers.

(3) When the capacity of Array Deque is insufficient, it will expand, doubling the capacity of each expansion;

(4) Array Deque can be used directly as a stack;

Egg

Double-ended queue and double-ended queue?

Deque refers to a queue in which both ends of the queue can enter and exit elements, in which the real elements are stored.

Dual Queue refers to a kind of queue which has two purposes. The nodes in it are divided into data nodes and non-data nodes. It is the data structure used by LinkedTransferQueue.

Remember LinkedTransferQueue? Click on the link to go directly[ LinkedTransferQueue Source Analysis of Dead java Sets].

Welcome to pay attention to my public number "Tong Ge Reads Source Code". Check out more articles about source code series and enjoy the sea of source code with Tong Ge.

Posted by elibizif on Mon, 29 Apr 2019 13:40:36 -0700