【问题标题】:What is causing NPE when creating TreeSet from ArrayList?从 ArrayList 创建 TreeSet 时导致 NPE 的原因是什么?
【发布时间】:2023-03-08 10:01:01
【问题描述】:

我正在开发一个使用 CDI 事件的 JEE 应用程序。

WildFly Full 12.0.0.Final (WildFly Core 4.0.0.Final)
jdk1.8.0_121
[org.jboss.weld.Version] (MSC service thread 1-2) WELD-000900: 3.0.3 (Final)

我在一些事件中传递数据。

我遇到困难的事件类是这个:-

public class KeywordPersist implements Serializable {

    private static final long serialVersionUID = 1L;

    private final String doi;
    private final List<String> keywords = new ArrayList<>();

    public KeywordPersist(final String doi, final List<String> keywords) {
        super();
        this.doi = doi;
        this.keywords.clear();
        this.keywords.addAll(keywords);
    }

//Getters, Setters, Hashcode, equals, toString NOT shown

}

我使用这个类如下:-

我将它注入到一个无状态的 EJB 中

@Inject
Event<KeywordPersist> keywordPersistEvent;

我只在数据存在时触发事件:-

if ((result.getKeywordList() == null) || (result.getKeywordList().getKeyword() == null) || result.getKeywordList().getKeyword().isEmpty()) {
            return;
        }

        keywordPersistEvent.fire(generateKeywordEvent(result));

我生成事件数据如下:-

private KeywordPersist generateKeywordEvent(final Result result) {
    final KeywordPersist keywordPersist = new KeywordPersist(result.getDoi(), new ArrayList<String>(result.getKeywordList().getKeyword()));
    return keywordPersist;
}

我对这个事件的观察者是另一个无状态的 EJB:-

@Asynchronous
@Transactional(TxType.REQUIRES_NEW)
@Lock(LockType.WRITE)
public void parse(@Observes final KeywordPersist keywordPersist) {
    persistDoiKeyword(keywordPersist);
}

persist 方法类似于:-

我想要唯一的关键字,所以我从事件中传递的关键字的 ArrayList 创建一个 TreeSet

/**
 * 
 * @param result
 */
private void persistDoiKeyword(final KeywordPersist keywordPersist) {

    if (keywordPersist == null) {
        System.out.println("---------------------------- persistDoiKeyword() if (keywordPersist == null) {}");
        throw new RuntimeException("Never can happen 0000");
    }

    if (keywordPersist.getDoi() == null) {
        System.out.println("---------------------------- persistDoiKeyword() if (keywordPersist.getDoi() == null) {}");
        throw new RuntimeException("Never can happen 0001");
    }

    if (keywordPersist.getKeywords() == null) {
        System.out.println("---------------------------- persistDoiKeyword() if (keywordPersist.getKeywords()== null) {}");
        throw new RuntimeException("Never can happen 0002");
    }

    if (keywordPersist.getKeywords().isEmpty()) {
        System.out.println("---------------------------- persistDoiKeyword() if (keywordPersist.getKeywords().isEmpty()) {}");
        throw new RuntimeException("Never can happen 0003");
    }

    final Set<String> uniqueKeywordsSet = new TreeSet<>(keywordPersist.getKeywords());
    final List<String> uniqueKeywords = new ArrayList<>(uniqueKeywordsSet);

    long rowCount = 0;

    for (String keyword : uniqueKeywords) {

        final DoiKeyword doiKeyword = new DoiKeyword();
        doiKeyword.setKeywordDoi(keywordPersist.getDoi());
        doiKeyword.setKeyword(cleanse(keyword));

        entityManager.persist(doiKeyword);

        rowCount++;

        if ((rowCount % 20) == 0) {
            entityManager.flush();
            entityManager.clear();
        }
    }

    entityManager.flush();
    entityManager.clear();
}

我得到的例外是......

Caused by: java.lang.NullPointerException
    at java.util.TreeMap.put(TreeMap.java:563)
    at java.util.TreeSet.add(TreeSet.java:255)
    at java.util.AbstractCollection.addAll(AbstractCollection.java:344)
    at java.util.TreeSet.addAll(TreeSet.java:312)
    at java.util.TreeSet.<init>(TreeSet.java:160)
    at com.research.events.observers.KeywordPersistObserver.persistDoiKeyword(KeywordPersistObserver.java:66)
    at com.research.events.observers.KeywordPersistObserver.parse(KeywordPersistObserver.java:37)
    at sun.reflect.GeneratedMethodAccessor62.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.jboss.as.ee.component.ManagedReferenceMethodInterceptor.processInvocation(ManagedReferenceMethodInterceptor.java:52)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
    at org.jboss.invocation.InterceptorContext$Invocation.proceed(InterceptorContext.java:509)

当我使用 TreeSet 时,为什么堆栈跟踪中会提到 TreeMap?

这个 NPE 来自哪里?

【问题讨论】:

    标签: jakarta-ee java-8 wildfly cdi treeset


    【解决方案1】:

    null 不允许在使用自然顺序的TreeSet 中引用。这是 String 的 TreeSet 的情况。

    TreeSet.add(E e)TreeSet 构造函数在后台使用确实指定:

    投掷:

    ...

    NullPointerException - 如果指定元素为 null 并且此设置 使用自然排序,或者它的比较器不允许空元素

    作为解决方法,您可以创建一个既依赖于String.compareTo() 又接受null 值的比较器。
    例如,要将 null 引用列为较低元素,您可以实例化您的 TreeSet,例如:

    TreeSet<String> set = new TreeSet<>(Comparator.nullsFirst(Comparator.naturalOrder());
    

    【讨论】:

    • 或者,由于我们使用的是 Java 8,因此不需要第三方库:Comparator.nullsFirst(Comparator.naturalOrder())
    • @Holger 我的 IDE 结果不好。我不得不搜索 Comparators.xxx 而不是 Comparator.xxx 谢谢 :)
    猜你喜欢
    • 2016-08-28
    • 1970-01-01
    • 2012-07-05
    • 1970-01-01
    • 2023-03-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-02
    相关资源
    最近更新 更多