All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jhotdraw8.collection.primitive.IntArrayDeque Maven / Gradle / Ivy

The newest version!
/*
 * @(#)IntArrayDeque.java
 * Copyright © 2023 The authors and contributors of JHotDraw. MIT License.
 */
package org.jhotdraw8.collection.primitive;


import java.util.AbstractCollection;
import java.util.ConcurrentModificationException;
import java.util.Deque;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.PrimitiveIterator;

/**
 * A {@code int}-valued deque backed by a primitive array.
 *
 * @author Werner Randelshofer
 */
public class IntArrayDeque extends AbstractCollection implements IntDeque {
    /**
     * The length of this array is always a power of 2.
     */
    private int[] elements;

    /**
     * Index of the element at the head of the deque.
     */
    private int head;

    /**
     * Index at which the next element would be added to the tail of the deque.
     */
    private int tail;

    /**
     * Creates a new instance with an initial capacity for 8 elements.
     */
    public IntArrayDeque() {
        this(8);
    }

    /**
     * Creates a new instance with the specified initial capacity rounded up
     * to the next strictly positive power of two.
     *
     * @param capacity initial capacity
     */
    public IntArrayDeque(int capacity) {
        if (capacity < 0) {
            throw new IllegalArgumentException("Capacity must be non-negative. capacity=" + capacity + ".");
        }
        elements = new int[Math.max(Integer.highestOneBit(capacity + capacity - 1), 0)];
    }


    @Override
    public void addFirstAsInt(int e) {
        //Note: elements.length is a power of two.
        head = (head - 1) & (elements.length - 1);
        elements[head] = e;
        if (head == tail) {
            doubleCapacity();
        }
    }

    /**
     * Adds first using branch-less code that takes advantage of the out-of-order
     * execution unit in the CPU.
     *
     * @param e         an element
     * @param reallyAdd true if this element should really be added
     */
    public void addFirstAsIntBranchless(final int e, final boolean reallyAdd) {
        //Note: elements.length is a power of two.
        final int index = (head - 1) & (elements.length - 1);
        elements[index] = e;
        if (reallyAdd) {
            head = index;
            if (head == tail) {
                doubleCapacity();
            }
        }
    }

    @Override
    public void addLastAllAsInt(int[] array) {
        addLastAllAsInt(array, 0, array.length);
    }

    @Override
    public void addLastAllAsInt(int[] array, int offset, int length) {
        grow(length + size());


        int firstPart = elements.length - tail;
        if (tail >= head && firstPart >= length
                || head - tail > length) {
            System.arraycopy(array, offset, elements, tail, length);
            tail = (tail + length) & (elements.length - 1);
            return;
        }

        System.arraycopy(array, offset, elements, tail, firstPart);
        int secondPart = length - firstPart;
        System.arraycopy(array, offset + firstPart, elements, 0, secondPart);
        tail = secondPart;
    }

    /**
     * Increases the capacity of this deque.
     */
    private void grow(int capacity) {
        if (elements.length > capacity) {
            return;
        }
        int newLength = Integer.highestOneBit(capacity + capacity - 1);
        final int[] a = new int[newLength];
        int size = size();
        if (head < tail) {
            System.arraycopy(elements, head, a, 0, size);
        } else {
            final int r = elements.length - head; // number of elements to the right of head
            System.arraycopy(elements, head, a, 0, r);
            System.arraycopy(elements, 0, a, r, head);
        }
        elements = a;
        head = 0;
        tail = size;
    }

    @Override
    public void addLastAsInt(int e) {
        elements[tail] = e;
        tail = (tail + 1) & (elements.length - 1);
        if (tail == head) {
            doubleCapacity();
        }
    }

