【问题标题】:Why is LinkedList.get throwing a NPE为什么 LinkedList.get 会抛出 NPE
【发布时间】:2020-01-29 13:16:33
【问题描述】:

我遇到了一个只发生在生产环境中的奇怪案例。

基本上,我们会保留每个用户最近的选项的历史记录,然后我们会像这样检索它们:

LinkedList l_recent = ApplicationEnvironment.getUserRecentOptions(username);

for (int i = 0; i < l_recent.size(); i++) {
   l_recent.get(i); // When i == 2 throws a NPE
}

然而,stacktrace 是空的,get 方法的 javadoc 只描述了一个可能的异常:IndexOutOfBoundsException if index &lt; 0 || index &gt;= size

幸运的是,这只发生在极少数用户身上,我们通过清除最近选项的历史记录来修复它。

但我仍然想知道为什么这会引发 NPE。

我唯一的猜测是,由于此列表是针对每个用户的,因此可能会发生多次登录同一用户的情况,然后我们可能会同时调用 LinkedList 的 add 或 remove 方法

编辑 1

这里是完整的代码:

import java.util.*;

public class App {
    static class Option {
        private String name;

        Option(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }

    private static final HashMap<String, LinkedList<Option>> userOptions = new HashMap<>();

    public static LinkedList<Option> getUserRecentOptions(String user) {
        LinkedList<Option> options;

        if (userOptions.containsKey(user)) {
            options = userOptions.get(user);
        } else {
            options = new LinkedList<>();
            userOptions.put(user, options);
        }

        return options;
    }

    public static void addRecentOptionToUser(String user, Option option){
        LinkedList<Option> options = getUserRecentOptions(user);

        for (int i = 0; i < options.size(); i++) {
            Option opt = options.get(i);

            if (opt.getName().equalsIgnoreCase(option.getName())) {
                options.remove(i);
                break;
            }
        }

        options.addFirst(option);

        // Max 4
        if (options.size() > 5) {
            options.removeLast();
        }
    }

    public static void main() {
        LinkedList<Option> recentOptions = getUserRecentOptions("demo");

        for (int i = 0; i < recentOptions.size(); i++) {
            recentOptions.get(i); // Throws NPE when i == 2 (sometimes...)
        }
    }
}

【问题讨论】:

  • 这就是全部代码吗?似乎l_recent = null(或类似的东西)正在某处发生。
  • 这是真实代码还是简化版? NPE 是调用本身还是在get 内部抛出的?
  • 你大概不调用List::get 并忽略结果。显示真实代码(或者至少,比这个人为的例子更接近“真实”)
  • @AnkurChrungoo:如果l_recent 是此代码所暗示的局部变量,则不会。再说一遍:如果代码是 OP 声称的那样,这不会发生......这就是为什么我们需要真实代码或至少更接近真实代码的东西。
  • @JoachimSauer 你去吧,不是局部变量..

标签: java


【解决方案1】:

所以 get(i) 为空。如果您执行for (Object lri : l_recent) {,则 ConcurrentModificationException 可能指向并发问题。不过,LinkedList 在用户最近的选项中保持共享,并且可能经常更新,因此 LinkedList 级别太低,不可共享。

制作一个提供高级访问权限的 API:getLatest、getAndRemember、getListCopyOfAll。

存在 NPE 可能是由于序列化/反序列化或某些内部获取,或分配的新列表。没有堆栈跟踪似乎指向上下文切换之类的东西。但这一切都不是基于我这一方面的一些渊博知识。

【讨论】:

    【解决方案2】:

    好的,这确实是一个多线程问题,如果我从不同的线程多次调用addRecentOptionToUser,问题就会重现,因为节点为空而抛出NPE。

    解决方案显然是让这段代码线程安全。

    谢谢

    【讨论】:

    • 意料之中。干杯!
    【解决方案3】:

    试试这个

    l_recent.size() -1 
    

    for 循环中 方法返回大小和列表从 0 点开始

    【讨论】:

      猜你喜欢
      • 2011-10-10
      • 1970-01-01
      • 2011-08-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-10
      • 1970-01-01
      相关资源
      最近更新 更多