メソッド参照述語を否定する方法


331

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 2018年

1
この答えは、JDK / 11でも採用されている究極のソリューションであると思われます。
ナマン

2
この場合の特別なメソッド参照構文を本当に見たい:s.filter(String ::!isEmpty)
Mike Twain

回答:



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



150

現在のメソッド参照とは逆のメソッド参照を作成する方法があります。メソッド参照をaに明示的にキャストPredicateし、negate関数を使用して変換する方法を示す、以下の@vlasecの回答を参照してください。それは、他のいくつかのそれほど面倒ではない方法の1つです。

これの反対:

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最適化を回避しない限り、手動のベンチマークコードを回避します。Java8 :ストリームのパフォーマンスを参照してください。vsコレクション

** forループテクニックの方が速いことを示唆したことで、私は不安定になっています。これは、ストリームの作成を排除し、別のメソッド呼び出し(述語の負の関数)の使用を排除し、一時的なアキュムレーターリスト/カウンターを排除します。したがって、最後の構成によって保存されるいくつかのことは、それをより速くする可能性があります。

私はそれがより速くなくても、よりシンプルでより良いと思います。ハンマーと釘が必要な場合は、チェーンソーと接着剤を持ち込まないでください。あなた方の何人かがそれに問題を抱えているのを知っています

ウィッシュリスト:StreamJavaユーザーがJava 関数に慣れてきているため、Java 関数が少し進化することを期待しています。たとえば、Streamの「count」メソッドはaを受け入れるPredicateことができるため、これは次のように直接実行できます。

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

or

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

なぜそれがずっと速いと言うのですか?
ホセ・Andias

@JoséAndias(1)高速ですか、それとも「はるかに高速」ですか?(2)その場合、なぜですか?何を決めましたか?
コーディネーター

3
「もっと速く走る」について詳しく説明してください。質問:(1)高速ですか、それとも「はるかに高速」ですか?(2)その場合、なぜですか?何を決めましたか?声明の著者であるあなたによってよりよく答えられます。私はそれがより速いか遅いかを考えていません。ありがとう
ホセ・Andias

2
次に、検討のためにこれを破棄します。これにより、ストリームの作成がなくなり、別のメソッド呼び出し(述語の負の関数)の使用がなくなり、一時的なアキュムレータリスト/カウンタがなくなります。したがって、最後の構成によって保存されるいくつかのこと。それがもっと速いのか、それともどれだけ速いのかはわかりませんが、「ずっと」速いと思います。しかし、おそらく「たくさん」は主観的です。負の述語とストリームを作成してストレートカウントを行うよりも、後者をコーディングする方が簡単です。私の好み。
コーディネーター

4
negate()は理想的なソリューションのようです。Predicate.negate(String::isEmpty);面倒なキャスティングなしでは静的ではありません。
Joel Shemtov

92

Predicateメソッドを持っているandornegate

ただし、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はコンパイルされません-ラムダは機能的なインターフェースを推測する必要があります(= 1つの抽象メソッドで)
  • p1とf1は正常に機能し、それぞれ異なるタイプを推測します
  • obj2はa PredicateをキャストしますObject-ばかげていますが有効です
  • f2は、実行時に失敗した-あなたはキャストすることはできませんPredicateFunction、それは推論について、もはやません
  • f3が機能する- testラムダで定義されている述語のメソッドを呼び出す
  • p2はコンパイルされません- メソッドIntegerがありませんisEmpty
  • p3もコンパイルしません- 引数をString::isEmpty持つ静的メソッドはありませんInteger

これが、型推論がどのように機能するかについてのさらなる洞察を得るために役立つことを願っています。


46

他者の答えと個人的な経験に基づいて構築する:

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

4
おもしろい-関数::参照をインラインでインライン化することはできません(String::isEmpty.negate())かもしれませんが、最初に変数に割り当てる(またはPredicate<String>最初にキャストする)と、機能します。!ほとんどの場合、ラムダw / が最も読みやすくなると思いますが、コンパイルできるものとコンパイルできないものを知ることは役に立ちます。
Joshua Goldberg

2
@JoshuaGoldberg私は私の答えでそれを説明しました:メソッド参照はそれ自体が述語ではありません。ここでは、キャストは変数によって行われます。
Vlasec 2016年

17

別のオプションは、曖昧でないコンテキストでラムダキャストを1つのクラスに利用することです。

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

これは文字列に対しては機能しますが、リストに対しては機能しません。エラー:(20、39)java:as。への参照があいまいです。ラムダとメソッド<T、R> as(java.util.function.Function <T、R>)in com.strands.sbs.function.Lambdas match
Daniel

ダニエル、オーバーロードされたメソッドを使用しようとすると発生する可能性があります:)
Askar Kalykov 2017年

型推論を最初よりもよく理解したので、それがどのように機能するかを理解しました。基本的には、機能する唯一のオプションを見つけます。おもしろそうだ、ボイラープレートを引き起こさないより良い名前があるかどうかわからない。
Vlasec

12

Predicate#negateあなたが探しているものではありませんか?


Predicate最初に取得する必要があります。
Sotirios Delimanolis 2014年

21
あなたはキャストする必要がありString::isEmpty()Predicate<String>する前に-それは非常に醜いです。
axtavt 2014年

3
@assylias Predicate<String> p = (Predicate<String>) String::isEmpty;およびasを使用しp.negate()ます。
Sotirios Delimanolis 2014年

8
@SotiriosDelimanolis私は知っていますが、それは目的に反します- s -> !s.isEmpty()その場合はむしろ書きたいです!
アサイリア2014年

@assylias:はい、私はそれが実際の考えだと思います。ラムダのロングハンドを書き出すだけが意図されたフォールバックです。
Louis Wasserman

8

この場合、uを使用しorg.apache.commons.lang3.StringUtilsて行うことができます

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

6
いいえ。問題は、メソッド参照を無効にする方法でありString::isEmpty、例として取り上げます。このユースケースがある場合でも、これは関連情報ですが、文字列のユースケースにのみ答える場合は、受け入れられません。
アンソニードロゴン2018

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コレクションの寄稿者です。



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.