it-swarm.com.ru

Найти первый элемент по предикату

Я только начал играть с Java 8 лямбдами и пытаюсь реализовать некоторые вещи, к которым я привык, в функциональных языках.

Например, большинство функциональных языков имеют какую-то функцию поиска, которая работает с последовательностями, или списки, которые возвращают первый элемент, для которого предикатом является true. Единственный способ добиться этого в Java 8 - это:

lst.stream()
    .filter(x -> x > 5)
    .findFirst()

Однако это кажется мне неэффективным, так как фильтр будет сканировать весь список, по крайней мере, насколько я понимаю (что может быть неправильно). Есть ли способ лучше?

434
siki

Нет, фильтр не сканирует весь поток. Это промежуточная операция, которая возвращает ленивый поток (фактически все промежуточные операции возвращают ленивый поток). Чтобы убедить вас, вы можете просто сделать следующий тест:

List<Integer> list = Arrays.asList(1, 10, 3, 7, 5);
int a = list.stream()
            .peek(num -> System.out.println("will filter " + num))
            .filter(x -> x > 5)
            .findFirst()
            .get();
System.out.println(a);

Какие выводы:

will filter 1
will filter 10
10

Вы видите, что фактически обрабатываются только два первых элемента потока.

Таким образом, вы можете пойти с вашим подходом, который прекрасно подходит.

629
Alexis C.

Однако это кажется мне неэффективным, так как фильтр будет сканировать весь список

Нет, не будет - он "сломается", как только будет найден первый элемент, удовлетворяющий предикату. Вы можете прочитать больше о лени в в частности, потоковый пакет javadoc (выделено мое)

Многие потоковые операции, такие как фильтрация, отображение или удаление дубликатов, могут быть реализованы лениво, открывая возможности для оптимизации. Например, "найти первую строку с тремя последовательными гласными" не нужно проверять все входные строки. Потоковые операции подразделяются на промежуточные (генерирующие поток) и терминальные (создающие побочные эффекты). Промежуточные операции всегда ленивы.

98
wha'eve'
return dataSource.getParkingLots().stream().filter(parkingLot -> Objects.equals(parkingLot.getId(), id)).findFirst().orElse(null);

Мне пришлось отфильтровать только один объект из списка объектов. Так что я использовал это, надеюсь, это поможет.

29
CodeShadow

В дополнение к ответу Alexis C , если вы работаете со списком массивов, в котором вы не уверены, существует ли элемент, который вы ищете, используйте это.

Integer a = list.stream()
                .peek(num -> System.out.println("will filter " + num))
                .filter(x -> x > 5)
                .findFirst()
                .orElse(null);

Затем вы можете просто проверить, является ли a null.

11
Ifesinachi Bryan

Если вы ищете логическое возвращаемое значение, мы можем сделать это лучше, добавив нулевую проверку:

return dataSource.getParkingLots().stream().filter(parkingLot -> Objects.equals(parkingLot.getId(), id)).findFirst().orElse(null) != null;
0
shreedhar bhat