Java 7,TreeSet和NullPointerException。
最近,我尝试用Java 7编译一个用Java 6开发的项目。在执行测试过程中发生了很多有趣的事情,在Java 6中使用Java 7平稳运行的测试失败了! 因此,我必须理解为什么,这就是我发现的内容……首先要了解的上下文:在该项目中,我或多或少有一个简单的Hibernate Entity,如下所示。
package com.marco.test;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import org.hibernate.validator.NotNull;
@Entity
@Table(...)
public class ABean {
...
private String name;
@Column(name = "name", nullable = false)
@NotNull
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
请注意,字段“名称”为nullable = false并标有@NotNull 。 这是为了告诉Hibernate在用户尝试创建或将此列更新为Null的情况下使验证失败。 我也有该实体的比较器。 该比较器使用名称字段来比较Entity(这只是项目中的简化版本,当然,我不基于字符串长度来订购Bean)
package com.marco.test;
import java.util.Comparator;
public class ABeanComparator implements Comparator<ABean> {
@Override
public int compare(ABean o1, ABean o2) {
if (o1.getName().length() > o2.getName().length()) {
return 1;
} else if (o1.getName().length() < o2.getName().length()) {
return -1;
} else {
return 0;
}
}
}
请注意,字段名称上没有null检查,在我的项目中,Hibernate已经在处理它。 现在,我有一个测试,它创建一个空的Entity并将其存储到TreeSet中,然后执行其他我们在这里并不真正关心的东西。 测试的开始类似于以下代码:
package com.marco.test;
import java.util.SortedSet;
import java.util.TreeSet;
public class SortedTestTest {
public static void main(String[] args) {
ABean aBean = new ABean();
SortedSet<ABean> sortedSet = new TreeSet<ABean>(new ABeanComparator());
sortedSet.add(aBean);
}
}
如果我使用Java 6运行此程序,一切正常。 但是,对于Java 7,我有一个NullPointerException。
Exception in thread "main" java.lang.NullPointerException
at com.marco.test.ABeanComparator.compare(ABeanComparator.java:9)
at com.marco.test.ABeanComparator.compare(ABeanComparator.java:1)
at java.util.TreeMap.compare(TreeMap.java:1188)
at java.util.TreeMap.put(TreeMap.java:531)
at java.util.TreeSet.add(TreeSet.java:255)
at com.marco.test.SortedTestTest.main(SortedTestTest.java:14)
为什么? 这就是为什么:
public V put(K key, V value) {
Entry<K,V> t = root;
if (t == null) {
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
在Java 7中,当第一个Object添加到TreeSet时 (如果(t == null)),将执行与自身的比较(compare(key,key))。 然后,compare方法将调用比较器(如果有的话),并且name属性将具有NullPointerException。
// Little utilities
/**
* Compares two keys using the correct comparison method for this TreeMap.
*/
final int compare(Object k1, Object k2) {
return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
: comparator.compare((K)k1, (K)k2);
}
提出的问题多于答案:
- 如果您知道TreeSet中的对象是第一个也是唯一的,为什么还要进行比较?
- 我的猜测是他们想做的是运行一个简单的Null检查。
- 为什么不创建适当的null检查方法?
- 没有答案
- 为什么要浪费CPU和内存运行不需要的比较?
- 没有答案
- 为什么将一个对象与其自身进行比较(compare(key,key))?
- 没有答案
这是Java 6中TreeSet的put方法,您可以看到比较已被注释掉。
public V put(K key, V value) {
Entry<K, V> t = root;
if (t == null) {
// TBD:
// 5045147: (coll) Adding null to an empty TreeSet should
// throw NullPointerException
//
// compare(key, key); // type check
root = new Entry<K, V>(key, value, null);
size = 1;
modCount++;
return null;
}
您看到评论了吗? 向空的TreeSet添加null会引发NullPointerException。 因此,只需检查key是否为null,就不要进行无用的比较! 结论? 始终尝试分析您使用的代码,因为即使在jdk中,也存在错误代码!
翻译自: https://www.javacodegeeks.com/2013/04/even-in-the-jdk-there-is-bad-code.html