diff --git a/platform/util/src/com/intellij/util/containers/HashSetQueue.java b/platform/util/src/com/intellij/util/containers/HashSetQueue.java
index 5092f1005cd0..dd60695421e2 100644
--- a/platform/util/src/com/intellij/util/containers/HashSetQueue.java
+++ b/platform/util/src/com/intellij/util/containers/HashSetQueue.java
@@ -23,17 +23,24 @@ import java.util.NoSuchElementException;
import java.util.Queue;
/**
- * Unbounded non-thread-safe {@link Queue} implementation backed by {@link gnu.trove.THashSet}.
+ * Unbounded non-thread-safe {@link Queue} with fast add/remove/contains.
* Differs from the conventional Queue by:
* - The new method {@link #find(T)} which finds the queue element equivalent to its parameter in O(1) avg. time
* - The {@link #contains(Object)} method is O(1)
* - The {@link #remove(Object)} method is O(1)
*
+ * Implementation is backed by {@link gnu.trove.THashSet} containing double-linked QueueEntry nodes holding elements themselves.
*/
public class HashSetQueue extends AbstractCollection implements Queue {
private final OpenTHashSet> set = new OpenTHashSet>();
- private QueueEntry last;
- private QueueEntry first;
+ // 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 TOMB = new QueueEntry(cast(new Object()));
+
+ public HashSetQueue() {
+ TOMB.next = TOMB.prev = TOMB;
+ }
private static class QueueEntry {
@NotNull private final T t;
@@ -65,15 +72,12 @@ public class HashSetQueue extends AbstractCollection implements Queue {
QueueEntry newLast = new QueueEntry(t);
boolean added = set.add(newLast);
if (!added) return false;
- if (last == null) {
- last = newLast;
- first = newLast;
- }
- else {
- last.next = newLast;
- newLast.prev = last;
- last = newLast;
- }
+ QueueEntry oldLast = TOMB.prev;
+
+ oldLast.next = newLast;
+ newLast.prev = oldLast;
+ newLast.next = TOMB;
+ TOMB.prev = newLast;
return true;
}
@@ -105,7 +109,7 @@ public class HashSetQueue extends AbstractCollection implements Queue {
@Override
public T peek() {
- return first == null ? null : first.t;
+ return TOMB.next == TOMB ? null : TOMB.next.t;
}
public T find(@NotNull T t) {
@@ -124,18 +128,10 @@ public class HashSetQueue extends AbstractCollection implements Queue {
if (entry == null) return false;
QueueEntry prev = entry.prev;
QueueEntry next = entry.next;
- if (prev != null) {
- prev.next = next;
- }
- else {
- first = next;
- }
- if (next != null) {
- next.prev = prev;
- }
- else {
- last = prev;
- }
+
+ prev.next = next;
+ next.prev = prev;
+
set.remove(entry);
return true;
}
@@ -159,24 +155,22 @@ public class HashSetQueue extends AbstractCollection implements Queue {
@Override
public Iterator iterator() {
return new Iterator() {
- QueueEntry cursor = first;
+ private QueueEntry cursor = TOMB;
@Override
public boolean hasNext() {
- return cursor != null;
+ return cursor.next != TOMB;
}
@Override
public T next() {
- QueueEntry entry = cursor;
cursor = cursor.next;
- return entry.t;
+ return cursor.t;
}
@Override
public void remove() {
- QueueEntry toDelete = cursor == null ? last : cursor.prev;
- if (toDelete == null) throw new NoSuchElementException();
- HashSetQueue.this.remove(toDelete.t);
+ if (cursor == TOMB) throw new NoSuchElementException();
+ HashSetQueue.this.remove(cursor.t);
}
};
}
diff --git a/platform/util/testSrc/com/intellij/util/containers/HashSetQueueTest.java b/platform/util/testSrc/com/intellij/util/containers/HashSetQueueTest.java
index a741d219ff54..3997e2389bc1 100644
--- a/platform/util/testSrc/com/intellij/util/containers/HashSetQueueTest.java
+++ b/platform/util/testSrc/com/intellij/util/containers/HashSetQueueTest.java
@@ -21,6 +21,7 @@ import gnu.trove.PrimeFinder;
import junit.framework.TestCase;
import java.util.ArrayList;
+import java.util.Iterator;
public class HashSetQueueTest extends TestCase {
private final Assertion CHECK = new Assertion();
@@ -114,4 +115,17 @@ public class HashSetQueueTest extends TestCase {
toRemove = (toRemove + delta) % N;
}
}
+
+ public void testIteratorCatchesUpQueueModificationImmediately() {
+ assertTrue(myQueue.add("1"));
+ Iterator iterator = myQueue.iterator();
+ assertTrue(iterator.hasNext());
+ assertEquals("1", iterator.next());
+ assertFalse(iterator.hasNext());
+
+ myQueue.add("2");
+ assertTrue(iterator.hasNext());
+ assertEquals("2", iterator.next());
+ assertFalse(iterator.hasNext());
+ }
}