这是来自JDK的keySet()
类的HashMap
方法。为什么作者将字段keySet
分配给局部变量ks
?
public Set<K> keySet() {
Set<K> ks;
return (ks = keySet) == null ? (keySet = new KeySet()) : ks;
}
上面和下面有什么区别?这与线程安全有关吗?
public Set<K> keySet() {
return (keySet == null ? (keySet = new KeySet()) : keySet;
}
答案 0 :(得分:9)
如果您查看keySet
中的abstract class AbstractMap<K,V>
声明,您会看到它被定义为:
transient volatile Set<K> keySet;
由于它是易失性的,因此使用局部变量赋值只读一次比读取它所提供的其他示例中的两倍便宜。
此外,如果您要直接返回 keySet
变量,那么所有客户端代码都将处理易失性引用与非易失性引用(即Set<K> ks
)
答案 1 :(得分:8)
为了稍微扩展迈克尔的答案,我希望确保keySet()
方法永远不会返回null
,除了提供所提到的性能优势之外。
鉴于此代码:
public Set<K> keySet() {
return (keySet == null ? (keySet = new KeySet()) : keySet;
}
在多线程代码中理论上至少可以在第一次读取(keySet
)和第二次读取之间将null
字段设置为keySet == null
,其中退回。我没有查看其余的代码,但我认为还有其他地方keySet
可能被分配null
。这是作为野外问题的结果,还是防御性措施对作者来说是一个问题。
实际代码:
public Set<K> keySet() {
Set<K> ks;
return (ks = keySet) == null ? (keySet = new KeySet()) : ks;
}
...没有这个问题,因为该字段只读一次。