[java] 술어로 첫 번째 요소 찾기

방금 Java 8 람다로 연주하기 시작했으며 기능적 언어로 익숙한 것들 중 일부를 구현하려고합니다.

예를 들어, 대부분의 기능적 언어에는 시퀀스에서 작동하는 일종의 찾기 함수 또는 술어가 첫 번째 요소를 리턴하는 목록이 true있습니다. Java 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 에서 게으름에 대해 더 자세히 읽을 수 있습니다 . 특히 (강조 광산) :

필터링, 매핑 또는 중복 제거와 같은 많은 스트림 작업을 느리게 구현하여 최적화 기회를 제공 할 수 있습니다. 예를 들어, “세 개의 연속 모음으로 첫 번째 문자열 찾기”는 모든 입력 문자열을 검사 할 필요는 없습니다. 스트림 작업은 중간 (스트림 생성) 작업과 터미널 (값 또는 부작용 발생) 작업으로 구분됩니다. 중간 작업은 항상 게으르다.


답변

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.


답변


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);
        }
    }
}


답변

개선 된 One-Liner 답변 : 부울 반환 값을 찾고 있다면 isPresent를 추가하여 더 잘 수행 할 수 있습니다.

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


답변