Java 空指针检查以加快代码执行
Java null pointer checking for faster code execution
想象一个方法,该方法将一个对象作为参数并使用 for each 循环进行检查,让我们说一些集合内的其他值,根据传入的参数可以是 found/filtered。在函数开头检查空指针和 return 立即检查空集合或空指针是一个好习惯,还是最好省略空指针检查,因为每个循环都会处理它,但该函数将需要更多时间来执行(因为每次迭代的整体)。并且可以说这个集合并不大(不那么耗时)。
public ArrayList<Foo> find(Bar bar) {
if (bar == null) { // get rid of these part?
return null; //
} //
ArrayList<Foo> foos = new ArrayList<Foo>();
for (Foo f: Foo.values()) {
if (f.someBarCollection.contains(bar)) {
foos.add(f);
}
}
return foos;
}
我认为最好立即检查 null 和 return 如果您知道做任何进一步的操作都是浪费时间,因为您知道不需要它们。因此,我以牺牲更短的代码为代价来支持语义,以使事情变得明确。
编辑:
让我进一步详细说明。函数的结果与没有空检查部分的 OR 相同。问题是,我是否应该检查它,只是为了更好的表达(和一点性能提升,但这不是问题),但代码会更长(因为增加了检查)?
这取决于您希望 null
传递给此方法的频率(以及是否有)。
视情况而定
根据您的 API,当接收到的参数具有 null
值时,您可以执行以下操作:
- 抛出异常。可能 IllegalArgumentException 或描述此错误参数原因的自定义异常。
- Return 一个
null
值并让客户端在其余代码中处理结果。
- Return 空结果。如果是
List
(不是 ArrayList
),你可以 return Collections#emptyList
.
无论您使用哪个选项 API/methods,请务必在 javadoc 中正确记录。
是否检查更多的是品味问题而不是性能问题。 但是,为了让您的客户更轻松,您应该不是 return null
,而是空集合。
根据用例,您还可以引发异常(如果不允许空值)and/or 用 @NonNull
注释 bar
参数以允许使用可插入检查器.
这取决于预期的 bar
值。如果 null
是 bar
的允许值,那么您的决定是正确的。否则最好抛出异常。
需要考虑的一件事是面向未来。
如果你能想象集合合法 contains(null)
的时间,那么要么让它 运行 通过(并且 return 一个空集合)或者抛出异常(可能 IllegalArgumentException
).抛出异常意味着更改不会影响现有合法代码的行为。
除非你在 bar!=null
时考虑 returning null
'empty'(通常不推荐)它与 return 有点不一致 bar==null
.
大多数其他帖子都不鼓励 null
'empty'。
使用它的唯一原因是性能:
//WARNING - THIS CODE IS NOT FIRST CHOICE
public ArrayList<Foo> find(Bar bar) {
ArrayList<Foo> foos = null;
for (Foo f: Foo.values()) {
if (f.someBarCollection.contains(bar)) {
if(foos==null){
foos=new ArrayList<Foo>();
}
foos.add(f);
}
}
return foos;
}
Collections.emptyList()
是有价值的,但任何认为它可以修改列表的调用代码都会得到 UnsupportedOperationException
的令人讨厌的惊喜。
因此,将 null
替换为 'empty' 可能存在将 NullPointerException
替换为(可能频率较低且更难发现)UnsupportedOperationException
的风险。没有我们想要的那么多进展。
return本地 ArrayList
的下一个选择令人恐惧:
//DANGER - CODING HORROR!
static final sEmpty=new ArrayList<Foo>();
public ArrayList<Foo> find(Bar bar) {
ArrayList<Foo> foos = null;
for (Foo f: Foo.values()) {
if (f.someBarCollection.contains(bar)) {
if(foos==null){
foos=new ArrayList<Foo>();
}
foos.add(f);
}
}
return foos==null?sEmpty:foos;
}
在这种情况下,无知的代码可能会向假定为空的 ArrayList
添加元素,并导致它们被后续调用 find()
编辑 return。度过一天的好方法就是找到那个!
遗憾的是,我们似乎距离 Java 实现 C++ 风格 const
以区分可变和不可变引用比以往任何时候都远。
所以至少在某些情况下,你会留下 returning 小垃圾对象或 null
for 'empty'.
归根结底,Java 库采取的态度是,为了简单起见,创建和销毁大量小对象是可以接受的开销(看看 'boxed' 原语,如 Integer)。许多现代 JVM 都有分代垃圾收集器,旨在与这种风格相协调。
所以如果你还犹豫不决 'accepting' null
:
public ArrayList<Foo> find(Bar bar) {
if(bar==null){
throw new IllegalArgumentException("bar==null");
}
ArrayList<Foo> foos = new ArrayList<Foo>();
for (Foo f: Foo.values()) {
if (f.someBarCollection.contains(bar)) {
foos.add(f);
}
}
return foos;
}
否则顺其自然:
public ArrayList<Foo> find(Bar bar) {
ArrayList<Foo> foos = new ArrayList<Foo>();
for (Foo f: Foo.values()) {
if (f.someBarCollection.contains(bar)) {
foos.add(f);
}
}
return foos;
}
仅当此方法已被证明是瓶颈时才考虑替代方案。
想象一个方法,该方法将一个对象作为参数并使用 for each 循环进行检查,让我们说一些集合内的其他值,根据传入的参数可以是 found/filtered。在函数开头检查空指针和 return 立即检查空集合或空指针是一个好习惯,还是最好省略空指针检查,因为每个循环都会处理它,但该函数将需要更多时间来执行(因为每次迭代的整体)。并且可以说这个集合并不大(不那么耗时)。
public ArrayList<Foo> find(Bar bar) {
if (bar == null) { // get rid of these part?
return null; //
} //
ArrayList<Foo> foos = new ArrayList<Foo>();
for (Foo f: Foo.values()) {
if (f.someBarCollection.contains(bar)) {
foos.add(f);
}
}
return foos;
}
我认为最好立即检查 null 和 return 如果您知道做任何进一步的操作都是浪费时间,因为您知道不需要它们。因此,我以牺牲更短的代码为代价来支持语义,以使事情变得明确。
编辑: 让我进一步详细说明。函数的结果与没有空检查部分的 OR 相同。问题是,我是否应该检查它,只是为了更好的表达(和一点性能提升,但这不是问题),但代码会更长(因为增加了检查)?
这取决于您希望 null
传递给此方法的频率(以及是否有)。
视情况而定
根据您的 API,当接收到的参数具有 null
值时,您可以执行以下操作:
- 抛出异常。可能 IllegalArgumentException 或描述此错误参数原因的自定义异常。
- Return 一个
null
值并让客户端在其余代码中处理结果。 - Return 空结果。如果是
List
(不是ArrayList
),你可以 returnCollections#emptyList
.
无论您使用哪个选项 API/methods,请务必在 javadoc 中正确记录。
是否检查更多的是品味问题而不是性能问题。 但是,为了让您的客户更轻松,您应该不是 return null
,而是空集合。
根据用例,您还可以引发异常(如果不允许空值)and/or 用 @NonNull
注释 bar
参数以允许使用可插入检查器.
这取决于预期的 bar
值。如果 null
是 bar
的允许值,那么您的决定是正确的。否则最好抛出异常。
需要考虑的一件事是面向未来。
如果你能想象集合合法 contains(null)
的时间,那么要么让它 运行 通过(并且 return 一个空集合)或者抛出异常(可能 IllegalArgumentException
).抛出异常意味着更改不会影响现有合法代码的行为。
除非你在 bar!=null
时考虑 returning null
'empty'(通常不推荐)它与 return 有点不一致 bar==null
.
大多数其他帖子都不鼓励 null
'empty'。
使用它的唯一原因是性能:
//WARNING - THIS CODE IS NOT FIRST CHOICE
public ArrayList<Foo> find(Bar bar) {
ArrayList<Foo> foos = null;
for (Foo f: Foo.values()) {
if (f.someBarCollection.contains(bar)) {
if(foos==null){
foos=new ArrayList<Foo>();
}
foos.add(f);
}
}
return foos;
}
Collections.emptyList()
是有价值的,但任何认为它可以修改列表的调用代码都会得到 UnsupportedOperationException
的令人讨厌的惊喜。
因此,将 null
替换为 'empty' 可能存在将 NullPointerException
替换为(可能频率较低且更难发现)UnsupportedOperationException
的风险。没有我们想要的那么多进展。
return本地 ArrayList
的下一个选择令人恐惧:
//DANGER - CODING HORROR!
static final sEmpty=new ArrayList<Foo>();
public ArrayList<Foo> find(Bar bar) {
ArrayList<Foo> foos = null;
for (Foo f: Foo.values()) {
if (f.someBarCollection.contains(bar)) {
if(foos==null){
foos=new ArrayList<Foo>();
}
foos.add(f);
}
}
return foos==null?sEmpty:foos;
}
在这种情况下,无知的代码可能会向假定为空的 ArrayList
添加元素,并导致它们被后续调用 find()
编辑 return。度过一天的好方法就是找到那个!
遗憾的是,我们似乎距离 Java 实现 C++ 风格 const
以区分可变和不可变引用比以往任何时候都远。
所以至少在某些情况下,你会留下 returning 小垃圾对象或 null
for 'empty'.
归根结底,Java 库采取的态度是,为了简单起见,创建和销毁大量小对象是可以接受的开销(看看 'boxed' 原语,如 Integer)。许多现代 JVM 都有分代垃圾收集器,旨在与这种风格相协调。
所以如果你还犹豫不决 'accepting' null
:
public ArrayList<Foo> find(Bar bar) {
if(bar==null){
throw new IllegalArgumentException("bar==null");
}
ArrayList<Foo> foos = new ArrayList<Foo>();
for (Foo f: Foo.values()) {
if (f.someBarCollection.contains(bar)) {
foos.add(f);
}
}
return foos;
}
否则顺其自然:
public ArrayList<Foo> find(Bar bar) {
ArrayList<Foo> foos = new ArrayList<Foo>();
for (Foo f: Foo.values()) {
if (f.someBarCollection.contains(bar)) {
foos.add(f);
}
}
return foos;
}
仅当此方法已被证明是瓶颈时才考虑替代方案。