Использование try-finally для выполнения операторов после возврата

Рассмотрим следующий код:

Foo result = array[index];
index = (index + 1) % array.length;
return result;

Для выполнения некоторых заключительных действий требуется дополнительная переменная. Имеет ли смысл написать это как:

try {
    return array[index];
} finally {
    index = (index + 1) % array.length;
}

или это повлияет на производительность? Обычно это считается хорошей / плохой практикой, и если да, то почему?

(В примере предполагается, что index действительный индекс для array и код не будет бросать ArrayIndexOutOfBoundsException)

Редактировать: вопрос не о необходимости использования try-finally а скорее о любом выигрыше или потере в производительности, которые я получаю, решив сделать это. Без этого переменная создается. С его помощью возвращаемое значение сохраняется где-то еще, возможно, более эффективным способом.

2 ответа

Решение

Без finally Вы объявляете действительно дополнительный Foo переменная.
Но действительно ли это дорого? Не так, как в обоих случаях Foo объект существует в памяти. Вы только что добавили ссылку для доступа к нему.
Ссылка на объект в области действия метода действительно дешева.
Вы никогда не должны беспокоиться об этом.

Кроме того, вам не нужно использовать finally оператор как способ повышения производительности исполняемого кода.
Читатели кода никогда не догадаются об этом.
finally служит для:

Блок finally всегда выполняется при выходе из блока try. Это гарантирует, что блок finally выполняется даже в случае непредвиденного исключения.

а также

размещение кода очистки в блоке finally - это всегда хорошая практика, даже если исключений не ожидается.

Первый код без finally утверждение намного яснее и не имеет никакого косвенного чтения.

Поэтому советую придерживаться:

Foo result = array[index];
index = (index + 1) % array.length;
return result;  

Как прокомментировано, основными накладными расходами является использование % вместо условия или маски

Вы можете запустить тест с JMH

static class Foo {

}

Foo[] array = new Foo[8];
int index = 0;

@Benchmark
public Foo simple() {
    Foo result = array[index];
    index = (index + 1) % array.length;
    return result;
}

@Benchmark
public Foo withFinally() {
    try {
        return array[index];
    } finally {
        index = (index + 1) % array.length;
    }
}

@Benchmark
public Foo withCondition() {
    int i = index++;
    if (index == array.length) index = 0;
    return array[i];
}

@Benchmark
public Foo withMask() {
    int i = index++;
    return array[i & (array.length-1)];
}

Результаты на моей машине... Ваш пробег будет меняться

Benchmark               Mode  Cnt    Score   Error   Units
ModMain.simple         thrpt   25  132.473 ± 1.764  ops/us
ModMain.withCondition  thrpt   25  363.077 ± 4.752  ops/us
ModMain.withFinally    thrpt   25  130.179 ± 1.585  ops/us
ModMain.withMask       thrpt   25  397.310 ± 3.506  ops/us

Чем выше, тем лучше.

Короче говоря, используя finally может быть немного медленнее, но я не буду беспокоиться об этом по сравнению с альтернативами.

Другие вопросы по тегам