blob: b05b62a197ee1bbeee417881c87ada515c791d9f [file] [log] [blame]
package accord.utils;
import java.util.AbstractSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.function.Consumer;
public class DeterministicIdentitySet<T> extends AbstractSet<T>
{
static class Entry<T>
{
final T item;
Entry<T> prev;
Entry<T> next;
Entry(T item)
{
this.item = item;
}
}
// TODO: an identity hash map that doesn't mind concurrent modification / iteration
final IdentityHashMap<T, Entry<T>> lookup = new IdentityHashMap<>();
final Entry<T> head = new Entry<T>(null);
public DeterministicIdentitySet()
{
head.prev = head.next = head;
}
@Override
public Iterator<T> iterator()
{
return new Iterator<T>()
{
Entry<T> next = head.next;
@Override
public boolean hasNext()
{
return next != head;
}
@Override
public T next()
{
if (!hasNext())
throw new NoSuchElementException();
T result = next.item;
next = next.next;
return result;
}
};
}
@Override
public int size()
{
return lookup.size();
}
// we add to the front, and iterate in reverse order, so that we can add and remove while iterating without modifying the set we iterate over
public boolean add(T item)
{
Entry<T> entry = lookup.computeIfAbsent(item, Entry::new);
if (entry.prev != null)
return false;
entry.prev = head;
entry.next = head.next;
head.next = entry;
entry.next.prev = entry;
return true;
}
public boolean remove(Object item)
{
Entry<T> entry = lookup.remove(item);
if (entry == null)
return false;
Entry<T> prev = entry.prev, next = entry.next;
prev.next = next;
next.prev = prev;
return true;
}
public void forEach(Consumer<? super T> consumer)
{
Entry<T> next = head.next;
while (next != head)
{
consumer.accept(next.item);
next = next.next;
}
}
}