Deque Implementation in Java Please review my Deque implementation in Java and suggest ways of improving this.

    public class DequeImplementation&lt;T&gt; {
    private QueueNode&lt;T&gt; first;
    private QueueNode&lt;T&gt; last;
    private int size;

    private static class QueueNode&lt;T&gt; {
        private T val;
        private QueueNode&lt;T&gt; next;
        private QueueNode&lt;T&gt; prev;

        public QueueNode(T val) {
            this.val = val;
            this.next = null;
            this.prev = null;
        }

        public T getValue() {
            return val;
        }

        public void setNext(QueueNode&lt;T&gt; nextNode) {
            next = nextNode;
        }

        public QueueNode&lt;T&gt; getNext() {
            return next;
        }

        public void setPrev(QueueNode&lt;T&gt; prevNode) {
            prev = prevNode;
        }

        public QueueNode&lt;T&gt; getPrev() {
            return prev;
        }

        public String toString() {
            if(val == null) {
                return "null";
            }
            return val.toString();
        }
    }

    public int size() {
        return size;
    }

    public boolean isEmpty() {
        return size == 0;
    }

    public void offerLast(T val) {
        QueueNode&lt;T&gt; cur = new QueueNode(val);
        if(size == 0) {
            cur.setNext(first);
            first = cur;
            last = cur;
            size++;
            return;
        }

        last.setNext(cur);
        cur.setPrev(last);
        last = cur;
        size++;
    }

    public void offerFirst(T val) {
        QueueNode&lt;T&gt; cur = new QueueNode(val);
        if(size == 0) {
            cur.setNext(first);
            first = cur;
            last = cur;
            size++;
            return;
        }

        cur.setNext(first);
        first.setPrev(cur);
        first = cur;
        size++;
    }

    public T peekLast() {
        if(isEmpty()) {
            return null;
        }
        return last.getValue();
    }

    public T peekFirst() {
        if(isEmpty()) {
            return null;
        }
        return first.getValue();
    }

    public QueueNode&lt;T&gt; pollFirst() {
        if(isEmpty()) {
            return null;
        }

        QueueNode&lt;T&gt; cur = first;
        first = first.getNext();
        if(first != null) {
            first.setPrev(null);
        }
        size--;
        return cur;
    }

    public QueueNode&lt;T&gt; pollLast() {
        if(isEmpty()) {
            return null;
        }

        QueueNode&lt;T&gt; cur = last;
        last = last.getPrev();
        if(last != null) {
            last.setNext(null);
        }
        size--;
        return cur;
    }

    public void clear() {
        size = 0;
        first = null;
        last = null;
    }

    @Override
    public String toString() {
        if(size == 0) {
            return "[]";
        }

        StringBuilder sb = new StringBuilder("null&lt;-");
        QueueNode&lt;T&gt; cur = first;
        sb.append(cur.getValue() + "&lt;-");

        while(cur.getNext() != null &amp;&amp; !cur.equals(last)) {
            sb.append(cur.getNext().getValue() + "&lt;-");
            cur = cur.getNext();
        }

        sb.append("null");
        return sb.toString();
    }

    public static void main(String[] args) {
        DequeImplementation&lt;Integer&gt; dq = new DequeImplementation&lt;&gt;();
        System.out.println(dq.size());
        System.out.println(dq.pollLast()); //returns null
        System.out.println(dq);
        dq.offerLast(100);
        dq.offerFirst(10);
        dq.offerFirst(1);
        System.out.println(dq.pollFirst());
        System.out.println(dq);
        System.out.println(dq.size());
        System.out.println(dq.peekLast());
        System.out.println(dq.pollLast());
        System.out.println(dq);
        System.out.println(dq.size());
        System.out.println(dq.pollFirst());
        System.out.println(dq.pollLast()); //returns null
        dq.clear();
        System.out.println(dq.size());
    }   
}
I have embedded my comments straight in the code:

import java.util.Objects;

// 'Implementation' does not provide any more information on this class.
// Remove it, so less typing.
//public class DequeImplementation&lt;T&gt; {
public class Deque&lt;T&gt; {

    private QueueNode&lt;T&gt; first;
    private QueueNode&lt;T&gt; last;
    private int size;

