메소드 참조 술어를 부정하는 방법


330

Java 8에서 메소드 참조를 사용하여 스트림을 필터링 할 수 있습니다. 예를 들면 다음과 같습니다.

Stream<String> s = ...;
long emptyStrings = s.filter(String::isEmpty).count();

기존 참조의 부정 인 메소드 참조를 작성하는 방법이 있습니까?

long nonEmptyStrings = s.filter(not(String::isEmpty)).count();

not아래와 같은 방법을 만들 수는 있지만 JDK가 비슷한 것을 제공하는지 궁금합니다.

static <T> Predicate<T> not(Predicate<T> p) { return o -> !p.test(o); }

6
JDK-8050818 은 정적 Predicate.not(Predicate)메소드 추가를 다룹니다 . 그러나이 문제는 여전히 열려 있으므로 Java 12에서 가장 빨리 볼 수 있습니다.
Stefan Zobel

1
이 답변 은 JDK / 11에서도 채택 된 최고의 솔루션 인 것 같습니다 .
Naman

2
s.filter (문자열 :: IsEmpty 함수!) : 난 정말이 사건에 대한 특별한 방법 참조 구문 보는 것처럼
마이크 트웨인

답변:


177

Predicate.not( … )

새로운 메소드 술어를 제공합니다

따라서 메소드 참조를 부정 할 수 있습니다.

Stream<String> s = ...;
long nonEmptyStrings = s.filter(Predicate.not(String::isEmpty)).count();

214

메소드 참조를 인라인으로 사용할 수 있도록 다음을 정적 가져 오기로 계획하고 있습니다.

public static <T> Predicate<T> not(Predicate<T> t) {
    return t.negate();
}

예 :

Stream<String> s = ...;
long nonEmptyStrings = s.filter(not(String::isEmpty)).count();

업데이트 : Java-11부터 JDK는 유사한 솔루션을 기본제공합니다.


9
@SaintHill 그러나 당신은 그것을 써야하고, 매개 변수의 이름을 주어야합니다
flup



149

현재 메소드 참조와 반대 인 메소드 참조를 작성하는 방법이 있습니다. 아래의 @vlasec의 답변을 참조하여 메소드 참조를 a로 명시 적으로 캐스팅 Predicate한 다음 negate함수를 사용하여 변환 하는 방법을 보여줍니다 . 그렇게하는 몇 가지 다른 방법 중 하나입니다.

이것의 반대 :

Stream<String> s = ...;
int emptyStrings = s.filter(String::isEmpty).count();

이것입니다 :

Stream<String> s = ...;
int notEmptyStrings = s.filter(((Predicate<String>) String::isEmpty).negate()).count()

아니면 이거:

Stream<String> s = ...;
int notEmptyStrings = s.filter( it -> !it.isEmpty() ).count();

개인적으로, 나는 it -> !it.isEmpty()장황한 명시 적 캐스트보다 읽는 것이 더 명확하고 부정 하기 때문에 나중의 기술을 선호합니다 .

술어를 만들어 재사용 할 수도 있습니다.

Predicate<String> notEmpty = (String it) -> !it.isEmpty();

Stream<String> s = ...;
int notEmptyStrings = s.filter(notEmpty).count();

또는 컬렉션이나 배열이있는 경우 간단하고 오버 헤드가 적고 * 더 빠를 수있는 for 루프를 사용하십시오.

int notEmpty = 0;
for(String s : list) if(!s.isEmpty()) notEmpty++;

* 빠른 것이 무엇인지 알고 싶다면 JMH http://openjdk.java.net/projects/code-tools/jmh 를 사용 하고 모든 JVM 최적화를 피하지 않는 한 핸드 벤치 마크 코드를 피하십시오 ( Java 8 : Streams의 성능 참조) . vs 컬렉션

** for-loop 기술이 더 빠르다는 제안에 착각했습니다. 스트림 작성을 제거하고 다른 메소드 호출 (조건 자에 대해 음수 함수)을 사용하지 않고 임시 누적 기 목록 / 카운터를 제거합니다. 따라서 마지막 구성으로 저장되는 몇 가지 사항은 더 빠를 수 있습니다.

그래도 더 빠르지는 않더라도 더 간단하고 훌륭하다고 생각합니다. 직업이 망치와 못을 요구하는 경우 전기 톱과 접착제를 가져 오지 마십시오! 나는 당신 중 일부가 그 문제를 안다는 것을 알고 있습니다.

희망 목록 : StreamJava 사용자가 익숙해지면서 Java 기능이 조금 발전하고 싶습니다 . 예를 들어 Stream의 'count'메서드는 Predicate다음과 같이 직접 수행 할 수 있도록 허용 할 수 있습니다.

Stream<String> s = ...;
int notEmptyStrings = s.count(it -> !it.isEmpty());

or

List<String> list = ...;
int notEmptyStrings = lists.count(it -> !it.isEmpty());

훨씬 빠르다고 말 합니까?
José Andias

