/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.pepsoft.util.jobqueue;
import java.util.*;
/**
* A combination of the List and Set interfaces. A List which can only contain
* equal objects once (according to their <code>equals()</code> and
* <code>hashCode()</code> methods). Or in other words an ordered Set.
*
* <p>This collection's iterator is read-only and does not support the add(),
* etc. methods. It is also <i>not</i> fail-fast! If you modify the collection
* while iterating over it, the results are unspecified.
*
* @author pepijn
*/
public class HashList<E> extends AbstractList<E> implements Set<E> {
public HashList() {
anchor = new Element<>(null);
anchor.previous = anchor;
anchor.next = anchor;
map = new HashMap<>();
}
public HashList(int initialCapacity) {
anchor = new Element<>(null);
anchor.previous = anchor;
anchor.next = anchor;
map = new HashMap<>(initialCapacity);
}
@Override
@SuppressWarnings({"element-type-mismatch", "SuspiciousMethodCalls"})
public boolean contains(Object o) {
return map.containsKey(o);
}
public boolean addToEnd(E e) {
Element<E> element = map.get(e);
if (element != null) {
if (anchor.previous == element) {
// Object already at the end
return false;
} else {
// Object on the list, but not at the end
element.previous.next = element.next;
element.next.previous = element.previous;
element.next = anchor;
element.previous = anchor.previous;
element.previous.next = element;
anchor.previous = element;
return true;
}
} else {
// Object not on the list
element = new Element<>(e);
map.put(e, element);
element.next = anchor;
element.previous = anchor.previous;
element.previous.next = element;
anchor.previous = element;
return true;
}
}
@Override
public boolean add(E e) {
if (map.containsKey(e)) {
return false;
} else {
Element<E> element = new Element<>(e);
map.put(e, element);
element.next = anchor;
element.previous = anchor.previous;
element.previous.next = element;
anchor.previous = element;
return true;
}
}
@Override
@SuppressWarnings({"element-type-mismatch", "SuspiciousMethodCalls"})
public boolean remove(Object o) {
if (map.containsKey(o)) {
Element element = map.remove(o);
element.previous.next = element.next;
element.next.previous = element.previous;
return true;
} else {
return false;
}
}
@Override
public void clear() {
map.clear();
anchor.next = anchor;
anchor.previous = anchor;
}
@Override
public Iterator<E> iterator() {
return listIterator(0);
}
@Override
public int size() {
return map.size();
}
@Override
public E get(int index) {
if ((index < 0) || (index >= map.size())) {
throw new IndexOutOfBoundsException(Integer.toString(index));
}
Element<E> element = anchor;
for (int i = 0; i <= index; i++) {
element = element.next;
}
return element.object;
}
@Override
public E set(int index, E e) {
if ((index < 0) || (index >= map.size())) {
throw new IndexOutOfBoundsException(Integer.toString(index));
}
Element<E> element = anchor;
for (int i = 0; i <= index; i++) {
element = element.next;
}
E previous = element.object;
element.object = e;
map.put(e, element);
return previous;
}
@Override
public void add(int index, E e) {
if ((index < 0) || (index > map.size())) {
throw new IndexOutOfBoundsException(Integer.toString(index));
}
Element<E> element = anchor;
for (int i = 0; i < index; i++) {
element = element.next;
}
Element<E> newElement = new Element<>(e);
newElement.previous = element;
newElement.next = element.next;
element.next.previous = newElement;
element.next = newElement;
if (map.containsKey(e)) {
Element existingElement = map.get(e);
existingElement.previous.next = existingElement.next;
existingElement.next.previous = existingElement.previous;
}
map.put(e, newElement);
}
@Override
public E remove(int index) {
if ((index < 0) || (index >= map.size())) {
throw new IndexOutOfBoundsException(Integer.toString(index));
}
Element<E> element = anchor;
for (int i = 0; i <= index; i++) {
element = element.next;
}
element.previous.next = element.next;
element.next.previous = element.previous;
map.remove(element.object);
return element.object;
}
@Override
@SuppressWarnings({"element-type-mismatch", "SuspiciousMethodCalls"})
public int indexOf(Object o) {
if (! map.containsKey(o)) {
return -1;
} else {
Element element = map.get(o);
int count = 0;
while (element.previous != anchor) {
element = element.previous;
count++;
}
return count;
}
}
@Override
public int lastIndexOf(Object o) {
return indexOf(o);
}
@Override
public ListIterator<E> listIterator(final int initialIndex) {
if ((initialIndex < 0) || (initialIndex >= map.size())) {
throw new IndexOutOfBoundsException(Integer.toString(initialIndex));
}
Element<E> element = anchor;
for (int i = 0; i <= initialIndex; i++) {
element = element.next;
}
final Element<E> startElement = element;
return new ListIterator<E>() {
@Override
public boolean hasNext() {
return element.next != anchor;
}
@Override
public E next() {
if (element.next == anchor) {
throw new NoSuchElementException();
}
element = element.next;
index++;
return element.object;
}
@Override
public boolean hasPrevious() {
return element != anchor;
}
@Override
public E previous() {
if (element == anchor) {
throw new NoSuchElementException();
}
E obj = element.object;
element = element.previous;
index--;
return obj;
}
@Override
public int nextIndex() {
return index + 1;
}
@Override
public int previousIndex() {
return index;
}
@Override
public void remove() {
HashList.this.remove(element.object);
}
@Override
public void set(E e) {
throw new UnsupportedOperationException("Not supported");
}
@Override
public void add(E e) {
throw new UnsupportedOperationException("Not supported");
}
private Element<E> element = startElement;
private int index = initialIndex - 1;
};
}
@Override
public Spliterator<E> spliterator() {
return Spliterators.spliterator(this, Spliterator.ORDERED | Spliterator.DISTINCT);
}
private final Map<E, Element<E>> map;
private final Element<E> anchor;
static class Element<T> {
Element(T object) {
this.object = object;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Element other = (Element) obj;
if (this.object != other.object && (this.object == null || !this.object.equals(other.object))) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 7;
hash = 47 * hash + (this.object != null ? this.object.hashCode() : 0);
return hash;
}
T object;
Element<T> previous, next;
}
}