[java] Stream.allMatch ()가 빈 스트림에 대해 true를 반환하는 이유는 무엇입니까?

제 동료와 저는 빈 스트림 호출 allMatch()이 반환 될 것이라는 가정으로 인해 버그가 발생했습니다 false.

if (myItems.allMatch(i -> i.isValid()) {
    //do something
}

물론 문서를 읽지 않고 가정하는 것은 우리의 잘못입니다. 그러나 내가 이해하지 못하는 allMatch()것은 빈 스트림에 대한 기본 동작이 true. 그 이유는 무엇입니까? anyMatch()(반대로 false를 반환하는) 와 마찬가지로이 작업은 모나드를 떠나 if명령문 에서 사용되는 명령형 방식으로 사용됩니다 . 이러한 사실을 고려할 때 대부분의 경우 빈 스트림에서 allMatch()기본값을 true사용하는 것이 바람직한 이유 가 있습니까?



답변

이것은 공허한 진실로 알려져 있습니다 . 빈 컬렉션의 모든 구성원이 조건을 충족합니다. 결국 그렇지 않은 것을 가리킬 수 있습니까?

마찬가지로, 조건과 일치하는 컬렉션 요소를 찾을 수 없기 때문에를 anyMatch반환 false합니다. 이것은 많은 사람들에게 혼란 스럽지만 빈 세트에 대해 “any”및 “all”을 정의하는 가장 유용하고 일관된 방법으로 밝혀졌습니다.


답변

이에 대해 생각하는 또 다른 방법이 있습니다.

allMatch()이다 &&무엇 sum()이다+

다음 논리 문장을 고려하십시오.

IntStream.of(1, 2).sum() + 3 == IntStream.of(1, 2, 3).sum()
IntStream.of(1).sum() + 2 == IntStream.of(1, 2).sum()

이것은 sum()단지 일반화 이기 때문에 의미 가 있습니다 +. 그러나 요소를 하나 더 제거하면 어떻게됩니까?

IntStream.of().sum() + 1 == IntStream.of(1).sum()

IntStream.of().sum()특정 방식으로, 또는 빈 숫자 시퀀스의 합 을 정의하는 것이 합리적이라는 것을 알 수 있습니다 . 이것은 우리에게 합산의 “정체성 요소”또는 어떤 것에 추가 될 때 효과가없는 값 ( 0)을 제공합니다.

같은 논리를 Boolean대수에 적용 할 수 있습니다 .

Stream.of(true, true).allMatch(it -> it) == Stream.of(true).allMatch(it -> it) && true

보다 일반적으로 :

stream.concat(Stream.of(thing)).allMatch(it -> it) == stream.allMatch(it -> it) && thing

그렇다면 stream = Stream.of()이 규칙을 적용해야합니다. 이 문제를 해결하기 위해 &&의 “identity element”를 사용할 수 있습니다. true && thing == thing, 그래서 Stream.of().allMatch(it -> it) == true.


답변

호출 할 때 list.allMatch(또는 다른 언어로 된 유사어)의 항목 list이 술어와 일치하지 않는지 감지하고 싶습니다 . 항목이 없으면 일치하지 않을 수 있습니다. 다음 논리는 항목을 선택하고 조건 자와 일치 할 것으로 예상합니다. 빈 목록의 경우 항목을 선택하지 않고 논리는 여전히 유효합니다.

빈 목록이 allMatch반환 되면 어떻게 false됩니까?

내 간단한 논리는 실패합니다.

 if (!myList.allMatch(predicate)) {
   throw new InvalidDataException("Some of the items failed to match!");
 }
 for (Item item : myList) { ... }

수표를 !myList.empty() && !myList.allMatch().

요컨대, 빈 목록을 allMatch반환 true하는 것은 논리적으로 건전 할뿐만 아니라 실행의 행복한 경로에 있으며 확인이 더 적게 필요합니다.


답변

그것의 기초가 수학적 귀납처럼 보입니다. 컴퓨터 과학의 경우이를 적용하는 것이 재귀 알고리즘의 기본 사례가 될 수 있습니다.

스트림이 비어 있으면 정량화가 막연하게 충족되었다고하며 항상 참입니다. Oracle Docs : 스트림 작업 및 파이프 라인

여기서 핵심은 본질적으로 다소 오해의 소지가있는 “진공 적으로 만족”한다는 것입니다. Wikipedia에는 ​​그것에 대해 적절한 토론이 있습니다.

순수 수학에서 막연하게 참된 진술은 일반적으로 그 자체로 흥미롭지는 않지만 수학적 귀납법에 의한 증명의 기본 사례로 자주 발생합니다. Wikipedia : 공허한 진실


답변

이 질문에 이미 정확한 곱셈에 대한 답변이 있었지만 더 수학적 접근 방식을 도입하고 싶습니다.

이를 위해 스트림을 (수학적 의미에서) 세트로 간주하고 싶습니다. 그때

emptyStream.allMatch(x-> p(x))

여기에 이미지 설명 입력while에 해당

emtpyStream.anyMatch(x -> p(x))

에 해당합니다 여기에 이미지 설명 입력.

두 번째 부분이 거짓이라는 것은 빈 집합에 요소가 없기 때문에 매우 분명합니다. 첫 번째는 좀 더 까다 롭습니다. 정의에 따라 사실이라고 받아들이거나 그 이유 중 일부에 대해 다른 답변을 살펴볼 수 있습니다.

이 차이를 설명하는 예로는 “화성에 사는 모든 인간은 다리가 3 개 있습니다”(참) 및 “화성에 다리가 3 개있는 인간이 살고 있습니다”(거짓)와 같은 명제를들 수 있습니다.


답변