@ JoséAndias (1) 더 빠르거나 '더 빠르다'? (2) 그렇다면 왜 그렇습니까? 무엇을 결정 했습니까?
코디네이터

3
"더 빨리 실행"에 대해 자세히 설명하고 싶습니다. 질문 : (1) 더 빠르거나 '더 빠르다'? (2) 그렇다면 왜 그렇습니까? 무엇을 결정 했습니까? 성명서 작성자 인 당신이 더 잘 대답합니다. 나는 그것이 더 빠르거나 느리다고 생각하지 않습니다. 감사합니다
José Andias

2
그런 다음 고려할 사항으로이를 버릴 것입니다. 스트림 생성을 제거하고 다른 메서드 호출 (조건 자에 대해 음수 함수)을 사용하지 않고 임시 누산기 목록 / 카운터를 제거합니다. 마지막 구성으로 저장되는 몇 가지 사항입니다. 나는 그것이 더 빠르거나 얼마나 빠를 지 확신하지 못하지만 '많이'더 빠르다고 가정합니다. 그러나 아마도 '많이'는 주관적입니다. 음수 술어와 스트림을 작성하여 직선 계수를 수행하는 것보다 나중에 코딩하는 것이 더 간단합니다. 내 선호.
코디네이터

4
negate ()는 이상적인 솔루션처럼 보입니다. 안타깝게도 Predicate.negate(String::isEmpty);성가신 캐스팅 이없는 것처럼 정적이 아닙니다 .
Joel Shemtov

91

Predicate방법을 가지고 and, or그리고 negate.

그러나 String::isEmpty는 아닙니다. Predicate단지 String -> Boolean람다이며 여전히 예를 들어 무엇이든 될 수 있습니다 Function<String, Boolean>. 형식 유추 가 먼저 발생해야합니다. 이 filter메소드는 타입을 암시 적으로 유추합니다 . 그러나 인수로 전달하기 전에 부정하면 더 이상 발생하지 않습니다. @axtavt가 언급했듯이 명시 적 추론은 추악한 방법으로 사용될 수 있습니다.

s.filter(((Predicate<String>) String::isEmpty).negate()).count()

다른 답변에는 다른 방법이 있습니다. 정적 not방법과 람다는 가장 좋은 아이디어 일 것입니다. 이것으로 tl; dr 섹션을 마 칩니다 .


그러나 람다 형식 유추에 대해 더 깊이 이해하고 싶다면 예제를 사용하여 조금 더 깊이 설명하고 싶습니다. 이것들을보고 무슨 일이 일어나는지 알아 내십시오.

Object obj1                  = String::isEmpty;
Predicate<String> p1         = s -> s.isEmpty();
Function<String, Boolean> f1 = String::isEmpty;
Object obj2                  = p1;
Function<String, Boolean> f2 = (Function<String, Boolean>) obj2;
Function<String, Boolean> f3 = p1::test;
Predicate<Integer> p2        = s -> s.isEmpty();
Predicate<Integer> p3        = String::isEmpty;
  • obj1이 컴파일되지 않습니다-람다는 기능적 인터페이스를 유추해야합니다 (= 하나의 추상 메소드 사용)
  • p1과 f1은 각각 다른 유형을 유추하여 잘 작동합니다.
  • obj2보다 A는 캐스트 PredicateObject바보 만 유효 -
  • F2는 런타임에 실패하지 않습니다 - 당신이 캐스팅 할 수 PredicateFunction,이 추론에 대해 더 이상이다
  • f3 작동- test람다로 정의 된 술어의 메소드 를 호출
  • p2는 컴파일되지 않습니다- 메소드 Integer가 없습니다isEmpty
  • p3도 컴파일되지 않습니다- 인수가있는 String::isEmpty정적 메소드 가 없습니다Integer

이것이 유형 유추 작동 방식에 대한 통찰력을 얻는 데 도움이되기를 바랍니다.


45

다른 사람의 답변과 개인적인 경험을 바탕으로 :

Predicate<String> blank = String::isEmpty;
content.stream()
       .filter(blank.negate())

4
흥미 롭습니다 – ::원하는 것처럼 함수 참조를 인라인 할 수는 없지만 String::isEmpty.negate()변수에 먼저 (또는 Predicate<String>먼저 캐스트) 할당하면 효과가 있습니다. 람다는 w / !대부분의 경우 가장 읽기 쉬운 것으로 생각 하지만 컴파일 할 수있는 것과 컴파일 할 수없는 것을 아는 것이 도움이됩니다.
Joshua Goldberg

2
@JoshuaGoldberg 나는 내 대답에서 다음과 같이 설명했습니다. 메서드 참조는 자체적으로 술어가 아닙니다. 여기서 캐스팅은 변수에 의해 수행됩니다.
Vlasec

17

또 다른 옵션은 모호하지 않은 컨텍스트에서 람다 캐스팅을 한 클래스로 활용하는 것입니다.

public static class Lambdas {
    public static <T> Predicate<T> as(Predicate<T> predicate){
        return predicate;
    }

