
org.jhotdraw8.collection.primitive.IntArrayDeque Maven / Gradle / Ivy
/*
* @(#)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 - 2025 Weber Informatics LLC | Privacy Policy