/*
Copyright (c) 2012 LinkedIn Corp.
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.
*/
/**
* $Id: $
*/
package com.linkedin.r2.util;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* This class provides the ability to remove an arbitrary interior element (neither head nor tail)
* from the queue in O(1) time, by invoking {@link #removeNode(com.linkedin.r2.util.LinkedDeque.Node)}.
* No class in the Java Collections framework provides this ability.
*
* Adding to and removing from head or tail also run in O(1) time, as expected.
*
* External synchronization is required!
*
* @author Steven Ihde
* @version $Revision: $
*/
public class LinkedDeque<T> extends AbstractDeque<T>
{
public static class Node<T>
{
private final T _value;
private Node<T> _next;
private Node<T> _prev;
private Node(T value)
{
_value = value;
}
}
private Node<T> _head;
private Node<T> _tail;
private int _size;
/**
* Construct a new instance.
*/
public LinkedDeque()
{
super();
}
/**
* Construct a new instance, adding all objects in the specified collection.
*
* @param collection a {@link Collection} of objects to be added.
*/
public LinkedDeque(Collection<? extends T> collection)
{
super();
addAll(collection);
}
/**
* Add a new item at the head of the queue.
*
* @param item the item to be added.
* @return the {@link Node} of the newly added item.
*/
public Node<T> addFirstNode(T item)
{
return addBeforeNode(_head, item);
}
/**
* Add a new item at the tail of the queue.
*
* @param item the item to be added.
* @return the {@link Node} of the newly added item.
*/
public Node<T> addLastNode(T item)
{
return addBeforeNode(null, item);
}
/**
* Add a new item before the specified Node.
*
* @param before the {@link Node} before which the item should be added.
* @param item the item to be added.
* @return the {@link Node} of the newly added item.
*/
public Node<T> addBeforeNode(Node<T> before, T item)
{
if (item == null)
{
throw new NullPointerException();
}
if (before != null && before != _head && before._next == null && before._prev == null)
{
throw new IllegalStateException("node was already removed");
}
Node<T> node = new Node<T>(item);
if (before == null)
{
// Adding to tail
node._next = null;
node._prev = _tail;
if (_tail != null)
{
_tail._next = node;
}
_tail = node;
if (_head == null)
{
_head = node;
}
}
else
{
node._next = before;
node._prev = before._prev;
before._prev = node;
if (before == _head)
{
_head = node;
}
}
_size++;
return node;
}
/**
* Remove the specified Node from the queue.
*
* @param node the Node to be removed.
* @return the item contained in the Node which was removed.
*/
public T removeNode(Node<T> node)
{
// TODO what if the node is from the wrong list??
if (node != _head && node._next == null && node._prev == null)
{
// the node has already been removed
return null;
}
if (node == _head)
{
_head = node._next;
}
if (node._prev != null)
{
node._prev._next = node._next;
}
if (node == _tail)
{
_tail = node._prev;
}
if (node._next != null)
{
node._next._prev = node._prev;
}
node._next = null;
node._prev = null;
_size--;
return node._value;
}
// Remaining methods implement Deque<T>
@Override
public boolean offerFirst(T t)
{
addFirstNode(t);
return true;
}
@Override
public boolean offerLast(T t)
{
addLastNode(t);
return true;
}
@Override
public T peekFirst()
{
if (_head == null)
{
return null;
}
return _head._value;
}
@Override
public T peekLast()
{
if (_tail == null)
{
return null;
}
return _tail._value;
}
@Override
public T pollFirst()
{
if (_head == null)
{
return null;
}
return removeNode(_head);
}
@Override
public T pollLast()
{
if (_tail == null)
{
return null;
}
return removeNode(_tail);
}
@Override
public int size()
{
return _size;
}
@Override
public Iterator<T> iterator()
{
return new LinkedQueueIterator(Direction.ASCENDING);
}
@Override
public Iterator<T> descendingIterator()
{
return new LinkedQueueIterator(Direction.DESCENDING);
}
private enum Direction { ASCENDING, DESCENDING }
private class LinkedQueueIterator implements Iterator<T>
{
private final Direction _dir;
private Node<T> _index;
private Node<T> _last;
private LinkedQueueIterator(Direction dir)
{
_dir = dir;
_index = (_dir == Direction.ASCENDING ? _head : _tail);
}
@Override
public boolean hasNext()
{
return _index != null;
}
@Override
public T next()
{
if (_index == null)
{
throw new NoSuchElementException();
}
_last = _index;
T value = _index._value;
_index = (_dir == Direction.ASCENDING ? _index._next : _index._prev);
return value;
}
@Override
public void remove()
{
if (_last == null)
{
throw new IllegalStateException();
}
removeNode(_last);
_last = null;
}
}
}