    public static <T> Consumer<T> as(Consumer<T> consumer){
        return consumer;
    }

    public static <T> Supplier<T> as(Supplier<T> supplier){
        return supplier;
    }

    public static <T, R> Function<T, R> as(Function<T, R> function){
        return function;
    }

}

... 그리고 유틸리티 클래스를 정적으로 가져옵니다 :

stream.filter(as(String::isEmpty).negate())

1
실제로 이것이 작동하는 것에 놀랐습니다. 그러나 JDK는 Function <T, Boolean>보다 Predicate <T>를 선호하는 것 같습니다. 그러나 Lambdas가 Function <T, Boolean>으로 캐스팅하지는 않습니다.
Vlasec 2016 년

String에서는 작동하지만 List에서는 작동하지 않습니다. Error : (20, 39) java : com.strands.sbs.function의 <T> as (java.util.function.Consumer <T>) 메소드 모두 모호합니다. com.strands.sbs.function.Lambdas에서 Lambdas 및 메소드 <T, R> (java.util.function.Function <T, R>)
Daniel Pinyol

Daniel, 과부하 된 방법을 사용하려고하면 발생할 수 있습니다. :)
Askar Kalykov

형식 추론을 원래보다 훨씬 잘 이해 했으므로 작동 방식을 이해합니다. 기본적으로 작동하는 유일한 옵션을 찾습니다. 흥미롭게 보이며, 상용구를 유발하지 않는 더 나은 이름이 있는지 모르겠습니다.
Vlasec

12

Predicate#negate당신이 찾고있는 것이 아니어야합니까 ?


Predicate먼저 해야합니다 .
Sotirios Delimanolis 2009 년

21
당신은 캐스트에있는 String::isEmpty()Predicate<String>매우 추한 - 전에.
axtavt

3
@assylias Predicate<String> p = (Predicate<String>) String::isEmpty;및로 사용하십시오 p.negate().
Sotirios Delimanolis 2009 년

8
나는 알고있다 s -> !s.isEmpty().
assylias

@assylias : 그렇습니다, 나는 그것이 실제로 아이디어라고 생각합니다; 람다를 손으로 쓰는 것이 의도 된 폴백입니다.
Louis Wasserman

8

이 경우 u를 사용할 수 org.apache.commons.lang3.StringUtils있고

int nonEmptyStrings = s.filter(StringUtils::isNotEmpty).count();

6
아니오. 질문은 메소드 참조를 무효화하는 방법에 관한 String::isEmpty것이며, 예를 들어 설명합니다. 이 유스 케이스가 있으면 여전히 관련 정보이지만 String 유스 케이스에만 응답하면 받아 들여서는 안됩니다.
Anthony Drogon

4

Java 8 람다 식을 가져 와서 패키지에 정의 된 모든 유형의 표준 Java 8 람다로 바꿀 수있는 완전한 유틸리티 클래스 (Askar의 제안에서 영감을 얻음)를 작성했습니다 java.util.function. 예를 들어 다음을 수행 할 수 있습니다.

  • asPredicate(String::isEmpty).negate()
  • asBiPredicate(String::equals).negate()

모든 정적 메소드의 이름이 just이면 많은 모호성이 있기 때문에 as()메소드를 "as"로 호출 한 다음 리턴 된 유형을 선택했습니다. 이를 통해 람다 해석을 완전히 제어 할 수 있습니다. 다음은 사용 된 패턴을 나타내는 (거의 큰) 유틸리티 클래스의 첫 번째 부분입니다.

여기 에서 전체 수업을 살펴보십시오 (요점).

public class FunctionCastUtil {

    public static <T, U> BiConsumer<T, U> asBiConsumer(BiConsumer<T, U> biConsumer) {
        return biConsumer;
    }

    public static <T, U, R> BiFunction<T, U, R> asBiFunction(BiFunction<T, U, R> biFunction) {
        return biFunction;
    }

     public static <T> BinaryOperator<T> asBinaryOperator(BinaryOperator<T> binaryOperator) {
        return binaryOperator;
    }

    ... and so on...
}

4

Eclipse 콜렉션 에서 술어 를 사용할 수 있습니다

MutableList<String> strings = Lists.mutable.empty();
int nonEmptyStrings = strings.count(Predicates.not(String::isEmpty));

다음에서 문자열을 변경할 수없는 경우 List:

List<String> strings = new ArrayList<>();
int nonEmptyStrings = ListAdapter.adapt(strings).count(Predicates.not(String::isEmpty));

부정 만 필요한 경우을 String.isEmpty()사용할 수도 있습니다 StringPredicates.notEmpty().

참고 : 저는 Eclipse Collections에 기고자입니다.



0

Spring Boot (2.0.0+)를 사용하는 경우 다음을 사용할 수 있습니다.

import org.springframework.util.StringUtils;

...
.filter(StringUtils::hasLength)
...

어느 것이 : return (str != null && !str.isEmpty());

따라서 필요한 부정 효과가 있습니다. isEmpty

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.