    /**
     * Adds last using branch-less code that takes advantage of the out-of-order
     * execution unit in the CPU.
     *
     * @param e         an element
     * @param reallyAdd true if this element should really be added
     */
    public void addLastAsIntBranchless(int e, boolean reallyAdd) {
        elements[tail] = e;
        if (reallyAdd) {
            tail = (tail + 1) & (elements.length - 1);
            if (tail == head) {
                doubleCapacity();
            }
        }
    }

    /**
     * Clears the deque in O(1).
     */
    @Override
    public void clear() {
        // Performance: Do not fill the array with zeros.
        this.head = this.tail = 0;
    }

    @Override
    public PrimitiveIterator.OfInt descendingIterator() {
        return new ReverseDeqIterator();
    }


    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof IntArrayDeque that)) {
            return false;
        }
        if (this.size() != that.size()) {
            return false;
        }
        int thisMask = elements.length - 1;
        int thatMask = that.elements.length - 1;
        for (int i = this.head, j = that.head; i != this.tail; i = (i + 1) & thisMask, j = (j + 1) & thatMask) {
            if (this.elements[i] != that.elements[j]) {
                return false;
            }
        }
        return true;
    }

    /**
     * Returns the first index of the specified element
     * or -1 if this deque does not contain the element.
     *
     * @param o the element
     * @return the first index of the element
     */
    public int firstIndexOfAsInt(int o) {
        if (tail < head) {
            for (int i = head; i < elements.length; i++) {
                if (o == (elements[i])) {
                    return i - head;
                }
            }
            for (int i = 0; i < tail; i++) {
                if (o == (elements[i])) {
                    return i + elements.length - head;
                }
            }
        } else {
            for (int i = head; i < tail; i++) {
                if (o == (elements[i])) {
                    return i - head;
                }
            }
        }
        return -1;
    }


    @Override
    public int getFirstAsInt() {
        if (head == tail) {
            throw new NoSuchElementException();
        }
        return elements[head];
    }

    public int getAsInt(int index) {
        Objects.checkIndex(index, size());
        return (head + index < elements.length) ? elements[head + index] : elements[head + index - elements.length];
    }


    @Override
    public int getLastAsInt() {
        if (head == tail) {
            throw new NoSuchElementException();
        }
        return elements[tail == 0 ? elements.length - 1 : tail - 1];
    }

    /**
     * Increases the capacity of this deque.
     */
    private void doubleCapacity() {
        assert head == tail;
        final int p = head;
        final int n = elements.length;
        final int r = n - p; // number of elements to the right of p
        final int[] a = new int[n << 1];
        System.arraycopy(elements, p, a, 0, r);
        System.arraycopy(elements, 0, a, r, p);
        elements = a;
        head = 0;
        tail = n;
    }


    @Override
    public int hashCode() {
        int hash = 0;
        int mask = elements.length - 1;
        for (int i = head; i != tail; i = (i + 1) & mask) {
            hash = hash * 31 + elements[i];
        }
        return hash;
    }

    /**
     * Returns true if this deque is empty.
     *
     * @return {@code true} if this deque contains no elements
     */
    @Override
    public boolean isEmpty() {
        return head == tail;
    }

    @Override
    public Deque reversed() {
        throw new UnsupportedOperationException();
    }

    @Override
    public PrimitiveIterator.OfInt iterator() {
        return new DeqIterator();
    }

    /**
     * Returns the last index of the specified element
     * or -1 if this deque does not contain the element.
     *
     * @param o the element
     * @return the last index of the element
     */
    public int lastIndexOfAsInt(int o) {
        if (tail < head) {
            for (int i = elements.length - 1; i >= head; i--) {
                if (o == (elements[i])) {
                    return i - head;
                }
            }
            for (int i = tail - 1; i >= 0; i--) {
                if (o == (elements[i])) {
                    return i + elements.length - head;
                }
            }
        } else {
            for (int i = tail - 1; i >= head; i--) {
                if (o == (elements[i])) {
                    return i - head;
                }
            }
        }
        return -1;
    }

    /**
     * Removes an element at the given array index.
     *
     * @param i an array index
     */
    public void removeAt(int i) {
        int size = size();
        Objects.checkIndex(i, size);
        if (tail < head) {
            if (head + i < elements.length) {
                if (i > 0) {
                    System.arraycopy(elements, head, elements, head + 1, i - 1);
                }
                elements[head] = 0;
                head = head == elements.length ? 0 : head + 1;
            } else {
                if (i < size - 1) {
                    System.arraycopy(elements, i - elements.length + head + 1, elements, i - elements.length + head, size - i);
                }
                elements[tail] = 0;
                tail = tail == 0 ? elements.length : tail - 1;
            }
        } else {
            if (i < size - 1) {
                System.arraycopy(elements, head + i + 1, elements, head + i, size - i);
            }
            elements[head + i] = 0;
            tail--;
        }
    }

    @Override
    public int removeFirstAsInt() {
        if (head == tail) {
            throw new NoSuchElementException();
        }
        int result = elements[head];
        elements[head] = 0;
        head = (head == elements.length - 1) ? 0 : head + 1;
        return result;
    }

    @Override
    public boolean removeFirstOccurrenceAsInt(int o) {
        int index = firstIndexOfAsInt(o);
        if (index != -1) {
            removeAt(index);
            return true;
        }
        return false;
    }

    @Override
    public boolean removeLastOccurrenceAsInt(int o) {
        int index = lastIndexOfAsInt(o);
        if (index != -1) {
            removeAt(index);
            return true;
        }
        return false;
    }

    @Override
    public int removeLastAsInt() {
        if (head == tail) {
            throw new NoSuchElementException();
        }
        tail = (tail == 0) ? elements.length - 1 : tail - 1;
        int result = elements[tail];
        elements[tail] = 0;
        return result;
    }

    @Override
    public boolean contains(Object o) {
        if (o instanceof Integer) {
            return firstIndexOfAsInt((int) o) != -1;
        }
        return false;
    }

    @Override
    public int size() {
        return (tail - head) & (elements.length - 1);
    }

    public String toString() {
        Iterator it = iterator();
        if (!it.hasNext()) {
            return "[]";
        }

        StringBuilder sb = new StringBuilder();
        sb.append('[');
        for (; ; ) {
            Integer e = it.next();
            sb.append(e);
            if (!it.hasNext()) {
                return sb.append(']').toString();
            }
            sb.append(',').append(' ');
        }
    }

    private class DeqIterator implements PrimitiveIterator.OfInt {
        /**
         * Tail recorded at construction, to stop
         * iterator and also to check for co-modification.
         */
        private final int fence = tail;
        /**
         * Index of element to be returned by subsequent call to next.
         */
        private int cursor = head;

        @Override
        public boolean hasNext() {
            return cursor != fence;
        }

        @Override
        public int nextInt() {
            if (cursor == fence) {
                throw new NoSuchElementException();
            }
            int result = elements[cursor];
            // This check doesn't catch all possible co-modifications,
            // but does catch the ones that corrupt traversal
            if (tail != fence) {
                throw new ConcurrentModificationException();
            }
            cursor = (cursor + 1) & (elements.length - 1);
            return result;
        }
    }

    private class ReverseDeqIterator implements PrimitiveIterator.OfInt {
        /**
         * Tail recorded at construction, to stop
         * iterator and also to check for co-modification.
         */
        private final int fence = head;
        /**
         * Index of element to be returned by subsequent call to next.
         */
        private int cursor = tail;

        @Override
        public boolean hasNext() {
            return cursor != fence;
        }

        @Override
        public int nextInt() {
            if (cursor == fence) {
                throw new NoSuchElementException();
            }
            int result = elements[cursor];
            // This check doesn't catch all possible co-modifications,
            // but does catch the ones that corrupt traversal
            if (tail != fence) {
                throw new ConcurrentModificationException();
            }
            cursor = (cursor - 1) & (elements.length - 1);
            return result;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy