package cofh.lib.util; import com.google.common.base.Objects; import com.google.common.primitives.Ints; import java.util.AbstractCollection; import java.util.Collection; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.ListIterator; import java.util.NoSuchElementException; @SuppressWarnings("unchecked") public class LinkedHashList<E extends Object> extends AbstractCollection<E> implements Cloneable { // TODO: implements List<E>, java.io.Serializable protected static final class Entry { protected Entry next; protected Entry prev; protected final Object key; protected final int hash; protected Entry nextInBucket; protected Entry(Object key, int keyHash) { this.key = key; this.hash = keyHash; } } protected static int hash(Object n) { int h = n == null ? 0 : n.hashCode(); h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); } private static int roundUpToPowerOf2(int number) { return number >= Ints.MAX_POWER_OF_TWO ? Ints.MAX_POWER_OF_TWO : (number > 2) ? Integer.highestOneBit((number - 1) << 1) : 2; } protected transient Entry head; protected transient Entry tail; protected transient int size; protected transient int mask; protected transient Entry[] hashTable; protected transient int modCount; public LinkedHashList() { hashTable = new Entry[8]; mask = 7; } public LinkedHashList(int size) { size = roundUpToPowerOf2(size); hashTable = new Entry[size]; mask = size - 1; } public LinkedHashList(Collection<E> col) { int size = roundUpToPowerOf2(col.size()); hashTable = new Entry[size]; mask = size - 1; addAll(col); } @Override public int size() { return size; } @Override public boolean add(E e) { return push(e); } public E get(int index) { checkElementIndex(index); return (E) index(index).key; } public boolean push(E obj) { int hash = hash(obj); if (seek(obj, hash) != null) return false; Entry e; ++modCount; insert(e = new Entry(obj, hash)); rehashIfNecessary(); e.prev = tail; e.next = null; if (tail != null) tail.next = e; else head = e; tail = e; return true; } public E pop() { Entry e = tail; if (e != null) { ++modCount; delete(e); tail = e.prev; e.prev = null; if (tail != null) tail.next = null; else head = null; return (E) e.key; } return null; } public E peek() { return tail != null ? (E) tail.key : null; } public E poke() { return head != null ? (E) head.key : null; } public boolean unshift(E obj) { int hash = hash(obj); if (seek(obj, hash) != null) return false; Entry e; ++modCount; insert(e = new Entry(obj, hash)); rehashIfNecessary(); e.next = head; e.prev = null; if (head != null) head.prev = e; else tail = e; head = e; return true; } public E shift() { Entry e = head; if (e != null) { ++modCount; delete(e); head = e.next; e.next = null; if (head != null) head.prev = null; else tail = null; return (E) e.key; } return null; } @Override public boolean contains(Object obj) { return seek(obj, hash(obj)) != null; } @Override public boolean remove(Object obj) { Entry e = seek(obj, hash(obj)); if (e == null) return false; unlink(e); return true; } protected Entry index(int index) { Entry x; if (index < (size >> 1)) { x = head; for (int i = index; i --> 0; ) x = x.next; } else { x = tail; for (int i = size - 1; i --> index; ) x = x.prev; } return x; } protected Entry seek(Object obj, int hash) { for (Entry entry = hashTable[hash & mask]; entry != null; entry = entry.nextInBucket) if (hash == entry.hash && Objects.equal(obj, entry.key)) return entry; return null; } protected void insert(Entry entry) { int bucket = entry.hash & mask; entry.nextInBucket = hashTable[bucket]; hashTable[bucket] = entry; ++size; } protected boolean linkBefore(E obj, Entry succ) { int hash = hash(obj); if (seek(obj, hash) != null) return false; final Entry pred = succ.prev; final Entry newNode = new Entry(obj, hash); modCount++; insert(newNode); rehashIfNecessary(); newNode.next = succ; newNode.prev = pred; succ.prev = newNode; if (pred == null) head = newNode; else pred.next = newNode; return true; } protected void delete(Entry entry) { l: synchronized (hashTable) { int bucket = entry.hash & mask; Entry prev = null, cur = hashTable[bucket]; if (cur == entry) { hashTable[bucket] = cur.nextInBucket; break l; } for (; true; cur = cur.nextInBucket) { if (cur == entry) { prev.nextInBucket = entry.nextInBucket; break l; } prev = cur; } } --size; } protected E unlink(Entry x) { final E element = (E) x.key; final Entry next = x.next; final Entry prev = x.prev; if (prev == null) { head = next; } else { prev.next = next; x.prev = null; } if (next == null) { tail = prev; } else { next.prev = prev; x.next = null; } delete(x); modCount++; return element; } protected void rehashIfNecessary() { Entry[] old = hashTable, newTable; if (size > old.length * 2 && old.length < Ints.MAX_POWER_OF_TWO) synchronized (hashTable) { int newTableSize = old.length * 2, newMask = newTableSize - 1; newTable = hashTable = new Entry[newTableSize]; mask = newMask; for (int bucket = old.length; bucket --> 0 ; ) { Entry entry = old[bucket]; while (entry != null) { Entry nextEntry = entry.nextInBucket; int keyBucket = entry.hash & newMask; entry.nextInBucket = newTable[keyBucket]; newTable[keyBucket] = entry; entry = nextEntry; } } } } @Override public LinkedHashList<E> clone() { return new LinkedHashList<E>(this); } @Override public Iterator<E> iterator() { return listIterator(); } public ListIterator<E> listIterator() { return listIterator(0); } public ListIterator<E> listIterator(int index) { checkPositionIndex(index); return new ListItr(index); } public Iterator<E> descendingIterator() { return new DescendingIterator(); } protected boolean isElementIndex(int index) { return index >= 0 && index < size; } protected boolean isPositionIndex(int index) { return index >= 0 && index <= size; } protected String outOfBoundsMsg(int index) { return "Index: "+index+", Size: "+size; } protected void checkElementIndex(int index) { if (!isElementIndex(index)) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } protected void checkPositionIndex(int index) { if (!isPositionIndex(index)) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } protected class ListItr implements ListIterator<E> { protected Entry lastReturned = null; protected Entry next; protected int nextIndex; protected int expectedModCount = modCount; protected ListItr(int index) { next = (index == size) ? null : index(index); nextIndex = index; } @Override public boolean hasNext() { return nextIndex < size; } @Override public E next() { checkForComodification(); if (!hasNext()) throw new NoSuchElementException(); lastReturned = next; next = next.next; nextIndex++; return (E) lastReturned.key; } @Override public boolean hasPrevious() { return nextIndex > 0; } @Override public E previous() { checkForComodification(); if (!hasPrevious()) throw new NoSuchElementException(); lastReturned = next = (next == null) ? tail : next.prev; nextIndex--; return (E) lastReturned.key; } @Override public int nextIndex() { return nextIndex; } @Override public int previousIndex() { return nextIndex - 1; } @Override public void remove() { checkForComodification(); if (lastReturned == null) throw new IllegalStateException(); Entry lastNext = lastReturned.next; unlink(lastReturned); if (next == lastReturned) next = lastNext; else nextIndex--; lastReturned = null; expectedModCount++; } @Override public void set(E e) { checkForComodification(); if (lastReturned == null) throw new IllegalStateException(); linkBefore(e, lastReturned); unlink(lastReturned); lastReturned = (next == null) ? tail : next.prev; expectedModCount += 2; } @Override public void add(E e) { checkForComodification(); lastReturned = null; if (next == null) push(e); else linkBefore(e, next); nextIndex++; expectedModCount++; } protected final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } } protected class DescendingIterator implements Iterator<E> { protected final ListItr itr = new ListItr(size()); @Override public boolean hasNext() { return itr.hasPrevious(); } @Override public E next() { return itr.previous(); } @Override public void remove() { itr.remove(); } } }