背景:菜鸟一枚,想补补基础。最近在看算法,1.3.26有一道题是:编写一个方法remove()接受一条链表和一个字符串key作为参数,删除链表中所有的item域为key的节点。
上代码:
public class Stack<Item> implements Iterable<Item> {
private Node<Item> first; // top of stack
private int n; // size of the stack
// helper linked list class
private static class Node<Item> {
private Item item;
private Node<Item> next;
}
/**
* Initializes an empty stack.
*/
public Stack() {
first = null;
n = 0;
}
/**
* Returns true if this stack is empty.
*
* @return true if this stack is empty; false otherwise
*/
public boolean isEmpty() {
return first == null;
}
/**
* Returns the number of items in this stack.
*
* @return the number of items in this stack
*/
public int size() {
return n;
}
/**
* Adds the item to this stack.
*
* @param item
* the item to add
*/
public void push(Item item) {
Node<Item> oldfirst = first;
first = new Node<Item>();
first.item = item;
first.next = oldfirst;
n++;
}
/**
* Removes and returns the item most recently added to this stack.
*
* @return the item most recently added
* @throws NoSuchElementException
* if this stack is empty
*/
public Item pop() {
if (isEmpty())
throw new NoSuchElementException("Stack underflow");
Item item = first.item; // save item to return
first = first.next; // delete first node
n--;
return item; // return the saved item
}
/**
* Returns (but does not remove) the item most recently added to this stack.
*
* @return the item most recently added to this stack
* @throws NoSuchElementException
* if this stack is empty
*/
public Item peek() {
if (isEmpty())
throw new NoSuchElementException("Stack underflow");
return first.item;
}
/**
* Returns a string representation of this stack.
*
* @return the sequence of items in this stack in LIFO order, separated by
* spaces
*/
public String toString() {
StringBuilder s = new StringBuilder();
for (Item item : this)
s.append(item + " ");
return s.toString();
}
// 1.3.20
public void delete(int k) {
// 不允许大于n的值
if (k <= n) {
Node<Item> nowNextNode = first;
// 要第几个就next到第几个的上一个
for (int i = 1; (k - 1) != i; i++) {
nowNextNode = nowNextNode.next;
}
// 删除节点
Node<Item> needLinePoint = nowNextNode.next.next;
nowNextNode.next = needLinePoint;
} else {
// 这里简单的打印一下
StdOut.print("!------------------删除元素越界!------------------!");
}
}
// 1.3.21
public boolean find(String key) {
// 其实也可以用foreach
Node<Item> nowNode = first;
while (true) {
if (nowNode.item.equals(key)) {
StdOut.print("output : true");
return true;
// 链表没有下一个了
} else if (first.next.equals(null)) {
StdOut.print("output : false");
return false;
}
nowNode = nowNode.next;
StdOut.println("then next~");
}
}
// 1.3.24
public void removeAfter(Node<Item> node) {
if (node == null || node.next == null) {
// 什么也别做
} else {
if (isEmpty())
throw new NoSuchElementException("Stack underflow");
Node<Item> tempNode = node.next.next;
node.next = null;
node.next = node.next.next;
n--;
}
}
//1.3.26 接受一个链表和一个字符串key作为参数,删除链表中所有item域为key的节点
// 那么为了降低难度,直接写成对this产生作用的方法
public void remove(String key) {
//首先肯定是一个能够不停next直到没有的循环
//第二个就是要时时刻刻记住上一个节点,这样便于移除现在的节点
//然后就是对等于key和不等于key的逻辑判断
//先判断nowNode.next == null,如果是,说明已经到了尾部,结束循环
//相等:移除节点,上一个节点还是原先的上一个节点,但会连接至“被移除节点”的next,继续循环
//不相等:frontNode变更为nowNode,继续循环
Node<Item> nowNode = first;
Node<Item> frontNode = null;
while(true){
if (nowNode.next.equals(null)){
StdOut.print("没有拥有该字符串的item");
break;
}else if (nowNode.item.equals(key)){
frontNode.next = nowNode.next;
nowNode.next=null;
nowNode = frontNode.next;
StdOut.print("已搜索到该字符串!");
}else {
frontNode = nowNode;
nowNode = nowNode.next;
}
}
}
/**
* Returns an iterator to this stack that iterates through the items in LIFO
* order.
*
* @return an iterator to this stack that iterates through the items in LIFO
* order
*/
public Iterator<Item> iterator() {
return new ListIterator<Item>(first);
}
// an iterator, doesn't implement remove() since it's optional
private class ListIterator<Item> implements Iterator<Item> {
private Node<Item> current;
public ListIterator(Node<Item> first) {
current = first;
}
public boolean hasNext() {
return current != null;
}
public void remove() {
throw new UnsupportedOperationException();
}
public Item next() {
if (!hasNext())
throw new NoSuchElementException();
Item item = current.item;
current = current.next;
return item;
}
}
}
但是一经测试就报java.lang.NullPointerException
,为什么?
详细错误:
java.lang.NullPointerException
at Unit1.Stack.remove(Stack.java:168)
at Unit1.StackTest.Test(StackTest.java:19)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
测试里相关的代码:
public class StackTest {
@Test
public void Test() {
Stack<String> stack = new Stack<String>();
while (!StdIn.isEmpty()) {
String item = StdIn.readString();
if (item.equals("find")) {
stack.find("3");
}
else if (item.equals("remove")) {
stack.remove("1");
}
else if (item.equals("del")) {
stack.delete(3);
}
else if (!item.equals("-")) {
stack.push(item);
}
else if (!stack.isEmpty()) {
StdOut.print(stack.pop() + " ");
}
}
StdOut.println("(" + stack.size() + " left on stack)");
}
}
报错行是stack里的
if (nowNode.next.equals(null)){
以及测试里的
stack.remove("1");
但是我认为一楼的答案并不是很好...找到空指针的引用后,就向前找这个引用的初始化的逻辑部分。
初始化部分:
Node<Item> nowNode = first;
Node<Item> frontNode = null;
我很同意授人鱼不如授人以渔
。然而...
StdIn是一个能够不停输入的函数。
测试数据输入的是
1
2
3
4
5
remove
感谢各位!问题似乎已经解决了!附上代码:
public void remove(String key) {
// 首先肯定是一个能够不停next直到没有的循环
// 然后就是对等于key和不等于key的逻辑判断
// 先判断nowNode== null,如果是,说明已经到了尾部,结束循环。同样nowNode.next == null也是
Node<Item> nowNode = first;
while (true) {
if (nowNode == null ) {
StdOut.println("搜索结束");
break;
}
if (nowNode.item.equals(key)) {
StdOut.println("已搜索到该字符串!");
if (nowNode.next == null){
StdOut.println("已到达底端,退出!");
break;
}
//删除节点操作
//临时存储的对象:直接让nextNode和nowNode交换
Node<Item> nextNode ;
nextNode = nowNode.next;
nowNode.item = nextNode.item;
nowNode.next = nextNode.next;
nextNode = null;
}
else {
nowNode = nowNode.next;
}
}
}
前に外にいたときは、この質問自体は面白くなかったのですが、1階はとても面白くて、気に入った人が2人いました。
この一文
授人鱼不如授人以渔。
が合意を引き起こしたのではないかと心配していますが、これは特に lz のような問題に共鳴しやすいものです。找到空指针的引用后,就向前找这个引用的初始化的逻辑部分。
この文は少し誤解を招きます。最後に投稿したコードはわかりにくすぎて、質問の意味さえ理解できませんでした。
first.next.equals(null)
そのようなコードについて文句を言いたくありません。何か実践的なことはできますか? 2階の@hsfzxjyも
まずエラーについて話しましょう。誰もこのエラーを見つけませんでしたか?
リーリーxxx.equals(null) と xxx == null
違いは、プログラムが正常に実行できる場合、
xxx.equals(null) は false のみを返す必要があることです
xxx == null が返される場合もありますtrue、falseを返す場合もあります
xxx が null の場合
xxx.equals(null) は NullPointerException をスローします
xxx == null は true を返します
次に、論理の問題があります
最後のキーでは、次のキーは null です
リーリーつまり、
frontNode.next は null を指し、nowNode は null を指します。
コードの改善:
リーリーあなたのアイデアに基づいて実装しましたが、質問が残っています。
また、決済方法はあまりカウントしておりません。ご満足いただける回答にならなかった場合はご容赦ください。
first
がターゲットの場合、161 行目frontNode.next
でエラーが報告されますリンクされたリストが空の場合、157 行目と 167 行目でエラーが報告されます
nowNode.next
涼しい年にそれを行うことはできません。段階的に実行してみてはいかがでしょうか
————————補足内容————————————————
つまり、== は null かどうかを判断する場合にのみ使用できます。それ以外の場合は、equals メソッドを使用できる場合は、equals を使用するのが最善です。Java のほとんどの変数は参照型でなければなりません。補足: == 記号は等号の両側を比較します。変数の値は であるため、2 つのオブジェクト参照を比較するために使用すると、2 つの参照のメモリ アドレスが比較されます。実際、equals メソッドを使用して、参照の特定の内容を比較できます。 たとえば
リーリー
————————————————————————————————————————
タイトルを更新しました。xxx.equls(null) の形式は空である可能性がありますので使用しないでください。将来的には、== 記号を直接使用して、ブール値 isEmpty (オブジェクト o) ツール メソッドを決定またはカスタマイズできるようになります
——————————————————————————————古いアップデート———————————————— ———————————————————————リーリー
それでもざっと見てみました。
エラーを報告した後、java.lang.NullPointerException は引き続き下を見て、クラスのパッケージ名を含む最初の行を見つけます。その行の後の数字は、エラーが発生した行数です。リーリー
null ポインターの参照を見つけたら、この参照の初期化ロジック部分に注目してください。