【问题标题】:Why am I throwing a NullPointerException为什么我抛出 NullPointerException
【发布时间】:2014-01-24 07:17:21
【问题描述】:

所以现在是大学的寒假,我正在努力保持编码,所以我只是为我们在课堂上只讨论过但从未编码过的程序和算法编写代码。无论如何,我今天正在做的一个程序是你给计算机一个打乱的单词,它会输出所有可以由这些字母组成的单词(来自我们给出的 EnglishWordList 文件)。

不管怎样,这是我到目前为止的代码:

import java.io.*;
import java.util.*;

public class ProdFinder {
    private HashMap<Character, Integer> prodFinder = new HashMap<Character, Integer>();
    private HashMap<Integer, LinkedList<String>> findWord = new HashMap<Integer,     LinkedList<String>>();

    private static final char[] letters =     {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r',
            's','t','u','v','w','x','y','z'};
    private static final int[] primes =     {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101}; 

    public ProdFinder() throws FileNotFoundException {
        for (int i = 0; i < letters.length; i++) {
            prodFinder.put(letters[i], primes[i]);
        }

        Scanner sc = new Scanner(new File("EnglishWordList.txt"));


        while (sc.hasNextLine()) {
            String str = sc.nextLine();
            int strProd = findProduct(str);

            if (findWord.containsKey(strProd)) {
                LinkedList<String> wordList = findWord.get(strProd);
                wordList.add(str);
                findWord.put(strProd, wordList);
            }
            else {
            LinkedList<String> wordList = new LinkedList<String>();
            wordList.add(str);
            findWord.put(strProd, wordList);
        }
    }

    sc.close();
}

public int findProduct(String x) {
    int product = 1;
    char[] str = x.toCharArray();

    for (Character val: str) {
        product = product*prodFinder.get(val);
    }

    return product;
}

public void descramble(String x) {
    int prod = findProduct(x);

    if (findWord.containsKey(prod)) {
        System.out.println("The words that can be formed from the letters in " + x + " are: " +
                            findWord.get(prod));
    }
    else {
        System.out.println("No words can be formed from the letters in " + x + ".");
    }
}

}

现在,错误源于我开始将数字的所有质数乘积放入我的 HashMap 的那一行(尝试将每个质数乘积映射到其字母乘以该数字的单词的 LinkedList。)不知何故,这是抛出例外,当我注释掉那段代码并只运行 findProduct 方法时,它适用于我给它的任何单词以输出素数形式的字母乘积。

关于异常来自何处的任何想法?

编辑:对不起,堆栈跟踪如下:

Exception in thread "main" java.lang.NullPointerException
at ProdFinder.findProduct(ProdFinder.java:44)
at ProdFinder.<init>(ProdFinder.java:22)
at Descramble.main(Descramble.java:7)

据我所知,错误源于我尝试在 str 上调用 findProd 时,在这一行:

int strProd = findProduct(str);

【问题讨论】:

  • 请堆栈跟踪! (异常 => 堆栈跟踪 => 快速帮助)
  • 或者至少指出你在说哪一行(哪种方法等)
  • 或者告诉我们你想用素数做什么......字母如何乘以数字(这与在字典中找到你的单词有什么关系)?
  • 我会说你会在这里得到整数溢出,除非你的话都很短。您尝试将单词转换为素数乘积是否有原因?如果这是必要的功能,那么请使用BigInteger,而不是int
  • @DavidWallace 动机很明确:通过其组成字符集实现单词的 O(1) 查找。

标签: java exception io


【解决方案1】:

您目前是来自Mapunboxing null values。这就是您生成NullPointerException 的原因。

autoboxing 的简要入门:有一组表示原语的对象;这些被称为“包装类”或“盒装类”。它们包括具有关联类的每个原语。

这允许您在泛型集合中使用原始值,因为没有对象,泛型将无法工作。

当 Java 遇到需要原始值的操作时发生自动装箱转换 - 即某种形式的算术或一元运算符 (+, -, *, /, ++, --, +, -)。

装箱和拆箱转换发生在according to the specification。我会鼓励您阅读该内容,因为要列出的内容很多,而且我会重复已经说过的内容。

自动装箱的注意事项是null。请记住,原语具有固定的有限值,对象可以存在(根据字段数具有无限多个值),或者不存在,或者是null。如果您尝试将 null 值拆箱,则会出现 NullPointerException

