скажите предполагают, что у меня есть класс как:
public class Age {
private int age;
public int getAge() {
return this.age;
}
}
В моем Основном классе я звоню getAge()
метод много раз.
Таким образом, я хотел знать, это желательный, чтобы звонить так много раз или звонить однажды и присвоить его некоторой переменной и использованию та переменная.
Который является лучшим и почему?
Не пытайтесь микрооптимизировать это, если вы не обнаружите, что это действительно узкое место при профилировании. Я бы использовал метод доступа getAge (), поскольку это, скорее всего, наиболее удобное и очевидное решение.
При этом оба подхода, вероятно, будут работать одинаково. Во время выполнения JIT, скорее всего, полностью оптимизирует вызов getAge (), поэтому в обоих случаях это будет единственный примитивный доступ.
Вероятно, это ситуация, когда вы оптимизируете, прежде чем знаете, что вам нужно. Это просто целое число, поэтому оно не занимает много памяти, если вы храните значение в нескольких местах. В то же время это очень простой вызов метода, выполнение которого не займет много времени. Напишите это так, как вы считаете, наиболее читаемым. Затем, когда у вас будет надежная версия кода, вы можете использовать инструмент профилирования, чтобы увидеть, есть ли заметная разница.
При многократном вызове метода getAge()
возможен некоторый перерасход производительности, но я предлагаю вам рассмотреть Печальную трагедию театра микрооптимизации.
Это то, что вы, как автор API, должны указать вызывающей стороне.
В общем, если вы просто возвращаете свойство, вы можете пометить вызов как финальный (если вы не предлагаете фактический интерфейс). Это должно снизить стоимость вызовов, поскольку компилятор с большей вероятностью будет инлайнить функцию.
Если стоимость вычисления свойства дорогая (например, поиск строки), задокументируйте это в JAvaDocs для данного метода и укажите вызывающей стороне, что она может захотеть получить значение один раз и кэшировать его.
Не беспокойтесь. Совершенно не стоит делать такую микрооптимизацию. Подождите, пока вы закончите свой код, тогда он будет работать слишком медленно, затем достаньте профилировщик и работайте над тем, что профилировщик сообщает вам, что является источником проблемы.
Преждевременная оптимизация - корень всех зол.
Сложная часть состоит в том, чтобы понять, что современные JVM выполняют агрессивную оптимизацию при компиляции байтового кода на основе знаний, доступных во время выполнения.
Если, например, данный метод не переопределен в подклассе, он может обрабатываться точно так же, как конечный метод, позволяя JVM встроить копию своего кода в вызывающий метод вместо явного вызова метода. . (Если условия меняются, эти классы просто считаются новыми и, следовательно, перекомпилируются позже на основе новых условий).
Это означает, что get / set для атрибутов bean-компонентов (где значения просто сохраняются и извлекаются, а не вычисляются) очень дешевы, и вы должны выполнять вызовы каждый раз и ожидать, что JVM обнаружит возможные оптимизации и применит их.
Думаю, вы не увидите разницы во время выполнения - при условии, что вы не создадите более одного класса Age.
Для такого простого случая я бы выбрал тот, который выглядит лучше всего с точки зрения кода.
Есть несколько случаев, когда рекомендуется вызвать один раз и прочитать сохраненное возвращаемое значение, например, в
for (int i = 0; i < list.size(); i++)
doSomethingThatDoesNotAffectSizeOfList();
, поскольку у компилятора могут возникнуть проблемы с определением того, влияет ли тело цикла на размер списка. Правильно реализованный список всегда должен легко определять его размер, но в других примерах (или при работе с плохо реализованными коллекциями) это может быть хуже.
В общем, если метод требует больших вычислительных ресурсов, вы можете использовать мемоизацию , что в основном означает, что вы кэшируете уже вычисленные значения ввода / вывода.
Мемоизированная функция «запоминает» результаты, соответствующие некоторому набору определенных входных данных. Последующие вызовы с запомненными входными данными возвращают запомненный результат, а не пересчитывают его, тем самым устраняя первичную стоимость вызова с заданными параметрами из всех, кроме первого вызова функции с этими параметрами.
Этот метод вводит принцип «сохранить возвращаемое значение для эффективности» в сам метод, что упрощает обслуживание - бла-бла-бла ...
Я постараюсь сказать с помощью примеров кода то, что уже было сказано в другом ответе.
В случае, если вы представили вызов getAge() очень просто, а стоимость его вызова практически ничтожна. Не беспокойтесь об этом в этом случае.
Но если ваш getAge был чем-то причудливым, что делает много вычислений или получает доступ к ресурсам ввода-вывода,like:
public int getAge() {
return slowService.calculateAgeByBirthDate(birthDate); // it takes 2 seconds to execute for every call
}
Тогда наверняка было бы неплохо кэшировать de result и использовать его. Потому что, если вы вызовете его 30 раз, ваш код займет 1 минуту.
Вы не увидите большого изменения в производительности, если только не будете выполнять много операций внутри метода getAge().
В зависимости от того, как устроено ваше приложение, эти два варианта могут дать разные результаты! Если экземпляр age, который вы используете, является общим и изменяемым, различные места в вашем приложении могут изменить его значение между вызовами getAge()
. В этом случае вопрос о том, какой вариант лучше для вашего кода, является вопросом корректности, и решать вам. Как гласит старая пословица: "сначала сделай правильно, потом сделай быстро". И, как уже отмечали другие, в данном случае вам, вероятно, не нужно беспокоиться о части "сделать быстро".
Смежный пример - когда вы изменяете коллекцию во время итерации по ней. Чтобы не получить ConcurrentModificationException
, нужно выполнять итерацию над снимком.
В этом случае я бы посоветовал не смотреть на производительность, а на удобство использования и повторное использование кода. Ваша текущая реализация - простейший из геттеров, возвращающий целое число.
Но что, если где-то в будущем вы сохраните дату рождения человека и захотите динамически генерировать возраст? Если вы просто вызываете свойство напрямую, вам придется реорганизовать код. Однако, изменив внутреннее устройство getAge (), вы можете поместить туда вычисление, и все готово.
Мне бы очень хотелось, чтобы в языках программирования был введен модификатор «суперсекретное» свойство / поле, который в основном гласит: «Вы можете получить доступ к этому свойству только через его средство доступа».