Присвоить поле экземпляра локальной переменной

Это метод keySet() в классе HashMap из JDK. Почему автор присвоил поле 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;
}

Ответы

Ответ 1

Чтобы немного расширить ответ Майкла, я ожидаю, что он должен гарантировать, что метод 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;
}

... не имеет этой проблемы, поскольку поле читается только раз.

Ответ 2

Если вы посмотрите объявление keySet в abstract class AbstractMap<K,V>, вы увидите, что оно определено как:

transient volatile Set<K>  keySet;

Так как он изменчивый, чтение его только один раз с помощью назначения локальной переменной дешевле, чем чтение его дважды, как в другом примере, который вы предоставили.

Кроме того, если вы должны были возвращать переменную keySet напрямую, тогда весь код клиента будет иметь дело с изменчивой ссылкой или нелетучей ссылкой (т.е. Set<K> ks)