现在,至于为什么要从 Map 中撤回 nullchar[] str = x.toCharArray(); 将任何 x 转换为字符数组,然后您在映射中查找所有 小写 个字符,并将它们映射到给定的素数。

如果x 中的字符是大写的会怎样?如果是数字呢?如果是特殊字符呢?空间?一个标签?不是小写 a-z 字母的东西?您在映射中找不到值,它会返回 null

这也是一个警告 - 在处理盒装原语时,必须非常小心,当依赖自动装箱转换时,它们不会处理任何 null 值或实例。这将导致混淆 NPE(比如这个),这可能更难追踪。

【讨论】:

  • 结果我们得到了一个只有小写字母的列表,它只是数据结构的介绍类,所以这些作业的重点是熟悉数据结构。你能解释更多关于拆箱空值的信息吗?据我所知,只有当我从列表中传递值时才会出现问题,当我对其中的字符串进行硬编码时,它可以正常工作。
  • +1 用大写字母文件中的单词测试了 OP 的代码 - NPE
  • @BillL:我在答案中添加了关于自动装箱的部分。此外,请注意您实际作为字符数组传入的内容 - 它会在任何不是小写字母字符的情况下爆炸。这包括空格。您可能需要剪掉一些。
【解决方案2】:

问题是 ProdFinder.java:44,因为堆栈跟踪表明它是

product = product*prodFinder.get(val);

空指针异常是 prodFinder 没有 val 的乘积/值,因此它返回 null,然后程序会尝试乘以乘积。

(anything * null) 导致 NullPointerException

prodFinder 映射没有值的原因可能是因为当添加到映射中的所有内容都是小写字母字符时,该单词有一些大写字母、特殊字符或数字

Java 调试器是解决此类问题的重要资源。使用 Eclipse 或 netbeans 等 IDE,只需在第 44 行放置一个断点即可快速找到罪魁祸首用例。

【讨论】:

  • 我将单词列表中的每个值传递给方法,因此 val 将是我传入的字符串中的每个字符。它通过手动传递单词来工作,所以我不不明白为什么从文件中获取它们并以这种方式传递它们会抛出 NullPointerException
  • 通过提示值的自动装箱无法将 null 整数对象从地图转换为 int 原语来改进您的答案。
  • @rolfl 说实话,Aaron 应该解释 怎么来 prodFinder.get(val) 返回 null,因为 OP 的逻辑显然希望每个可能的字符都是不变量已经在那里了。
  • @BillL 打印出它试图获取的内容(或使用调试器)。也许您的程序正在做一些您不希望它在其他地方做的事情,这只是另一个错误的结果。
  • @BillL - 在 product* 行之前添加一行 System.out.println(val + " maps to " + prodFinder.get(val));
【解决方案3】:

首先您应该学习如何使用调试器。如果你不能,你可以用一些 System.out 来帮助自己

public int findProduct(String x) {
    int product = 1;
    char[] str = x.toCharArray();
    System.out.println("str " + str);
    for (Character val: str) {
        System.out.println("prodFinder " + prodFinder);
        System.out.println("val " + val);
        System.out.println("prodFinder.get(val) " + prodFinder.get(val));
        product = product*prodFinder.get(val);
    }
    return product;
}

对于这样的文件内容:

Hello
World

输出是:

Cmd-$> java Descramble
str Hello
str [C@e80842
prodFinder {f=13, g=17, d=7, e=11, b=3, c=5, a=2, n=43, o=47, l=37 ...
val H
prodFinder.get(val) null
Exception in thread "main" java.lang.NullPointerException
        at ProdFinder.findProduct(Descramble.java:48)
        at ProdFinder.<init>(Descramble.java:23)
        at Descramble.main(Descramble.java:70)

如您所见,对于字母H,返回值为空

您的数组letters 中没有大写字母,因此地图中没有!

异常发生在行

product = product*prodFinder.get(val);

这里 jvm 试图将映射返回的 Integer 拆箱为原始 int 以执行乘法。但这是不可能的,因为地图返回null

【讨论】:

    【解决方案4】:

    我能想象下面一行抛出异常的唯一方法是,如果val 不在prodFinder

    product = product*prodFinder.get(val);
    

    您可能想在使用之前检查prodFinder.Get 的结果

    Integer value = prodFinder.get(val);
    
    if(val == null)
        //indicate that you have an error state?
    else
        product *= val;
    

    【讨论】:

      猜你喜欢
      • 2014-12-18
      • 2013-09-09
      • 2014-07-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-22
      • 2011-03-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多