В цепочке фильтрации один из предикатов объединён через Predicate.and: при каком результате первого предиката второй гарантированно не запускается?
Если первый предикат возвращает false, второй предикат не вызывается. Predicate.and использует ленивую проверку с коротким замыканием: второй предикат вычисляется только после успешного прохождения первого.
Функциональные интерфейсы появились в Java 8 вместе с лямбда-выражениями и Stream API. Для Predicate были добавлены методы композиции, включая and, or и negate, чтобы строить условия без ручного написания вспомогательных классов и вложенных if.
Такой подход решает две задачи: позволяет переиспользовать проверки и сохраняет логическую оптимизацию короткого замыкания, знакомую по операторам && и ||.
Предикаты могут выполнять дорогие операции: обращаться к кэшу, разбирать данные или вычислять сложное условие. Если дорогую проверку запускать для каждого элемента независимо от результата дешёвой предварительной проверки, возрастут время обработки и количество побочных обращений.
Неверный порядок особенно опасен, когда второй предикат зависит от предположения, проверяемого первым. Например, первая проверка может подтверждать, что строка непустая, а вторая — безопасно обращаться к её содержимому.
Вызов first.and(second) логически эквивалентен последовательности: сначала выполняется first, затем second только при результате true. Поэтому при false итог сразу становится false, а второй предикат не получает текущий элемент.
Для пустой строки startsWithA не запускается. Для остальных строк сначала выполняется nonEmpty, затем — startsWithA; результатом будут строки, начинающиеся с «А».
Порядок важен: and не переставляет предикаты автоматически. Обычно первым ставят дешёвое и достаточно селективное условие, а дорогую проверку — вторым. Если предикаты имеют побочные эффекты или могут выбрасывать исключения, перестановка меняет наблюдаемое поведение, поэтому составные предикаты предпочтительно делать чистыми.
Вызов and с null приводит к NullPointerException при построении составного предиката. Если первый предикат выбросил исключение, второй также не выполняется. В параллельном стриме это правило действует для каждого элемента отдельно, но порядок обработки разных элементов между потоками не гарантируется.
Сервис фильтрует большой поток объектов: сначала нужно проверить локальный статус объекта, а затем выполнить дорогую проверку доступности связанного ресурса.
Вариант с независимым запуском обеих проверок тратит ресурсы даже для объектов с неподходящим статусом. Вариант с двумя последовательными операциями filter также корректен и часто лучше читается; его преимущество — простая диагностика каждого этапа, а недостаток — более раздельное описание общего условия.
Выбран составной предикат с дешёвой проверкой статуса первым, если условие используется как единая переиспользуемая политика. Это сохраняет короткое замыкание и уменьшает число обращений к ресурсу. Если проверки требуют разных метрик или трассировки, предпочтительнее две операции filter, хотя механизм невычисления последующего условия останется тем же.
and в параллельном стриме?Да, но только внутри проверки одного элемента: второй предикат не вызывается, если первый вернул false. Параллельность не отменяет этот контракт. При этом разные элементы могут проверяться одновременно, поэтому нельзя делать вывод о глобальном порядке вызовов или использовать небезопасное общее состояние.
and без изменения результата?Только если предикаты чистые, не выбрасывают зависящие от порядка исключения и не имеют наблюдаемых побочных эффектов. В математическом смысле логическое AND коммутативно, но программные функции могут менять состояние, обращаться к внешним системам или зависеть от времени. Тогда перестановка способна изменить не только производительность, но и итоговое поведение.
Predicate.and от последовательного применения двух фильтров?Для обычных чистых предикатов оба варианта логически дают одинаковый набор элементов и сохраняют короткое замыкание: второй фильтр получает элемент только после успешного первого. Составной предикат удобен как единый объект, который можно передать или переиспользовать. Два фильтра обычно лучше показывают этапы конвейера и упрощают отдельное профилирование, но не создают принципиально другой гарантии вычисления второго условия.