programing

술어로 첫 번째 요소 찾기

newsource 2022. 8. 3. 23:18

술어로 첫 번째 요소 찾기

Java 8 lamda를 사용하기 시작한 지 얼마 안 되어 기능적인 언어로 익숙한 것을 구현하려고 합니다.

예를 들어, 대부분의 기능적 언어에는 시퀀스 또는 첫 번째 요소를 반환하는 목록으로 동작하는 일종의 검색 함수가 있습니다.trueJava 8에서 이를 실현하는 유일한 방법은 다음과 같습니다.

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

그러나 필터가 목록 전체를 스캔하기 때문에 비효율적으로 보입니다(적어도 잘못된 것일 수 있습니다).더 좋은 방법이 있을까요?

아니요, 필터는 전체 스트림을 검사하지 않습니다.이것은 느린 스트림을 반환하는 중간 작업입니다(실제로 모든 중간 작업은 느린 스트림을 반환합니다).다음과 같은 테스트를 실시하면 쉽게 납득할 수 있습니다.

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

스트림의 첫 번째 두 요소만 실제로 처리됩니다.

그래서 당신은 완벽하게 괜찮은 방법으로 접근 할 수 있습니다.

그러나 필터가 전체 목록을 스캔하기 때문에 비효율적으로 보입니다.

아니요. 술어를 만족시키는 첫 번째 요소가 발견되면 바로 "끊겨"집니다.특히 스트림 패키지 javadoc에서 게으름에 대한 자세한 내용을 볼 수 있습니다(강조).

필터링, 매핑 또는 중복 제거와 같은 많은 스트림 작업이 느긋하게 구현되어 최적화의 기회를 노출시킬 수 있습니다.예를 들어, "3개의 연속된 모음을 가진 첫 번째 문자열 찾기"는 모든 입력 문자열을 검사할 필요는 없습니다.스트림 운영은 중간(스트림 생산) 운영과 터미널(가치 또는 부작용 생산) 운영으로 나뉩니다.중간 작업은 항상 느립니다.

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

개체 목록에서 하나의 개체만 걸러야 했습니다.그래서 이걸 썼어 도움이 됐으면 좋겠어

Alexis C의 답변 외에 검색 중인 요소가 존재하는지 확실하지 않은 배열 목록을 사용하는 경우 이 항목을 사용하십시오.

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

그런 다음 단순히 a가 다음과 같은지 여부를 확인할 수 있습니다.null.

이미 @AjaxLeung에 의해 답변되어 있습니다만, 코멘트가 있어 찾기가 어렵습니다.
확인 전용

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

으로 단순화되다

lst.stream()
    .anyMatch(x -> x > 5)

import org.junit.Test;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;

// Stream is ~30 times slower for same operation...
public class StreamPerfTest {

    int iterations = 100;
    List<Integer> list = Arrays.asList(1, 10, 3, 7, 5);


    // 55 ms
    @Test
    public void stream() {

        for (int i = 0; i < iterations; i++) {
            Optional<Integer> result = list.stream()
                    .filter(x -> x > 5)
                    .findFirst();

            System.out.println(result.orElse(null));
        }
    }

    // 2 ms
    @Test
    public void loop() {

        for (int i = 0; i < iterations; i++) {
            Integer result = null;
            for (Integer walk : list) {
                if (walk > 5) {
                    result = walk;
                    break;
                }
            }
            System.out.println(result);
        }
    }
}

개선된 원라이너 답변:부울값 반환값을 찾고 있는 경우 isPresent를 추가하면 더 나은 결과를 얻을 수 있습니다.

return dataSource.getParkingLots().stream().filter(parkingLot -> Objects.equals(parkingLot.getId(), id)).findFirst().isPresent();

루핑 기능이 있는 범용 유틸리티 함수가 훨씬 깔끔한 것 같습니다.

static public <T> T find(List<T> elements, Predicate<T> p) {
    for (T item : elements) if (p.test(item)) return item;
    return null;
}

static public <T> T find(T[] elements, Predicate<T> p) {
    for (T item : elements) if (p.test(item)) return item;
    return null;
}

사용 중:

List<Integer> intList = Arrays.asList(1, 2, 3, 4, 5);
Integer[] intArr = new Integer[]{1, 2, 3, 4, 5};

System.out.println(find(intList, i -> i % 2 == 0)); // 2
System.out.println(find(intArr, i -> i % 2 != 0)); // 1
System.out.println(find(intList, i -> i > 5)); // null

언급URL : https://stackoverflow.com/questions/23696317/find-first-element-by-predicate