com.github.tommyettinger.ds.CharDeque Maven / Gradle / Ivy
Show all versions of jdkgdxds Show documentation
/*
* Copyright (c) 2022-2025 See AUTHORS file.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.github.tommyettinger.ds;
import com.github.tommyettinger.ds.support.sort.CharComparator;
import com.github.tommyettinger.ds.support.sort.CharComparators;
import com.github.tommyettinger.ds.support.util.CharIterator;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Arrays;
import java.util.Collection;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import java.util.Random;
/**
* A resizable, insertion-ordered double-ended queue of chars with efficient add and remove at the beginning and end. Values in the
* backing array may wrap back to the beginning, making add and remove at the beginning and end O(1) (unless the backing array needs to
* resize when adding). Deque functionality is provided via {@link #removeLast()} and {@link #addFirst(char)}.
*
* Unlike most Deque implementations in the JDK, you can get and set items anywhere in the deque in constant time with {@link #get(int)}
* and {@link #set(int, char)}.
*/
public class CharDeque implements PrimitiveCollection.OfChar, Arrangeable {
protected char defaultValue = '\uffff';
/**
* Contains the values in the queue. Head and tail indices go in a circle around this array, wrapping at the end.
*/
protected char[] values;
/**
* Index of first element. Logically smaller than tail. Unless empty, it points to a valid element inside queue.
*/
protected int head = 0;
/**
* Index of last element. Logically bigger than head. Usually points to an empty position, but points to the head when full
* (size == values.length).
*/
protected int tail = 0;
/**
* Number of elements in the queue.
*/
public int size = 0;
protected transient @Nullable CharDequeIterator iterator1;
protected transient @Nullable CharDequeIterator iterator2;
protected transient @Nullable CharDequeIterator descendingIterator1;
protected transient @Nullable CharDequeIterator descendingIterator2;
/**
* Creates a new CharDeque which can hold 16 values without needing to resize backing array.
*/
public CharDeque () {
this(16);
}
/**
* Creates a new CharDeque which can hold the specified number of values without needing to resize backing array.
*/
public CharDeque (int initialSize) {
this.values = new char[initialSize];
}
/**
* Creates a new CharDeque using all of the contents of the given PrimitiveCollection.OfChar, such as
* a {@link CharList}.
*
* @param coll a PrimitiveCollection.OfChar that will be copied into this and used in full
*/
public CharDeque (PrimitiveCollection.OfChar coll) {
this(coll.size());
addAll(coll);
}
/**
* Creates a new instance containing the items in the specified iterator.
*
* @param coll an iterator that will have its remaining contents added to this
*/
public CharDeque (CharIterator coll) {
this();
addAll(coll);
}
/**
* Copies the given CharDeque exactly into this one. Individual values will be shallow-copied.
*
* @param deque another CharDeque to copy
*/
public CharDeque (CharDeque deque) {
this.values = Arrays.copyOf(deque.values, deque.values.length);
this.size = deque.size;
this.head = deque.head;
this.tail = deque.tail;
this.defaultValue = deque.defaultValue;
}
/**
* Creates a new CharDeque using all of the contents of the given array.
*
* @param a an array of long that will be copied into this and used in full
*/
public CharDeque (char[] a) {
tail = a.length;
this.values = Arrays.copyOf(a, tail);
size = tail;
}
/**
* Creates a new CharDeque using {@code count} items from {@code a}, starting at {@code offset}.
*
* @param a an array of long
* @param offset where in {@code a} to start using items
* @param count how many items to use from {@code a}
*/
public CharDeque (char[] a, int offset, int count) {
this.values = Arrays.copyOfRange(a, offset, offset + count);
tail = count;
size = count;
}
public char getDefaultValue () {
return defaultValue;
}
public void setDefaultValue (char defaultValue) {
this.defaultValue = defaultValue;
}
/**
* Append given item to the tail (enqueue to tail). Unless backing array needs resizing, operates in O(1) time.
*
* @param item a char to add to the tail
* @see #addFirst(char)
*/
public void addLast (char item) {
char[] values = this.values;
if (size == values.length) {
resize(values.length << 1);
values = this.values;
}
if (tail == values.length) {
tail = 0;
}
values[tail++] = item;
size++;
}
/**
* Prepend given item to the head (enqueue to head). Unless backing array needs resizing, operates in O(1) time.
*
* @param item a char to add to the head
* @see #addLast(char)
*/
public void addFirst (char item) {
char[] values = this.values;
if (size == values.length) {
resize(values.length << 1);
values = this.values;
}
int head = this.head;
head--;
if (head == -1) {
head = values.length - 1;
}
values[head] = item;
this.head = head;
this.size++;
}
/**
* Increases the size of the backing array to accommodate the specified number of additional items. Useful before adding many
* items to avoid multiple backing array resizes.
*/
public void ensureCapacity (int additional) {
final int needed = size + additional;
if (values.length < needed) {
resize(needed);
}
}
/**
* Resize backing array. newSize must be bigger than current size.
*/
protected void resize (int newSize) {
final char[] values = this.values;
final int head = this.head;
final int tail = this.tail;
final char[] newArray = new char[Math.max(1, newSize)];
if (head < tail) {
// Continuous
System.arraycopy(values, head, newArray, 0, tail - head);
} else if (size > 0) {
// Wrapped
final int rest = values.length - head;
System.arraycopy(values, head, newArray, 0, rest);
System.arraycopy(values, 0, newArray, rest, tail);
}
this.values = newArray;
this.head = 0;
this.tail = size;
}
/**
* Remove the first item from the queue. (dequeue from head) Always O(1).
*
* @return removed item
* @throws NoSuchElementException when queue is empty
*/
public char removeFirst () {
if (size == 0) {
// Underflow
throw new NoSuchElementException("CharDeque is empty.");
}
final char[] values = this.values;
final char result = values[head];
head++;
if (head == values.length) {
head = 0;
}
size--;
return result;
}
/**
* Remove the last item from the queue. (dequeue from tail) Always O(1).
*
* @return removed item
* @throws NoSuchElementException when queue is empty
* @see #removeFirst()
*/
public char removeLast () {
if (size == 0) {
throw new NoSuchElementException("CharDeque is empty.");
}
final char[] values = this.values;
int tail = this.tail;
tail--;
if (tail == -1) {
tail = values.length - 1;
}
final char result = values[tail];
this.tail = tail;
size--;
return result;
}
/**
* Inserts the specified element at the front of this deque unless it would
* violate capacity restrictions. When using a capacity-restricted deque,
* this method is generally preferable to the {@link #addFirst} method,
* which can fail to insert an element only by throwing an exception.
*
* @param t the element to add
* @return {@code true} if the element was added to this deque, else
* {@code false}
* @throws IllegalArgumentException if some property of the specified
* element prevents it from being added to this deque
*/
public boolean offerFirst (char t) {
int oldSize = size;
addFirst(t);
return oldSize != size;
}
/**
* Inserts the specified element at the end of this deque unless it would
* violate capacity restrictions. When using a capacity-restricted deque,
* this method is generally preferable to the {@link #addLast} method,
* which can fail to insert an element only by throwing an exception.
*
* @param t the element to add
* @return {@code true} if the element was added to this deque, else
* {@code false}
* @throws IllegalArgumentException if some property of the specified
* element prevents it from being added to this deque
*/
public boolean offerLast (char t) {
int oldSize = size;
addLast(t);
return oldSize != size;
}
/**
* Retrieves and removes the first element of this deque,
* or returns {@link #getDefaultValue() defaultValue} if this deque is empty.
*
* @return the head of this deque, or {@link #getDefaultValue() defaultValue} if this deque is empty
*/
public char pollFirst () {
if(size == 0)
return defaultValue;
final char[] values = this.values;
final char result = values[head];
head++;
if (head == values.length) {
head = 0;
}
size--;
return result;
}
/**
* Retrieves and removes the last element of this deque,
* or returns {@link #getDefaultValue() defaultValue} if this deque is empty.
*
* @return the tail of this deque, or {@link #getDefaultValue() defaultValue} if this deque is empty
*/
public char pollLast () {
if (size == 0) {
return defaultValue;
}
final char[] values = this.values;
int tail = this.tail;
tail--;
if (tail == -1) {
tail = values.length - 1;
}
final char result = values[tail];
this.tail = tail;
size--;
return result;
}
/**
* Retrieves, but does not remove, the first element of this deque.
*
* This method differs from {@link #peekFirst peekFirst} only in that it
* throws an exception if this deque is empty.
*
* @return the head of this deque
* @throws NoSuchElementException if this deque is empty
*/
public char getFirst () {
return first();
}
/**
* Retrieves, but does not remove, the last element of this deque.
* This method differs from {@link #peekLast peekLast} only in that it
* throws an exception if this deque is empty.
*
* @return the tail of this deque
* @throws NoSuchElementException if this deque is empty
*/
public char getLast () {
return last();
}
/**
* Retrieves, but does not remove, the first element of this deque,
* or returns {@link #defaultValue} if this deque is empty.
*
* @return the head of this deque, or {@link #defaultValue} if this deque is empty
*/
public char peekFirst () {
if (size == 0) {
// Underflow
return defaultValue;
}
return values[head];
}
/**
* Retrieves, but does not remove, the last element of this deque,
* or returns {@link #defaultValue} if this deque is empty.
*
* @return the tail of this deque, or {@link #defaultValue} if this deque is empty
*/
public char peekLast () {
if (size == 0) {
// Underflow
return defaultValue;
}
final char[] values = this.values;
int tail = this.tail;
tail--;
if (tail == -1) {
tail = values.length - 1;
}
return values[tail];
}
/**
* Removes the first occurrence of the specified element from this deque.
* If the deque does not contain the element, it is unchanged.
* More formally, removes the first element {@code e} such that
* {@code o == e)} (if such an element exists).
* Returns {@code true} if this deque contained the specified element
* (or equivalently, if this deque changed as a result of the call).
*
* @param o element to be removed from this deque, if present
* @return {@code true} if an element was removed as a result of this call
*/
public boolean removeFirstOccurrence (char o) {
return removeValue(o);
}
/**
* Removes the last occurrence of the specified element from this deque.
* If the deque does not contain the element, it is unchanged.
* More formally, removes the last element {@code e} such that
* {@code o == e} (if such an element exists).
* Returns {@code true} if this deque contained the specified element
* (or equivalently, if this deque changed as a result of the call).
*
* @param o element to be removed from this deque, if present
* @return {@code true} if an element was removed as a result of this call
*/
public boolean removeLastOccurrence (char o) {
return removeLastValue(o);
}
/**
* Inserts the specified element into the queue represented by this deque
* (in other words, at the tail of this deque) if it is possible to do so
* immediately without violating capacity restrictions, returning
* {@code true} upon success and throwing an
* {@code IllegalStateException} if no space is currently available.
* When using a capacity-restricted deque, it is generally preferable to
* use {@link #offer(char) offer}.
*
*
This method is equivalent to {@link #addLast}.
*
* @param t the element to add
* @return {@code true} (as specified by {@link Collection#add})
* @throws IllegalStateException if the element cannot be added at this
* time due to capacity restrictions
* @throws IllegalArgumentException if some property of the specified
* element prevents it from being added to this deque
*/
@Override
public boolean add (char t) {
int oldSize = size;
addLast(t);
return oldSize != size;
}
/**
* Inserts the specified element into this deque at the specified index.
* Unlike {@link #offerFirst(char)} and {@link #offerLast(char)}, this does not run in expected constant time unless
* the index is less than or equal to 0 (where it acts like offerFirst()) or greater than or equal to {@link #size()}
* (where it acts like offerLast()).
* @param index the index in the deque's insertion order to insert the item
* @param item a char item to insert; may be null
* @return true if this deque was modified
*/
public boolean add (int index, char item) {
int oldSize = size;
if(index <= 0)
addFirst(item);
else if(index >= oldSize)
addLast(item);
else {
char[] values = this.values;
if (size == values.length) {
resize(values.length << 1);
values = this.values;
}
if(head < tail) {
index += head;
if(index >= values.length) index -= values.length;
System.arraycopy(values, index, values, (index + 1) % values.length, tail - index);
values[index] = item;
tail++;
if (tail > values.length) {
tail = 1;
}
} else {
if (head + index < values.length) {
// backward shift
System.arraycopy(values, head, values, head - 1, index);
values[head - 1 + index] = item;
head--;
// don't need to check for head being negative, because head is always > tail
}
else {
// forward shift
index -= values.length - 1;
System.arraycopy(values, head + index, values, head + index + 1, tail - head - index);
values[head + index] = item;
tail++;
// again, don't need to check for tail going around, because the head is in the way and doesn't need to move
}
}
size++;
}
return oldSize != size;
}
/**
* This is an alias for {@link #add(int, char)} to improve compatibility with primitive lists.
*
* @param index index at which the specified element is to be inserted
* @param element element to be inserted
*/
public boolean insert (int index, char element) {
return add(index, element);
}
/**
* Inserts the specified element into the queue represented by this deque
* (in other words, at the tail of this deque) if it is possible to do so
* immediately without violating capacity restrictions, returning
* {@code true} upon success and {@code false} if no space is currently
* available. When using a capacity-restricted deque, this method is
* generally preferable to the {@link #add} method, which can fail to
* insert an element only by throwing an exception.
*
*
This method is equivalent to {@link #offerLast}.
*
* @param t the element to add
* @return {@code true} if the element was added to this deque, else
* {@code false}
* @throws IllegalArgumentException if some property of the specified
* element prevents it from being added to this deque
*/
public boolean offer (char t) {
int oldSize = size;
addLast(t);
return oldSize != size;
}
/**
* Retrieves and removes the head of the queue represented by this deque
* (in other words, the first element of this deque).
* This method differs from {@link #poll() poll()} only in that it
* throws an exception if this deque is empty.
*
*
This method is equivalent to {@link #removeFirst()}.
*
* @return the head of the queue represented by this deque
* @throws NoSuchElementException if this deque is empty
*/
public char remove () {
return removeFirst();
}
/**
* Retrieves and removes the head of the queue represented by this deque
* (in other words, the first element of this deque), or returns
* {@link #defaultValue} if this deque is empty.
*
*
This method is equivalent to {@link #pollFirst()}.
*
* @return the first element of this deque, or {@link #defaultValue} if
* this deque is empty
*/
public char poll () {
return pollFirst();
}
/**
* Retrieves, but does not remove, the head of the queue represented by
* this deque (in other words, the first element of this deque).
* This method differs from {@link #peek peek} only in that it throws an
* exception if this deque is empty.
*
*
This method is equivalent to {@link #getFirst()}.
*
* @return the head of the queue represented by this deque
* @throws NoSuchElementException if this deque is empty
*/
public char element () {
return first();
}
/**
* Retrieves, but does not remove, the head of the queue represented by
* this deque (in other words, the first element of this deque), or
* returns {@link #defaultValue} if this deque is empty.
*
*
This method is equivalent to {@link #peekFirst()}.
*
* @return the head of the queue represented by this deque, or
* {@link #defaultValue} if this deque is empty
*/
public char peek () {
return peekFirst();
}
/**
* Pushes an element onto the stack represented by this deque (in other
* words, at the head of this deque) if it is possible to do so
* immediately without violating capacity restrictions, throwing an
* {@code IllegalStateException} if no space is currently available.
*
*
This method is equivalent to {@link #addFirst}.
*
* @param t the element to push
* @throws IllegalStateException if the element cannot be added at this
* time due to capacity restrictions
* @throws ClassCastException if the class of the specified element
* prevents it from being added to this deque
* @throws NullPointerException if the specified element is null and this
* deque does not permit null elements
* @throws IllegalArgumentException if some property of the specified
* element prevents it from being added to this deque
*/
public void push (char t) {
addFirst(t);
}
/**
* Pops an element from the stack represented by this deque. In other
* words, removes and returns the first element of this deque.
*
*
This method is equivalent to {@link #removeFirst()}.
*
* @return the element at the front of this deque (which is the top
* of the stack represented by this deque)
* @throws NoSuchElementException if this deque is empty
*/
public char pop () {
return removeFirst();
}
/**
* Removes the first occurrence of the specified element from this deque.
* If the deque does not contain the element, it is unchanged.
* More formally, removes the first element {@code e} such that
* {@code Objects.equals(o, e)} (if such an element exists).
* Returns {@code true} if this deque contained the specified element
* (or equivalently, if this deque changed as a result of the call).
*
*
This method is equivalent to {@link #removeFirstOccurrence(char)}.
*
* @param o element to be removed from this deque, if present
* @return {@code true} if an element was removed as a result of this call
* @throws ClassCastException if the class of the specified element
* is incompatible with this deque
* (optional)
* @throws NullPointerException if the specified element is null and this
* deque does not permit null elements
* (optional)
*/
public boolean remove (char o) {
return removeFirstOccurrence(o);
}
/**
* Returns {@code true} if this deque contains the specified element.
* More formally, returns {@code true} if and only if this deque contains
* at least one element {@code e} such that {@code Objects.equals(o, e)}.
*
* @param o element whose presence in this deque is to be tested
* @return {@code true} if this deque contains the specified element
* @throws ClassCastException if the class of the specified element
* is incompatible with this deque
* (optional)
* @throws NullPointerException if the specified element is null and this
* deque does not permit null elements
* (optional)
*/
public boolean contains (char o) {
return indexOf(o) != -1;
}
/**
* Returns the number of elements in this deque.
*
* @return the number of elements in this deque
*/
@Override
public int size () {
return size;
}
/**
* Returns an array containing all of the elements in this collection.
* If this collection makes any guarantees as to what order its elements
* are returned by its iterator, this method must return the elements in
* the same order. The returned array's {@linkplain Class#getComponentType
* runtime component type} is {@code Object}.
*
*
The returned array will be "safe" in that no references to it are
* maintained by this collection. (In other words, this method must
* allocate a new array even if this collection is backed by an array).
* The caller is thus free to modify the returned array.
*
* @return an array, whose {@linkplain Class#getComponentType runtime component
* type} is {@code Object}, containing all of the elements in this collection
*/
@Override
public char[] toArray () {
char[] next = new char[size];
if (head < tail) {
System.arraycopy(values, head, next, 0, tail - head);
} else {
System.arraycopy(values, head, next, 0, size - head);
System.arraycopy(values, 0, next, size - head, tail);
}
return next;
}
/**
* Reduces the size of the deque to the specified size. If the deque is already smaller than the specified
* size, no action is taken.
*/
public void truncate (int newSize) {
newSize = Math.max(0, newSize);
if (size() > newSize) {
if(head < tail) {
// only removing from tail, near the end, toward head, near the start
tail -= size() - newSize;
size = newSize;
} else if(head + newSize < values.length) {
// tail is near the start, but we have to remove elements through the start and into the back
tail = head + newSize;
size = newSize;
} else {
// tail is near the start, but we only have to remove some elements between tail and the start
tail -= size() - newSize;
size = newSize;
}
}
}
/**
* Returns the index of first occurrence of value in the queue, or -1 if no such value exists.
*
* @return An index of first occurrence of value in queue or -1 if no such value exists
*/
public int indexOf (char value) {
if (size == 0)
return -1;
char[] values = this.values;
final int head = this.head, tail = this.tail;
if (head < tail) {
for (int i = head; i < tail; i++)
if (values[i] == value)
return i - head;
} else {
for (int i = head, n = values.length; i < n; i++)
if (values[i] == value)
return i - head;
for (int i = 0; i < tail; i++)
if (values[i] == value)
return i + values.length - head;
}
return -1;
}
/**
* Returns the index of last occurrence of value in the queue, or -1 if no such value exists.
*
* @return An index of last occurrence of value in queue or -1 if no such value exists
*/
public int lastIndexOf (char value) {
if (size == 0)
return -1;
char[] values = this.values;
final int head = this.head, tail = this.tail;
if (head < tail) {
for (int i = tail - 1; i >= head; i--)
if (values[i] == value)
return i - head;
} else {
for (int i = tail - 1; i >= 0; i--)
if (values[i] == value)
return i + values.length - head;
for (int i = values.length - 1; i >= head; i--)
if (values[i] == value)
return i - head;
}
return -1;
}
/**
* Removes the first instance of the specified value in the queue.
*
* @return true if value was found and removed, false otherwise
*/
public boolean removeValue (char value) {
int index = indexOf(value);
if (index == -1)
return false;
removeAt(index);
return true;
}
/**
* Removes the last instance of the specified value in the queue.
*
* @return true if value was found and removed, false otherwise
*/
public boolean removeLastValue (char value) {
int index = lastIndexOf(value);
if (index == -1)
return false;
removeAt(index);
return true;
}
/**
* Removes and returns the item at the specified index.
*/
public char removeAt (int index) {
if (index < 0)
throw new IndexOutOfBoundsException("index can't be < 0: " + index);
if (index >= size)
throw new IndexOutOfBoundsException("index can't be >= size: " + index + " >= " + size);
char[] values = this.values;
int head = this.head, tail = this.tail;
index += head;
char value;
if (head < tail) { // index is between head and tail.
value = values[index];
System.arraycopy(values, index + 1, values, index, tail - index - 1);
this.tail--;
} else if (index >= values.length) { // index is between 0 and tail.
index -= values.length;
value = values[index];
System.arraycopy(values, index + 1, values, index, tail - index - 1);
this.tail--;
} else { // index is between head and values.length.
value = values[index];
System.arraycopy(values, head, values, head + 1, index - head);
this.head++;
if (this.head == values.length) {
this.head = 0;
}
}
size--;
return value;
}
/**
* Returns true if the queue has one or more items.
*/
public boolean notEmpty () {
return size != 0;
}
/**
* Returns true if the queue is empty.
*/
public boolean isEmpty () {
return size == 0;
}
/**
* Returns the first (head) item in the queue (without removing it).
*
* @throws NoSuchElementException when queue is empty
* @see #addFirst(char)
* @see #removeFirst()
*/
public char first () {
if (size == 0) {
// Underflow
throw new NoSuchElementException("CharDeque is empty.");
}
return values[head];
}
/**
* Returns the last (tail) item in the queue (without removing it).
*
* @throws NoSuchElementException when queue is empty
* @see #addLast(char)
* @see #removeLast()
*/
public char last () {
if (size == 0) {
// Underflow
throw new NoSuchElementException("CharDeque is empty.");
}
final char[] values = this.values;
int tail = this.tail;
tail--;
if (tail == -1)
tail = values.length - 1;
return values[tail];
}
/**
* Retrieves the value in queue without removing it. Indexing is from the front to back, zero based. Therefore get(0) is the
* same as {@link #first()}.
*
* @throws IndexOutOfBoundsException when the index is negative or >= size
*/
public char get (int index) {
if (index < 0)
throw new IndexOutOfBoundsException("index can't be < 0: " + index);
if (index >= size)
throw new IndexOutOfBoundsException("index can't be >= size: " + index + " >= " + size);
final char[] values = this.values;
int i = head + index;
if (i >= values.length)
i -= values.length;
return values[i];
}
/**
* Sets an existing position in this deque to the given item. Indexing is from the front to back, zero based.
*
* @param index the index to set
* @param item what value should replace the contents of the specified index
* @return the previous contents of the specified index
* @throws IndexOutOfBoundsException when the index is negative or >= size
*/
public char set (int index, char item) {
if (index < 0)
throw new IndexOutOfBoundsException("index can't be < 0: " + index);
if (index >= size)
throw new IndexOutOfBoundsException("index can't be >= size: " + index + " >= " + size);
final char[] values = this.values;
int i = head + index;
if (i >= values.length)
i -= values.length;
char old = values[i];
values[i] = item;
return old;
}
/**
* Removes all values from this queue. Values in backing array are set to null to prevent memory leak, so this operates in
* O(n).
*/
public void clear () {
if (size == 0)
return;
this.head = 0;
this.tail = 0;
this.size = 0;
}
/**
* Returns an iterator for the items in the deque. Remove is supported.
*
* Reuses one of two iterators for this deque. For nested or multithreaded
* iteration, use {@link CharDequeIterator#CharDequeIterator(CharDeque)}.
*/
@Override
public CharDequeIterator iterator () {
if (iterator1 == null || iterator2 == null) {
iterator1 = new CharDequeIterator(this);
iterator2 = new CharDequeIterator(this);
}
if (!iterator1.valid) {
iterator1.reset();
iterator1.valid = true;
iterator2.valid = false;
return iterator1;
}
iterator2.reset();
iterator2.valid = true;
iterator1.valid = false;
return iterator2;
}
/**
* Returns an iterator over the elements in this deque in reverse
* sequential order. The elements will be returned in order from
* last (tail) to first (head).
*
* Reuses one of two descending iterators for this deque. For nested or multithreaded
* iteration, use {@link CharDequeIterator#CharDequeIterator(CharDeque, boolean)}.
*
* @return an iterator over the elements in this deque in reverse sequence
*/
public CharDequeIterator descendingIterator () {
if (descendingIterator1 == null || descendingIterator2 == null) {
descendingIterator1 = new CharDequeIterator(this, true);
descendingIterator2 = new CharDequeIterator(this, true);
}
if (!descendingIterator1.valid) {
descendingIterator1.reset();
descendingIterator1.valid = true;
descendingIterator2.valid = false;
return descendingIterator1;
}
descendingIterator2.reset();
descendingIterator2.valid = true;
descendingIterator1.valid = false;
return descendingIterator2;
}
@Override
public String toString () {
return toString(", ", true);
}
/**
* Simply returns all the char items in this as one String, with no delimiters.
*
* @return a String containing only the char items in this CharDeque
*/
public String toDenseString () {
if (head < tail)
return String.valueOf(values, head, size);
else
return String.valueOf(values, head, values.length - head) + String.valueOf(values, 0, tail);
}
public int hashCode () {
final int size = this.size;
final char[] values = this.values;
final int backingLength = values.length;
int index = this.head;
int hash = size + 1;
for (int s = 0; s < size; s++) {
final char value = values[index];
hash *= 421;
hash += value;
index++;
if (index == backingLength)
index = 0;
}
return hash;
}
public boolean equals (Object o) {
if (this == o)
return true;
if (!(o instanceof CharDeque))
return false;
CharDeque q = (CharDeque)o;
final int size = this.size;
if (q.size != size)
return false;
final char[] myValues = this.values;
final int myBackingLength = myValues.length;
final char[] itsValues = q.values;
final int itsBackingLength = itsValues.length;
int myIndex = head;
int itsIndex = q.head;
for (int s = 0; s < size; s++) {
char myValue = myValues[myIndex];
char itsValue = itsValues[itsIndex];
if (myValue != itsValue)
return false;
myIndex++;
itsIndex++;
if (myIndex == myBackingLength)
myIndex = 0;
if (itsIndex == itsBackingLength)
itsIndex = 0;
}
return true;
}
/**
* Switches the ordering of positions {@code first} and {@code second}, without changing any items beyond that.
*
* @param first the first position, must not be negative and must be less than {@link #size()}
* @param second the second position, must not be negative and must be less than {@link #size()}
*/
@Override
public void swap (int first, int second) {
if (first < 0)
throw new IndexOutOfBoundsException("first index can't be < 0: " + first);
if (first >= size)
throw new IndexOutOfBoundsException("first index can't be >= size: " + first + " >= " + size);
if (second < 0)
throw new IndexOutOfBoundsException("second index can't be < 0: " + second);
if (second >= size)
throw new IndexOutOfBoundsException("second index can't be >= size: " + second + " >= " + size);
final char[] values = this.values;
int f = head + first;
if (f >= values.length)
f -= values.length;
int s = head + second;
if (s >= values.length)
s -= values.length;
char fv = values[f];
values[f] = values[s];
values[s] = fv;
}
/**
* Reverses this CharDeque in-place.
*/
@Override
public void reverse () {
final char[] values = this.values;
int f, s, len = values.length;
char fv;
for (int n = size >> 1, b = 0, t = size - 1; b <= n && b != t; b++, t--) {
f = head + b;
if (f >= len)
f -= len;
s = head + t;
if (s >= len)
s -= len;
fv = values[f];
values[f] = values[s];
values[s] = fv;
}
}
/**
* Sorts this deque in-place using {@link Arrays#sort(char[], int, int)}.
* This should operate in O(n log(n)) time or less when the internals of the deque are
* continuous (the head is before the tail in the array). If the internals are not
* continuous, this takes an additional O(n) step (where n is less than the size of
* the deque) to rearrange the internals before sorting.
*/
public void sort () {
if (head <= tail) {
Arrays.sort(values, head, tail);
} else {
System.arraycopy(values, head, values, tail, values.length - head);
Arrays.sort(values, 0, size);
tail = size;
head = 0;
}
}
/**
* Sorts this deque in-place using {@link CharComparators#sort(char[], int, int, CharComparator)}.
* This should operate in O(n log(n)) time or less when the internals of the deque are
* continuous (the head is before the tail in the array). If the internals are not
* continuous, this takes an additional O(n) step (where n is less than the size of
* the deque) to rearrange the internals before sorting.
*/
public void sort (@Nullable CharComparator c) {
if (head <= tail) {
CharComparators.sort(values, head, tail, c);
} else {
System.arraycopy(values, head, values, tail, values.length - head);
CharComparators.sort(values, 0, size, c);
tail = size;
head = 0;
}
}
public char random (Random random) {
if (size <= 0) {
throw new NoSuchElementException("CharDeque is empty.");
}
return get(random.nextInt(size));
}
/**
* A {@link CharIterator}, plus similar methods to a {@link ListIterator}, over the elements of an CharDeque.
* Use {@link #nextChar()} in preference to {@link #next()} to avoid allocating Character objects.
*/
public static class CharDequeIterator implements CharIterator {
protected int index, latest = -1;
protected CharDeque deque;
protected boolean valid = true;
private final int direction;
public CharDequeIterator (CharDeque deque) {
this(deque, false);
}
public CharDequeIterator (CharDeque deque, boolean descendingOrder) {
this.deque = deque;
direction = descendingOrder ? -1 : 1;
}
public CharDequeIterator (CharDeque deque, int index, boolean descendingOrder) {
if (index < 0 || index >= deque.size())
throw new IndexOutOfBoundsException("CharDequeIterator does not satisfy index >= 0 && index < deque.size()");
this.deque = deque;
this.index = index;
direction = descendingOrder ? -1 : 1;
}
/**
* Returns the next {@code char} element in the iteration.
*
* @return the next {@code char} element in the iteration
* @throws NoSuchElementException if the iteration has no more elements
*/
public char nextChar () {
if (!hasNext()) {throw new NoSuchElementException();}
latest = index;
index += direction;
return deque.get(latest);
}
/**
* Returns {@code true} if the iteration has more elements.
* (In other words, returns {@code true} if {@link #nextChar} would
* return an element rather than throwing an exception.)
*
* @return {@code true} if the iteration has more elements
*/
@Override
public boolean hasNext () {
if (!valid) {throw new RuntimeException("#iterator() cannot be used nested.");}
return direction == 1 ? index < deque.size() : index > 0 && deque.notEmpty();
}
/**
* Returns {@code true} if this list iterator has more elements when
* traversing the list in the reverse direction. (In other words,
* returns {@code true} if {@link #previousChar} would return an element
* rather than throwing an exception.)
*
* @return {@code true} if the list iterator has more elements when
* traversing the list in the reverse direction
*/
public boolean hasPrevious () {
if (!valid) {throw new RuntimeException("#iterator() cannot be used nested.");}
return direction == -1 ? index < deque.size() : index > 0 && deque.notEmpty();
}
/**
* Returns the previous element in the list and moves the cursor
* position backwards. This method may be called repeatedly to
* iterate through the list backwards, or intermixed with calls to
* {@link #nextChar} to go back and forth. (Note that alternating calls
* to {@code next} and {@code previous} will return the same
* element repeatedly.)
*
* @return the previous element in the list
* @throws NoSuchElementException if the iteration has no previous
* element
*/
public char previousChar () {
if (!hasPrevious()) {throw new NoSuchElementException();}
return deque.get(latest = (index -= direction));
}
/**
* Returns the index of the element that would be returned by a
* subsequent call to {@link #nextChar}. (Returns list size if the list
* iterator is at the end of the list.)
*
* @return the index of the element that would be returned by a
* subsequent call to {@code next}, or list size if the list
* iterator is at the end of the list
*/
public int nextIndex () {
return index;
}
/**
* Returns the index of the element that would be returned by a
* subsequent call to {@link #previousChar}. (Returns -1 if the list
* iterator is at the beginning of the list.)
*
* @return the index of the element that would be returned by a
* subsequent call to {@code previous}, or -1 if the list
* iterator is at the beginning of the list
*/
public int previousIndex () {
return index - 1;
}
/**
* Removes from the list the last element that was returned by {@link
* #nextChar} or {@link #previousChar} (optional operation). This call can
* only be made once per call to {@code next} or {@code previous}.
* It can be made only if {@link #add} has not been
* called after the last call to {@code next} or {@code previous}.
*
* @throws UnsupportedOperationException if the {@code remove}
* operation is not supported by this list iterator
* @throws IllegalStateException if neither {@code next} nor
* {@code previous} have been called, or {@code remove} or
* {@code add} have been called after the last call to
* {@code next} or {@code previous}
*/
@Override
public void remove () {
if (!valid) {throw new RuntimeException("#iterator() cannot be used nested.");}
if (latest == -1 || latest >= deque.size()) {throw new NoSuchElementException();}
deque.removeAt(latest);
index = latest;
latest = -1;
}
/**
* Replaces the last element returned by {@link #nextChar} or
* {@link #previousChar} with the specified element (optional operation).
* This call can be made only if neither {@link #remove} nor {@link
* #add} have been called after the last call to {@code next} or
* {@code previous}.
*
* @param t the element with which to replace the last element returned by
* {@code next} or {@code previous}
* @throws UnsupportedOperationException if the {@code set} operation
* is not supported by this list iterator
* @throws ClassCastException if the class of the specified element
* prevents it from being added to this list
* @throws IllegalArgumentException if some aspect of the specified
* element prevents it from being added to this list
* @throws IllegalStateException if neither {@code next} nor
* {@code previous} have been called, or {@code remove} or
* {@code add} have been called after the last call to
* {@code next} or {@code previous}
*/
public void set (char t) {
if (!valid) {throw new RuntimeException("#iterator() cannot be used nested.");}
if (latest == -1 || latest >= deque.size()) {throw new NoSuchElementException();}
deque.set(latest, t);
}
/**
* Inserts the specified element into the list (optional operation).
* The element is inserted immediately before the element that
* would be returned by {@link #nextChar}, if any, and after the element
* that would be returned by {@link #previousChar}, if any. (If the
* list contains no elements, the new element becomes the sole element
* on the list.) The new element is inserted before the implicit
* cursor: a subsequent call to {@code next} would be unaffected, and a
* subsequent call to {@code previous} would return the new element.
* (This call increases by one the value that would be returned by a
* call to {@code nextIndex} or {@code previousIndex}.)
*
* @param t the element to insert
* @throws UnsupportedOperationException if the {@code add} method is
* not supported by this list iterator
* @throws ClassCastException if the class of the specified element
* prevents it from being added to this list
* @throws IllegalArgumentException if some aspect of this element
* prevents it from being added to this list
*/
public void add (char t) {
if (!valid) {throw new RuntimeException("#iterator() cannot be used nested.");}
if (index > deque.size()) {throw new NoSuchElementException();}
deque.insert(index, t);
index += direction;
latest = -1;
}
public void reset () {
index = deque.size - 1 & direction >> 31;
latest = -1;
}
public void reset (int index) {
if (index < 0 || index >= deque.size())
throw new IndexOutOfBoundsException("CharDequeIterator does not satisfy index >= 0 && index < deque.size()");
this.index = index;
latest = -1;
}
/**
* Returns an iterator over elements of type {@code char}. Allows this to be used like an {@link Iterable}.
*
* @return this same CharDequeIterator.
*/
public CharDeque.CharDequeIterator iterator () {
return this;
}
}
/**
* Constructs an empty deque.
* This is usually less useful than just using the constructor, but can be handy
* in some code-generation scenarios when you don't know how many arguments you will have.
*
* @return a new deque containing nothing
*/
public static CharDeque with () {
return new CharDeque(0);
}
/**
* Creates a new CharDeque that holds only the given item, but can be resized.
* @param item a char item
* @return a new CharDeque that holds the given item
*/
public static CharDeque with (char item) {
CharDeque deque = new CharDeque(1);
deque.add(item);
return deque;
}
/**
* Creates a new CharDeque that holds only the given items, but can be resized.
* @param item0 a char item
* @param item1 a char item
* @return a new CharDeque that holds the given items
*/
public static CharDeque with (char item0, char item1) {
CharDeque deque = new CharDeque(2);
deque.add(item0);
deque.add(item1);
return deque;
}
/**
* Creates a new CharDeque that holds only the given items, but can be resized.
* @param item0 a char item
* @param item1 a char item
* @param item2 a char item
* @return a new CharDeque that holds the given items
*/
public static CharDeque with (char item0, char item1, char item2) {
CharDeque deque = new CharDeque(3);
deque.add(item0);
deque.add(item1);
deque.add(item2);
return deque;
}
/**
* Creates a new CharDeque that holds only the given items, but can be resized.
* @param item0 a char item
* @param item1 a char item
* @param item2 a char item
* @param item3 a char item
* @return a new CharDeque that holds the given items
*/
public static CharDeque with (char item0, char item1, char item2, char item3) {
CharDeque deque = new CharDeque(4);
deque.add(item0);
deque.add(item1);
deque.add(item2);
deque.add(item3);
return deque;
}
/**
* Creates a new CharDeque that holds only the given items, but can be resized.
* @param item0 a char item
* @param item1 a char item
* @param item2 a char item
* @param item3 a char item
* @param item4 a char item
* @return a new CharDeque that holds the given items
*/
public static CharDeque with (char item0, char item1, char item2, char item3, char item4) {
CharDeque deque = new CharDeque(5);
deque.add(item0);
deque.add(item1);
deque.add(item2);
deque.add(item3);
deque.add(item4);
return deque;
}
/**
* Creates a new CharDeque that holds only the given items, but can be resized.
* @param item0 a char item
* @param item1 a char item
* @param item2 a char item
* @param item3 a char item
* @param item4 a char item
* @param item5 a char item
* @return a new CharDeque that holds the given items
*/
public static CharDeque with (char item0, char item1, char item2, char item3, char item4, char item5) {
CharDeque deque = new CharDeque(6);
deque.add(item0);
deque.add(item1);
deque.add(item2);
deque.add(item3);
deque.add(item4);
deque.add(item5);
return deque;
}
/**
* Creates a new CharDeque that holds only the given items, but can be resized.
* @param item0 a char item
* @param item1 a char item
* @param item2 a char item
* @param item3 a char item
* @param item4 a char item
* @param item5 a char item
* @param item6 a char item
* @return a new CharDeque that holds the given items
*/
public static CharDeque with (char item0, char item1, char item2, char item3, char item4, char item5, char item6) {
CharDeque deque = new CharDeque(7);
deque.add(item0);
deque.add(item1);
deque.add(item2);
deque.add(item3);
deque.add(item4);
deque.add(item5);
deque.add(item6);
return deque;
}
/**
* Creates a new CharDeque that holds only the given items, but can be resized.
* @param item0 a char item
* @param item1 a char item
* @param item2 a char item
* @param item3 a char item
* @param item4 a char item
* @param item5 a char item
* @param item6 a char item
* @return a new CharDeque that holds the given items
*/
public static CharDeque with (char item0, char item1, char item2, char item3, char item4, char item5, char item6, char item7) {
CharDeque deque = new CharDeque(8);
deque.add(item0);
deque.add(item1);
deque.add(item2);
deque.add(item3);
deque.add(item4);
deque.add(item5);
deque.add(item6);
deque.add(item7);
return deque;
}
/**
* Creates a new CharDeque that holds only the given items, but can be resized.
* This overload will only be used when an array is supplied and the type of the
* items requested is the component type of the array, or if varargs are used and
* there are 9 or more arguments.
* @param varargs a char varargs or char array; remember that varargs allocate
* @return a new CharDeque that holds the given items
*/
public static CharDeque with (char... varargs) {
return new CharDeque(varargs);
}
}