    // You can add 'final' if you WANT:
    private static final class QueueNode&lt;T&gt; {

        private T val;
        private QueueNode&lt;T&gt; next;
        private QueueNode&lt;T&gt; prev;

        // You don't really need 'public' since the class is the inner private
        // static class:
        /*public*/ QueueNode(T val) {
            this.val = val;
            // Reference fields are 'null' by default:
            //this.next = null;
            //this.prev = null;
        }

        // Remove 'public' as well:
        /*public*/ T getValue() {
            return val;
        }

        /*public*/ void setNext(QueueNode&lt;T&gt; nextNode) {
            next = nextNode;
        }

        /*public*/ QueueNode&lt;T&gt; getNext() {
            return next;
        }

        /*public*/ void setPrev(QueueNode&lt;T&gt; prevNode) {
            prev = prevNode;
        }

        /*public*/ QueueNode&lt;T&gt; getPrev() {
            return prev;
        }

        // Here we need 'public'.
        public String toString() {
            // if (val == null) {
            //    return "null";
            //}
            //return val.toString();
            // Does exactly the same as above 3 lines:
            return Objects.toString(val);
        }
    }

    public int size() {
        return size;
    }

    public boolean isEmpty() {
        return size == 0;
    }

    public void offerLast(T val) {
        QueueNode&lt;T&gt; cur = new QueueNode(val);
        if (size == 0) {
            first = last = cur;
            size = 1;
            return;
        }

        last.setNext(cur);
        cur.setPrev(last);
        last = cur;
        size++;
    }

    public void offerFirst(T val) {
        QueueNode&lt;T&gt; cur = new QueueNode(val);
        if (size == 0) {
            first = last = cur;
            size = 1;
            return;
        }

        cur.setNext(first);
        first.setPrev(cur);
        first = cur;
        size++;
    }

    public T peekLast() {
        if (isEmpty()) {
            return null;
        }
        return last.getValue();
    }

    public T peekFirst() {
        if (isEmpty()) {
            return null;
        }
        return first.getValue();
    }

    // No reason to return a node since everything the user can do with it is
    // to ask its value; return the value:
    // public QueueNode&lt;T&gt; pollFirst() {
    public T pollFirst() {
        if (isEmpty()) {
            return null;
        }

        QueueNode&lt;T&gt; cur = first;
        first = first.getNext();
        if (first != null) {
            first.setPrev(null);
        }
        size--;
        return cur.getValue();
    }

    // Same argument as in 'pollFirst':
    public T pollLast() {
        if (isEmpty()) {
            return null;
        }

        QueueNode&lt;T&gt; cur = last;
        last = last.getPrev();
        if (last != null) {
            last.setNext(null);
        }
        size--;
        return cur.getValue();
    }

    public void clear() {
        size = 0;
        first = null;
        last = null;
    }

    // This is a matter of taste, yet I don't quite like your idea to prepend
    // and append the output with 'null'. Think what the user sees if he really
    // has null values in the deque..a
    @Override
    public String toString() {
        if (size == 0) {
            return "[]";
        }

        StringBuilder sb = new StringBuilder("[");
        sb.append(first.getValue());
        QueueNode&lt;T&gt; node = first.getNext();

        while (node != null) {
            sb.append(" -&gt; ").append(node.getValue());
            node = node.getNext();
        }

        return sb.append("]").toString();
    }

    public static void main(String[] args) {
        Deque&lt;Integer&gt; dq = new Deque&lt;&gt;();
        System.out.println(dq.size());
        System.out.println(dq.pollLast()); //returns null
        System.out.println(dq);
        dq.offerLast(100);
        dq.offerFirst(10);
        dq.offerFirst(1);
        System.out.println(dq.pollFirst());
        System.out.println(dq);
        System.out.println(dq.size());
        System.out.println(dq.peekLast());
        System.out.println(dq.pollLast());
        System.out.println(dq);
        System.out.println(dq.size());
        System.out.println(dq.pollFirst());
        System.out.println(dq.pollLast()); //returns null
        dq.clear();
        System.out.println(dq.size());
    }
}


Hope that helps.