/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* 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.intellij.util.containers;
import org.jetbrains.annotations.NotNull;
import java.util.AbstractCollection;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Queue;
/**
* Unbounded non-thread-safe {@link Queue} with fast add/remove/contains.<br>
* Differs from the conventional Queue by:<ul>
* <li>The new method {@link #find(T)} which finds the queue element equivalent to its parameter in O(1) avg. time</li>
* <li>The {@link #contains(Object)} method is O(1)</li>
* <li>The {@link #remove(Object)} method is O(1)</li>
* </ul>
* Implementation is backed by {@link gnu.trove.THashSet} containing double-linked QueueEntry nodes holding elements themselves.
*/
public class HashSetQueue<T> extends AbstractCollection<T> implements Queue<T> {
private final OpenTHashSet<QueueEntry<T>> set = new OpenTHashSet<QueueEntry<T>>();
// Entries in the queue are double-linked circularly, the TOMB serving as a sentinel.
// TOMB.next is the first entry; TOMB.prev is the last entry;
// TOMB.next == TOMB.prev == TOMB means the queue is empty
private final QueueEntry<T> TOMB = new QueueEntry<T>(cast(new Object()));
public HashSetQueue() {
TOMB.next = TOMB.prev = TOMB;
}
private static class QueueEntry<T> {
@NotNull private final T t;
private QueueEntry<T> next;
private QueueEntry<T> prev;
public QueueEntry(@NotNull T t) {
this.t = t;
}
@Override
public int hashCode() {
return t.hashCode();
}
@Override
public boolean equals(Object obj) {
return obj instanceof QueueEntry && t.equals(((QueueEntry)obj).t);
}
}
@Override
public boolean offer(@NotNull T t) {
return add(t);
}
@Override
public boolean add(@NotNull T t) {
QueueEntry<T> newLast = new QueueEntry<T>(t);
boolean added = set.add(newLast);
if (!added) return false;
QueueEntry<T> oldLast = TOMB.prev;
oldLast.next = newLast;
newLast.prev = oldLast;
newLast.next = TOMB;
TOMB.prev = newLast;
return true;
}
@Override
@NotNull
public T remove() {
T poll = poll();
if (poll == null) throw new NoSuchElementException();
return poll;
}
@Override
public T poll() {
T peek = peek();
if (peek != null) {
remove(peek);
}
return peek;
}
@Override
@NotNull
public T element() {
T peek = peek();
if (peek == null) throw new NoSuchElementException();
return peek;
}
@Override
public T peek() {
return TOMB.next == TOMB ? null : TOMB.next.t;
}
public T find(@NotNull T t) {
QueueEntry<T> existing = findEntry(t);
return existing == null ? null : existing.t;
}
private QueueEntry<T> findEntry(@NotNull T t) {
return set.get(new QueueEntry<T>(t));
}
@Override
public boolean remove(Object o) {
T t = cast(o);
QueueEntry<T> entry = findEntry(t);
if (entry == null) return false;
QueueEntry<T> prev = entry.prev;
QueueEntry<T> next = entry.next;
prev.next = next;
next.prev = prev;
set.remove(entry);
return true;
}
@Override
public int size() {
return set.size();
}
@Override
public boolean contains(Object o) {
return find(cast(o)) != null;
}
private T cast(Object o) {
//noinspection unchecked
return (T)o;
}
@NotNull
@Override
public PositionalIterator<T> iterator() {
return new PositionalIterator<T>() {
private QueueEntry<T> cursor = TOMB;
private long count;
@Override
public boolean hasNext() {
return cursor.next != TOMB;
}
@Override
public T next() {
cursor = cursor.next;
count++;
return cursor.t;
}
@Override
public void remove() {
if (cursor == TOMB) throw new NoSuchElementException();
HashSetQueue.this.remove(cursor.t);
}
@NotNull
@Override
public IteratorPosition<T> position() {
return new MyIteratorPosition<T>(cursor, count, TOMB);
}
};
}
private static class MyIteratorPosition<T> implements PositionalIterator.IteratorPosition<T> {
private final QueueEntry<T> cursor;
private final long count;
private final QueueEntry<T> TOMB;
private MyIteratorPosition(@NotNull QueueEntry<T> cursor, long count, QueueEntry<T> TOMB) {
this.cursor = cursor;
this.count = count;
this.TOMB = TOMB;
}
@Override
public T peek() {
if (cursor == TOMB) {
throw new IllegalStateException("Iterator is before the first element. Must call .next() first.");
}
return cursor.t;
}
@Override
public PositionalIterator.IteratorPosition<T> next() {
return cursor.next == TOMB ? null : new MyIteratorPosition<T>(cursor.next, count + 1, TOMB);
}
@Override
public int compareTo(@NotNull PositionalIterator.IteratorPosition<T> o) {
return compare(count, ((MyIteratorPosition)o).count);
}
private static int compare(long x, long y) {
return x < y ? -1 : x == y ? 0 : 1;
}
}
public interface PositionalIterator<T> extends Iterator<T> {
/**
* @return the current position of this iterator.
* The position of the newly created iterator is before the first element of the queue (so the {@link IteratorPosition#peek()} value is undefined)
*/
@NotNull
IteratorPosition<T> position();
interface IteratorPosition<T> extends Comparable<IteratorPosition<T>> {
T peek();
IteratorPosition<T> next();
}
}
}