【问题标题】:Could anyone tell me why this returns an empty list (NPE)?谁能告诉我为什么这会返回一个空列表(NPE)?
【发布时间】:2011-10-25 06:33:24
【问题描述】:

我有这段代码应该将SortedLinkedList 的两个实例合并到一个 SLL 中(基于 mergeSort 合并),但它返回的是一个空列表:

import java.util.LinkedList;

public class SortedLinkedList<T extends Comparable<? super T>>
    extends LinkedList<T> {

private LinkedList<T> list; // the sorted list

// constructor, sorted with insertion sort
public SortedLinkedList(LinkedList<T> in)
{
    if(in.peek() == null || in.size() == 1)
        return;
    else {
        list = new LinkedList<T>();
        for(T e : in)
            list.add(e);
        int i, j;
        T temp;
        for(i = 0; i < list.size(); i++){
            j = i;
            temp = list.get(j);
            while(j > 0 && list.get(j-1).compareTo(temp) > 0){
                list.set(j, list.get(j-1));
                j--;
            }
            list.set(j, temp);
        }
}
}

// return the union of the sorted linked lists this
// and other
public SortedLinkedList<T> makeUnion( SortedLinkedList<T> other)
{
    list = new LinkedList<T>();
    SortedLinkedList<T> temp = new SortedLinkedList<T>(list);
    int i = 0, j = 0;
    while(i < this.size() && j < other.size()){
        if(this.get(i).compareTo(other.get(j)) <= 0){
            temp.add(this.get(i));
            i++;
        }
        else {
            temp.add(other.get(j));
            j++;
        }
    }
    while(i < this.size()){
            temp.add(this.get(i));
            i++;
    }
    while(j < other.size()){
            temp.add(other.get(j));
            j++;
        }
    return temp;
}

// print the items in list
public void print()
{
    for(T e : list)
        System.out.println(e);
}
}

在 SLL 构造函数中,我让它简单地返回 null 列表(私有变量列表在此方法的第一行初始化)。但是据我所知,这仍然应该给我一个 SLL 对象(最初也是null)。我可以在方法本身中很好地添加 temp,但在打印列表时会得到一个 NullPointerException。

我意识到将 get 与 LinkedList 一起使用并不是很有效。解决这个问题后,我将使用迭代器切换它们。

任何提示将不胜感激。

编辑:有趣的是,如果我将两个列表都放在临时 LL 中,然后在其上使用构造函数,我会得到相同的结果。由于 SLL 扩展了 LL,因此这些类型是兼容的:

public SortedLinkedList<T> makeUnion( SortedLinkedList<T> other)
{
    LinkedList<T> temp = new LinkedList<T>();
    temp.addAll(this);
    temp.addAll(other);
    SortedLinkedList<T> merge = new SortedLinkedList(temp);
    return merge;
}

EDIT2:似乎@Mead 是正确的......虽然 size() 和 get() 似乎适用于 SLL,但 add() 却没有。我在想,既然我正在扩展 LinkedList,它也可以与 SLL 一起使用。它没有,并且覆盖它们也没有做任何事情......我对此没有想法。有什么建议吗?

【问题讨论】:

  • 与其提供一些代码并描述其余部分,不如提供一个简短但完整的程序来说明问题。非常不清楚您为什么要在 makeUnion 方法中分配给 list 实例变量...我希望不理会现有列表。您的循环逻辑也有些问题 - 我希望您希望 while 循环在 for 循环之后,但您需要在循环中检查您是否已经用尽 @987654332 @也...
  • 这有一些问题(例如,这不是合并排序的正确实现)。您需要有测试代码证明 add()、get() 和 size() 工作正常。然后通过调用传递 LinkedList 的 SortedLinkedList 构造函数来测试实例化(如上面的代码中所示) - 并且在实例化之后 add()、get()、size() 仍然有效。以您的实例变量“链接”所暗示的方式,我怀疑它们是否正常工作 - 看来您正在扩展 Java List 类之一,但也使用私有实例变量“列表”。
  • 嘿。我的精神力量告诉我答案可能是用LinkedList&lt;T&gt; list = new LinkedList&lt;T&gt;(); 替换list = new LinkedList&lt;T&gt;();,但实际上,展示整个程序会有所帮助 - 任何答案都是猜测我们看不到的代码。
  • 很抱歉。感谢 Jon 提出这个问题,我现在修复了合并并处理了这些案例。 @Mead我不想让你们看着一团糟,这就是为什么我认为我只能摆脱那个sn-p。变量列表在顶部声明,我很抱歉。问题是 makeUnion 方法应该返回一个类构造函数类型的对象,所以我确定我声明 temp 的那一行是我出错的地方......
  • 只是为了烦人,让排序列表扩展 List 接口在概念上是错误的,因为您不能遵守 List 协定 - 也就是说,元素保持插入顺序。这就是为什么java中没有SortedList接口,而有SortedSet。

