使用泛型覆盖双端队列 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
方法不应该有副作用。
currTail
和 currHead
都不会在您的 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
,因为它允许您设置在每个字符串之间使用的分隔符,这正是您正在做的。
我正在尝试使用 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
方法不应该有副作用。
currTail
和 currHead
都不会在您的 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
,因为它允许您设置在每个字符串之间使用的分隔符,这正是您正在做的。