Здравствуйте, у меня есть два примера кода
операторы if/else if/else
private Object getObj(message) {
if (message.getA() != null)
return message.getA();
else if (message.getB() != null)
return message.getB();
else if (message.getC() != null)
return message.getC();
else return null;
}
Необязательные операторы
private Optional<Object> wrap(Object o){
return Optional.ofNullable(o);
}
private Object getObj(message) {
return wrap(message.getA())
.orElseGet(() -> wrap(message.getB())
.orElseGet(() -> wrap(message.getC())
.orElse(null)));
}
Итак, мой вопрос: как эти два сравниваются с точки зрения производительности (у меня есть около 15-20 операторов if-else в реальном коде)?
Стоит ли проводить рефакторинг читаемости кода и производительности или это неправильное использование опций?
Также каково снижение производительности в случае, если операторы if/else-if выросли до 100+?
заранее спасибо
Вы заботитесь о выполнении 100+ проверок. Я бы предпочел беспокоиться о читаемости более 100 операторов if/else.
Как насчет подхода создания Stream всех этих Optional и возврата с использованием findFirst с проверкой isPresent?
или Stream без Optional... но вы, вероятно, скоро закончите использовать Stream с Supplier тогда... не знаю, действительно ли это того стоит... Я думаю, что if/else-утверждения намного читабельнее... или, по крайней мере, вы сразу знаете, что Зачем вы возвращаете его, тогда как с Optional это довольно скрыто... не говоря уже о том, что это в основном злоупотребление самим Optional...
Если вы не знаете, что такое преждевременная оптимизация, найдите его.
FYI Java 9 имеет Optional#or метод, позволяющий избежать wrap того, что вы делаете.
@wilmol or хорош, но в данном случае не спасает от wrap. Синтаксис будет return wrap(message.getA()) .or(() -> wrap(message.getB())) .or(() -> wrap(message.getC())) .orElse(null);. Кронштейны расположены немного иначе. wrap используется так же.
возможно, геттеры должны вернуться Optional (вторая строка ответа Лино) - мы также не знаем, насколько дорог вызов геттеров или даже его побочные эффекты (причина против вышеприведенного решения if-else)