标签: java exception merge linked-list


【解决方案1】:

太棒了!您的编辑几乎揭示了您的问题:您没有正确扩展 LinkedList。解决这个问题,然后在 union 上工作。

手头的问题:这是一个名为SortedLinkedList 的类。我们可以假设它就像 LinkedList 一样,但其中的值是排序的。因此,鉴于此,这应该可行:

LinkedList<Integer> unsorted = new LinkedList<Integer>();
unsorted.add(200);
unsorted.add(100);
unsorted.add(300);
SortedLinkedList<Integer> sorted = new SortedLinkedList<Integer>(unsorted);
System.out.println(sorted.size());
for (Integer i : sorted) {
   System.out.println(i);
}
// Should print out:
// 3
// 100
// 200
// 300

但它不会。运行你的代码,它会打印出什么?

回来了?为什么打印出来?首先,考虑可以在类代码中使用的两个变量:this 指的是 SortedLinkedList 对象,this.list 指的是该 SortedLinkedList 对象中的实例变量。然后让我们看一下构造函数:当你添加到列表中时,你调用的是this.list.add()。您所写的内容使 SortedLinkedList 成为 list 实例变量的包装器 - 您没有添加到 SortedLinkedList (this) 中,而是添加到其中的列表 (this.list) 中。 使用this.list 实例变量的唯一方法是构造函数、打印和makeUnion。所有其他 LinkedList 方法都不知道 list 变量,所以当我调用 get() 时:

LinkedList<Integer> unsorted = new LinkedList<Integer>();
unsorted.add(200);
unsorted.add(100);
unsorted.add(300);
SortedLinkedList<Integer> sorted = new SortedLinked<Integer>(unsorted);
System.out.println(sorted.get(0));

它不知道查看您的 this.list 变量,因此它不会打印 100。实际上,它会崩溃,因为索引 0 中没有值。您没有添加到 get() 实际使用的实例变量中,因此方法认为 SortedLinkedList 对象为空。 this.list 是继承方法不知道的新变量。

所以,如果我们检查您的最新编辑:

public SortedLinkedList<T> makeUnion( SortedLinkedList<T> other)
{
    LinkedList<T> temp = new LinkedList<T>();
    temp.addAll(this);
    temp.addAll(other);
    SortedLinkedList<T> merge = new SortedLinkedList(temp);
    return merge;
}

temp.addAll(this) 不起作用,因为this 的所有方法都认为列表为空,因为它们没有查看this.list。出于同样的原因,temp.addAll(other) 也不起作用。

扩展类时常见的情况是希望现有方法继续工作。这意味着您需要将数据存储在 get() 和其他方法期望的位置。你是怎样做的?好吧,你已经在做! 您已经在做正确的事情 - 但是您是在实例变量 this.list 而不是 this 上做的。开始调用this.add()、this.set()、this.size() 而不是this.list.add() 并完全删除实例变量list - 它不需要,你有this。然后数据将位于其他方法所期望的位置。

(并在构造函数的第一行调用 super(),因此调用超类构造函数中的代码)。祝你功课好运 - 我建议在添加新方法之前测试对象的工作原理。

【讨论】:

  • 嗯,现在我觉得自己非常愚蠢。谢谢你的非常有见地的评论......不过有一个问题:当我这样做时,我仍然必须让'list'指向'this',这会给出“在构造函数中泄漏这个警告”。由于列表是私有的,这将是安全的,对吗?现在我只需要弄清楚如何为merge方法构造temp对象...
  • 听起来您仍然拥有(并且正在使用)private LinkedList&lt;T&gt; list。没有一个继承的方法知道它,停止使用它并从你的代码中删除实例变量。如果要在临时列表中进行排序,请使用局部变量,然后将排序后的元素添加到列表中 (this)。
  • 对不起,我太烦人了,实际上我在发布该评论后不久就想通了(我说得对,但在我的代码中错过了一个这样的实例......失眠让我想起了)但是昨天睡着了。无论如何,我为自己如此笨拙而道歉!我相信我会在相当长的一段时间内记住这个奇怪的东西。谢谢指点。
【解决方案2】:

查看实现后只是好奇,但您不能只执行一个 .addAll(...) 后跟一个 Collections.sort(...) 吗?这是我个人更喜欢的。

【讨论】:

  • 因为这是家庭作业,我不能使用 Collections.sort 和 list.toArray -> Arrays.sort 很遗憾。
猜你喜欢
  • 2017-10-09
  • 1970-01-01
  • 2017-05-03
  • 1970-01-01
  • 2011-04-13
  • 2021-04-16
  • 2023-02-08
  • 2013-05-29
  • 1970-01-01
相关资源
最近更新 更多