使用泛型覆盖双端队列 class 中的 toString 方法

Ovewriting toString method in deque class using generics

我正在尝试使用 toString 方法打印双端队列中的第一个和最后一个元素,但是我不完全确定我是否正确覆盖了 toString 方法。

据我所知,这些方法似乎都运行正常,但我无法判断,因为我看不到任何可读的输出。

我知道已经有一个双端队列接口,但这是 Java 中使用泛型的练习的一部分。

这段代码应该创建一个双端队列,能够向双端队列的前面添加值,从前面删除值,向后面添加值并从后面删除值。

这是有问题的 class:

import java.util.Iterator;
import java.util.NoSuchElementException;

class Deque<T> implements Iterable<T> {
    private class Node<T> {
        public Node<T> left, right;
        private final T item;

        public Node(T item) {
            if (item == null) {
                throw new NullPointerException();
            }
            this.item = item;
        }

        public void connectRight(Node<T> other) {
            this.right = other;
            other.left = this;
        }
    }

    private class DequeIterator implements Iterator<T> {

        private Node<T> curr = head;

        public boolean hasNext() {
            return curr != null;
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }

        public T next() {
            if (!hasNext()) {
                throw new NoSuchElementException();
            }
            T item = curr.item;
            curr = curr.right;
            return item;
        }
    }

    private Node<T> head, tail;
    private int size;

    public Iterator<T> iterator() {
        return new DequeIterator();
    }

    public Deque() {
    }

    public int size() {
        return size;
    }

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

    public void checkInvariants() {
        assert size >= 0;
        assert size > 0 || (head == null && tail == null);
        assert (head == null && tail == null) || (head != null && tail != null);
    }

    public void addFirst(T item) {
        Node<T> prevHead = head;
        Node<T> newHead = new Node<T>(item);
        if (prevHead != null) {
            newHead.connectRight(prevHead);
        } else {
            tail = newHead;
        }
        head = newHead;
        size++;
        checkInvariants();
    }

    public void addLast(T item) {
        Node<T> newTail = new Node<T>(item);
        Node<T> prevTail = tail;
        if (prevTail != null) {
            prevTail.connectRight(newTail);
        } else {
            head = newTail;
        }
        tail = newTail;
        size++;
        checkInvariants();
    }

    public T removeFirst() {
        if (isEmpty()) {
            throw new java.util.NoSuchElementException();
        }
        size--;
        Node<T> prevHead = head;
        head = prevHead.right;
        prevHead.right = null;
        if (head != null) {
            head.left = null;
        }
        checkInvariants();
        return prevHead.item;
    }

    public T removeLast() {
        if (isEmpty()) {
            throw new java.util.NoSuchElementException();
        }
        size--;
        Node<T> prevTail = tail;
        tail = prevTail.left;
        prevTail.left = null;
        if (tail != null) tail.right = null;
        checkInvariants();
        return prevTail.item;
    }

    @Override
    public String toString() {
        Node<T> currTail = tail;
        Node<T> currHead = head;
        head = currHead.right;
        tail = currTail.left;

        StringBuilder builder = new StringBuilder();

        while (currHead != null && currTail != null) {
            builder.append(currHead.item + "\n");
        }

        return builder.toString();

    }

    public static void main(String[] args) {
        Deque<Double> d = new Deque<Double>();
        d.addFirst(1.0);
        System.out.println(d);
        d.addLast(1.0);
        //d.removeFirst();
        //d.removeLast();

        System.out.println(d.toString());

    }
}

首先,您将实例变量 head 和 tail 设置为它们各自的邻居,这绝对不是您要做的。这使您的 queue 处于不一致状态,其中第二个元素是头部,但它仍然有一个左邻居,即原始头部。尾巴也一样。通常 toString 方法不应该有副作用。

currTailcurrHead 都不会在您的 while-loop 中发生变化,因此如果双端队列为 non-empty,您的条件 currHead != null && currTail != null 将始终为真。您必须在循环中设置这些变量,但是,您不需要同时从两端进行迭代。从头开始迭代就足够了。然后,您可以使用 for 循环,如下所示:

@Override
public String toString() {
    final StringJoiner stringJoiner = new StringJoiner("\n");

    for (Node<T> node = head; node != null; node = node.right) {
        stringJoiner.add(node.item.toString());
    }

    return stringJoiner.toString();
}

这会在每次迭代后将变量 node 设置为它的右邻居,如果双端队列为空,node 将从 get-go 为空并且循环不会按预期输入。

这只是更简洁(在我看来)的版本:

@Override
public String toString() {
    final StringJoiner stringJoiner = new StringJoiner("\n");

    Node<?> node = head;

    while (node != null) {
        stringJoiner.add(node.item.toString());
        node = node.right;
    }

    return stringJoiner.toString();
}

这基本上是您的尝试,刚刚修复。

并不是说我使用了 StringJoiner 而不是 StringBuilder,因为它允许您设置在每个字符串之间使用的分隔符,这正是您正在做的。