Не используйте Optional для условной логики.
Они были разработаны, быть возвращенным из метода, чтобы указать потенциально отсутствующее значение.
То, что вы можете красиво связать их в одну строку, не означает, что это понятно. Кроме того, вы буквально ничего не получаете. Накладные расходы на производительность может быть значительны. В худшем случае N объекты создаются, а затем удаляются. Просто оставайтесь со своими «обычными» if-else цепями.
Вместо того, чтобы искать способы сделать ваш текущий код более читабельным, сделайте шаг назад и спросите себя Зачем, вам нужно 15-20 операторов if-else. Можете ли вы разделить некоторую логику? Зачем вообще нужен геттер для стольких разных полей с потенциально разными типами? и т.п.
Спасибо за вклад, Лино, на самом деле это основной метод проверки, который должен выполнять все эти проверки (я знаю, что это, вероятно, проблема разделения обязанностей, но на данный момент логика рефакторинга очень утомительна).
@PanosK Если бы вы могли включить в свои вопросы более конкретные причины, почему вам это нужно (звонок на сайт, примеры и т. д.), Я могу дать более конкретный ответ.
Думаю, я получил свой ответ «Не используйте опционы для условной логики», я буду придерживаться if/else и попытаюсь реорганизовать логику, чтобы уменьшить их в будущем. Спасибо, Лино (все еще не получаю отрицательных голосов по моему вопросу).
Есть и третья форма (допускающая еще некоторые вариации).
return Stream.<Supplier<Object>>of(message::getA, message::getB, message::getC)
.map(Supplier::get)
.filter(Objects::nonNull)
.findFirst()
.orElse(null);
Вероятно, наименее гибкий и эффективный на данный момент, но понятный.
Вам нужно будет написать Stream.<Supplier<Object>>of(…), чтобы это скомпилировалось.
Отмечу, что использование поставщиков в потоке делает оценку этого столь же ленивой, как и в вопросе: getB вызывается только в том случае, если getA вернул null и т.д.
Если ваша цель — сжатый код, используйте троичную цепочку. Производительность, вероятно, идентична серии операторов if-then-else.
( this.getA() != null ) ? this.getA()
: ( this.getB() != null ) ? this.getB()
: ( this.getC() != null ) ? this.getC()
: null;
Как правильно указано в Ответ от Лино, вы пытаетесь вывести Optional за пределы их первоначальной цели (возвращая значения в лямбда-выражениях и потоках). Как правило, лучше всего использовать Optional только с оператором return и только тогда, когда вы хотите прояснить, что null является допустимым значением для возврата. См. этот ответ Брайана Гетца.
тернарный оператор — это сжатый if-then-else, объединенный в однострочный.
result = test ? valueToUseIfTestIsTrue : valueToUseIfTestIsFalse
Пример:
Color color = isPrinterMonochrome ? Color.GREY : Color.GREEN ;
Используйте цепочку троичных операторов.
Итак, это:
if ( this.getA() != null )
return this.getA();
else if ( this.getB() != null )
return this.getB();
else if ( this.getC() != null )
return this.getC();
else return null;
… становится следующим:
return
( this.getA() != null ) ? this.getA()
: ( this.getB() != null ) ? this.getB()
: ( this.getC() != null ) ? this.getC()
: null;
Пример кода.
public String getA ()
{
// return "A";
return null;
}
public String getB ()
{
// return "B";
return null;
}
public String getC ()
{
return "C";
// return null;
}
public String getABC ()
{
if ( this.getA() != null )
return this.getA();
else if ( this.getB() != null )
return this.getB();
else if ( this.getC() != null )
return this.getC();
else return null;
}
public String getABCTernary ()
{
return
( this.getA() != null ) ? this.getA()
: ( this.getB() != null ) ? this.getB()
: ( this.getC() != null ) ? this.getC()
: null;
}
Запустите этот пример кода.
String s = this.getABCTernary();
System.out.println( "s: " + s );
C
how these two compare in terms of performance
Тернарный оператор в Java — "короткое замыкание", что означает, что левая или правая сторона, которая соответствует результатам теста, является единственным вызываемым кодом. В нашем коде здесь, если getA возвращает ненулевое значение, это значение возвращается немедленно. Дальнейшие вызовы getB и getC никогда не выполняются. Так что в этом отношении производительность цепочечной тройки такая же, как у каскадного оператора if-then-else: выигрывает первое совпадение, дальнейших вызовов нет.
Если вы имеете в виду производительность в наносекундах исполнения, я не знаю. Беспокоиться об этом означало бы попасть в ловушку преждевременная оптимизация. Современные JVM чрезвычайно хорошо настроены для оптимизации вашего кода.
Пару дней назад я провел тщательный анализ производительности. Это оказывает огромное влияние на производительность. С AdoptOpenJDK операторы if выполняются до 10 раз быстрее. Когда JIT-компилятор работает в горячем режиме, штраф снижается до 20%.
GraalVM работает лучше: 3-кратное замедление с холодной JVM, и после того, как компилятору дается достаточно времени, чтобы сделать свое волшебство, также снижается производительность на 20%.
Однако реальный вопрос заключается в том, какая версия лучше для чтения и обслуживания приложения. Если вы похожи на меня, вам проще прочитать утверждение if, но есть и люди, предпочитающие функциональный подход.
Если вы готовы к глубокому-глубокому погружению, приглашаю вас на прочитайте мой подробный анализ о производительности и реализации Optional.orElseGet() и его друзей.
По моему мнению, после примерно 20 лет коммерческого опыта у меня сложилось мнение, что погоня за читабельностью — это абсолютная глупость, и в то же время намеренное написание запутанного кода — это зло.
Я знаю, что это полностью противоречит общественному мнению.
Однако это необходимо осознать каждому...
if или Optional. Такого рода дебаты будут происходить независимо от того, в каких конструкциях или ситуациях мы находимся.if, который более эффективен, чем функциональный подход, каждый и каждый раз, то люди, читающие этот код, привыкнут к нему и найдут его БОЛЕЕ ЧИТАЕМЫЙ — потому что это стиль, к которому они теперь привыкли.Итак, по сути: идите с if... используйте НЕТ это Optional!
Лично мне цепочку легче читать, но мои коллеги считают меня странным, поэтому я думаю, что это зависит. с точки зрения производительности это на 99% не имеет значения для вас; если профайлер не докажет обратное