Каков предпочтительный способ связывания предикатов в Java?

У меня есть куча предикатов, и я хочу связать их вместе логическим «и», чтобы конечный результат был истинным, только если все отдельные предикаты оцениваются как истинные.

Как я понимаю, есть два способа написать это. Я могу связать их вместе следующим образом:

Predicate composedPredicate = 
  predicate1
  .and(predicate2)
  .and(predicate3)
  .and(predicate4)

Или я могу использовать более вложенный подход:

Predicate composedPredicate = 
  predicate1
  .and(predicate2
       .and(predicate3
            .and(predicate4)))

Очевидно, что вариант 1 более удобочитаем, но кажется, что он может быть немного менее эффективным. Я предполагаю, что вариант 1 примерно эквивалентен: (((p1 && p2) && p3) && p4)

В то время как вариант 2 будет: (p1 && (p2 && (p3 && p4)))

Во втором случае будет оцениваться первый аргумент p1, и если он окажется ложным, все сразу замкнется, и все готово. В первом варианте первым аргументом фактически является целое выражение ((p1 && p2) && p3), которое само имеет первый аргумент (p1 && p2), который, в свою очередь, имеет p1 в качестве первого аргумента. По сути, вы делаете еще 2 «шага» вверх по стеку, прежде чем p1 действительно может быть оценен. На самом деле я не знаю, как Java реализует методы предикатов по умолчанию, поэтому поправьте меня, если я ошибаюсь.

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


person cont    schedule 10.09.2019    source источник


Ответы (1)


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

Итак, начните с вашего первого варианта, более читаемого, который не содержит вложенных предикатов, чтобы нарушить порядок операций.

Если вы обнаружите, что есть проблема с производительностью из-за какой-либо статистики профилировщика или выполнения тестов производительности, вы можете изменить порядок операций для повышения производительности.

  1. Вы можете сначала разместить предикаты, которые с наибольшей вероятностью вызовут короткое замыкание, чтобы избежать ненужных накладных расходов на выполнение предиката, который не повлияет на результат. Поскольку вы используете and между ними, это будут те, которые с большей вероятностью вернут false.
  2. Если вы вызываете эти связанные предикаты большое количество раз, посмотрите, сможете ли вы найти какие-либо предикаты, которые являются инвариантными, например. не зависеть от индекса итерации цикла и сохранять эти результаты, чтобы их не нужно было повторять снова и снова.
  3. Если вы обнаружите, что один из предикатов требует значительных вычислительных ресурсов и не является инвариантным в цикле, поэтому его необходимо выполнять снова и снова, поместите его последним в цепочку, чтобы увидеть, не могут ли предыдущие предикаты закоротить так, чтобы его вообще не нужно выполнять.

Но это также не должно влиять на читабельность. Вам не нужно вкладывать предикаты, как вы это сделали; самое большее, вам нужно будет изменить их порядок, например.

Predicate composedPredicate = 
  predicate4
  .and(predicate2)
  .and(predicate1)
  .and(predicate3)

если вы заметили, что predicate4 редко возвращает true, а predicate3 требует больших вычислительных ресурсов.

person rgettman    schedule 10.09.2019
comment
Переупорядочивание и другие упомянутые вами методы являются ортогональными проблемами по сравнению с тем, что у меня есть. Меня беспокоит то, что даже если предположить, что самый первый предикат будет коротким, читабельная версия все равно может создать более высокий стек, чем вложенная версия. - person cont; 10.09.2019
comment
Стек выполнения будет расти только в том случае, если будет вызван предикат (метод), и я бы не стал беспокоиться о стеке выполнения из 4 методов. - person rgettman; 10.09.2019
comment
+1 За предпочтение читабельности незначительному приросту производительности. Код создается людьми для людей. Он только случайно читается машиной. Кнут стиль. - person arcadeblast77; 10.09.2019