将实例字段分配给局部变量

这是来自JDK的HashMap类的keySet()方法。 为什么作者将字段(keySet)分配给局部变量ks?

public Set keySet() { Set ks; return (ks = keySet) == null ? (keySet = new KeySet()) : ks; } 

上面和下面有什么区别? 这与线程安全有关吗?

 public Set keySet() { return (keySet == null ? (keySet = new KeySet()) : keySet; } 

为了略微扩展Michael的答案,我希望确保keySet()方法永远不会返回null ,除了提供所提到的性能优势之外。

鉴于此代码:

 public Set keySet() { return (keySet == null ? (keySet = new KeySet()) : keySet; } 

至少在理论上,在multithreading代码中, keySet字段可以在第一次读取( keySet == null )和第二次读取之间设置为null ,返回它。 我没有看过其余的代码,但我认为还有其他地方可能将keySet指定为null 。 这是作为野外问题的结果,还是防御性措施对作者来说是一个问题。

实际代码:

 public Set keySet() { Set ks; return (ks = keySet) == null ? (keySet = new KeySet()) : ks; } 

…没有这个问题,因为该字段只读一次。

如果查看abstract class AbstractMap中的keySet声明,您将看到它被定义为:

 transient volatile Set keySet; 

由于它是易失性的,因此使用局部变量赋值仅读取一次比读取它所提供的另一个示例中的两倍便宜。

此外,如果您直接返回keySet变量,那么所有客户端代码将处理易失性引用与非易失性引用(即Set ks