Bir yöntem referans yüklemini reddetme


330

Java 8'de, bir akışı filtrelemek için bir yöntem başvurusu kullanabilirsiniz, örneğin:

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

Mevcut bir yöntemin reddi olan bir yöntem referansı oluşturmanın bir yolu var mı, örneğin:

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

notAşağıdaki gibi bir yöntem oluşturabilir ama JDK benzer bir şey teklif edip etmediğini merak ediyordum.

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

6
JDK-8050818 , statik bir Predicate.not(Predicate)yöntemin eklenmesini kapsar . Ancak bu sorun hala açık, bu yüzden bunu en erken Java 12'de görüyoruz (eğer varsa).
Stefan Zobel

1
Bu cevap , JDK / 11'de de uyarlanmış nihai çözüm olabilir gibi görünüyor .
Naman

2
Bu durum için özel bir yöntem referans sözdizimi görmek istiyorum: s.filter (String ::! İsEmpty)
Mike Twain

Yanıtlar:


177

Predicate.not( … )

yeni bir yöntem sunuyor Tahmin # değil

Böylece yöntem referansını reddedebilirsiniz:

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

214

Statik içe aktarma yöntem başvurusu için izin vermek için aşağıdaki statik alma planlıyorum:

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

Örneğin

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

Güncelleme : Java-11'den başlayarak, JDKyerleşikbir çözüm de sunar.


9
@SaintHill ama sonra yazmak zorundasınız, parametreye bir isim verin
flup



149

Geçerli bir yöntem başvurusunun tersi olan bir yöntem başvurusu oluşturmanın bir yolu vardır. Aşağıdaki yöntem başvurusunu açık bir şekilde a Predicateve ardından negateişlevi kullanarak dönüştürerek gösteren @ vlasec'in cevabına bakın . Bunu yapmak için çok sıkıntılı olmayan birkaç yol arasında bir yol budur.

Bunun tam tersi:

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

bu:

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

veya bu:

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

Şahsen, daha sonraki tekniği tercih ederim çünkü okumayı it -> !it.isEmpty()uzun ve ayrıntılı bir kadrodan daha net buluyorum ve sonra reddediyorum.

Ayrıca bir öngörüde bulunabilir ve yeniden kullanabilir:

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

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

Veya, bir koleksiyona veya diziye sahipseniz, basit, daha az ek yükü olan ve * daha hızlı olabilecek bir for-loop kullanın:

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

* Neyin daha hızlı olduğunu bilmek istiyorsanız, JMH http://openjdk.java.net/projects/code-tools/jmh kullanın ve tüm JVM optimizasyonlarından kaçınmadıkça el kıyaslama kodundan kaçının - bkz. Java 8: Akışların performansı vs Koleksiyonlar

** For-loop tekniğinin daha hızlı olduğunu öne sürdüğüm için pullanıyorum. Bir akış oluşturmayı ortadan kaldırır, başka bir yöntem çağrısı (yüklem için negatif işlev) kullanmayı ortadan kaldırır ve geçici bir akümülatör listesini / sayacını ortadan kaldırır. Son yapı tarafından kaydedilen ve daha hızlı hale getirebilecek birkaç şey.

Ben daha hızlı olmasa bile, daha basit ve daha güzel olduğunu düşünüyorum. İş bir çekiç ve çivi gerektiriyorsa, motorlu testere ve tutkal getirmeyin! Bazılarınızın bununla ilgili olduğunu biliyorum.

İstek listesi: StreamJava kullanıcılarının biraz daha evrim geçirdiğini görmek istiyorum . Örneğin, Stream'deki 'count' yöntemi Predicate, bunun doğrudan şu şekilde yapılabilmesi için bir kabul edebilir:

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

or

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

Neden daha hızlı olduğunu söylüyorsun ?
José Andias

@ JoséAndias (1) Daha hızlı mı yoksa 'çok daha hızlı mı? (2) Öyleyse, neden? Ne belirledin?
Koordinatör

3
Senden "koşmak için çok daha hızlı" üzerinde durmanı istiyorum. Sorular: (1) Daha hızlı mı yoksa 'çok daha hızlı mı? (2) Öyleyse, neden? Ne belirledin? ifadenin yazarı sizin tarafınızdan daha iyi yanıtlanır. Daha hızlı veya daha yavaş olduğunu düşünmüyorum. Teşekkür ederim
José Andias

2
Sonra bunu göz önünde bulunduracağım - Bir akış oluşturmayı ortadan kaldırır, başka bir yöntem çağrısı (yüklem için negatif işlev) kullanarak ortadan kaldırır ve geçici bir akümülatör listesini / sayacını ortadan kaldırır. Son yapı tarafından kurtarılan birkaç şey. Daha hızlı veya daha hızlı olup olmadığından emin değilim, ancak 'çok' daha hızlı olduğunu varsayıyorum. Ama belki de 'çok' özneldir. Sonuncuyu kodlamak, düz bir sayım yapmak için negatif tahminler ve akışlar yapmaktan daha kolaydır. Benim tercihim .
Koordinatör

4
negate () ideal bir çözüm gibi görünüyor. Ne yazık Predicate.negate(String::isEmpty);ki hantal döküm olmadan statik değil .
Joel Shemtov

92

Predicateyöntemleri vardır and, orve negate.

Ancak, String::isEmptybir değil Predicate, sadece bir String -> Booleanlambda ve hala bir şey olabilir, örneğin Function<String, Boolean>. Tip çıkarımı ilk önce yapılması gereken şeydir. filterYöntem algılar tip örtülü . Ancak bir argüman olarak geçmeden önce onu reddederseniz, artık gerçekleşmez. @Axtavt'ın belirttiği gibi, açık çıkarım çirkin bir yol olarak kullanılabilir:

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

Statik notyöntem ve lambda'nın muhtemelen en iyi fikir olduğu diğer cevaplarda tavsiye edilen başka yollar da vardır . Bu tl; dr bölümünü sonlandırır .


Bununla birlikte, lambda tipi çıkarımın daha derin bir şekilde anlaşılmasını istiyorsanız, örnekleri kullanarak biraz daha derinlemesine açıklamak istiyorum. Bunlara bakın ve ne olduğunu anlamaya çalışın:

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 derlenmez - lambdaların işlevsel bir arayüz çıkarması gerekir (= bir soyut yöntemle)
  • p1 ve f1 gayet iyi çalışıyor, her biri farklı bir tür ortaya çıkarıyor
  • obj2 bir atmalarını Predicateiçin Objectsaçma ama geçerli -
  • f2 zamanında başarısız - Eğer dökme edemez Predicateiçin Function, bu çıkarımın hakkında artık var
  • f3 çalışır - yükleminin testlambda tarafından tanımlanan yöntemini çağırırsınız
  • p2 derleme değil - Integeryok isEmptyyöntemi
  • p3 de derlemez - bağımsız değişkeni olan String::isEmptystatik bir yöntem yokturInteger

Umarım bu, tür çıkarımının nasıl çalıştığı hakkında daha fazla bilgi edinmeye yardımcı olur.


45

Başkalarının cevaplarına ve kişisel deneyimlerine dayanarak:

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

4
İlginç - istediği ::gibi işlevsel referansı satır içi yapamazsınız ( String::isEmpty.negate()), ancak önce bir değişkene (veya Predicate<String>önce yayınlanırsa) atarsanız , bu işe yarar . !Çoğu durumda lambda w / en okunabilir olacağını düşünüyorum , ancak neyin derlenebileceğini ve derlenemediğini bilmek yararlıdır.
Joshua Goldberg

2
@JoshuaGoldberg Cevabımda şöyle açıkladım: Metot referansı tek başına bir Predicate değil. Burada döküm değişken tarafından yapılır.
Vlasec

17

Başka bir seçenek, belirsiz sınıflarda lambda dökümünü bir sınıfa kullanmaktır:

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

}

... ve ardından yardımcı program sınıfını statik olarak içe aktarın:

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

1
Aslında bu işe şaşırdım - ancak JDK İşlev <T, Boolean> yerine Predicate <T> 'yi destekliyor gibi görünüyor. Ama Lambdas'ın <T, Boolean> Fonksiyonuna hiçbir şey atmasını sağlamazsınız.
Vlasec

Dize için çalışır, ancak Liste: Hata: (20, 39) java için kullanılır: com.strands.sbs.function içindeki <T> yönteminin her ikisinde de (java.util.function.Consumer <T>) belirsiz olduğu gibi başvuru. Lambdas ve com.strands.sbs.function.ambdas maçında (java.util.function.Function <T, R>) olarak <T, R> yöntemi. Lambdas maçı
Daniel Pinyol

Daniel, aşırı yüklenmiş yöntemi kullanmaya çalışıyorsanız olabilir :)
Askar Kalykov

Artık yazım çıkarımını orijinalinden çok daha iyi anladığım için, nasıl çalıştığını anlıyorum. Temel olarak, sadece çalışan tek seçeneği bulur. İlginç görünüyor, sadece kaynamaya neden olmayan daha iyi bir isim olup olmadığını bilmiyorum.
Vlasec

12

Predicate#negateAradığın şey olmamalı mı ?


Bir Predicateilk almalısın .
Sotirios Delimanolis

21
Daha önce yayınlamanız String::isEmpty()gerekiyor Predicate<String>- çok çirkin.
axtavt

3
@assylias Predicate<String> p = (Predicate<String>) String::isEmpty;ve olarak kullanın p.negate().
Sotirios Delimanolis

8
@SotiriosDelimanolis Biliyorum, ama bu amacı yendi - s -> !s.isEmpty()bu durumda yazmayı tercih ederim !
Ocak'ta

@assylias: Evet, aslında bunun bir fikir olduğuna inanıyorum; sadece lambda'yı uzun el yazmanın amaçlanan yedek.
Louis Wasserman

8

Bu durumda u org.apache.commons.lang3.StringUtilsve

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

6
Hayır. Soru, herhangi bir yöntem referansının nasıl reddedileceğidir ve String::isEmptyörnek olarak alınır. Bu kullanım senaryosuna sahipseniz yine de ilgili bilgiler vardır, ancak yalnızca String kullanım senaryosunu yanıtlıyorsa kabul edilmemelidir.
Anthony Drogon

4

Java 8 lambda ifadesini alıp (varsa) pakette tanımlanan herhangi bir yazılı standart Java 8 lambda'ya dönüştürebilen (Askar'ın teklifinden esinlenerek) tam bir yardımcı sınıf yazdım java.util.function. Örneğin şunları yapabilirsiniz:

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

Tüm statik yöntemler sadece adlandırılırsa çok sayıda belirsizlik olacağı için as(), yöntemi "as" olarak adlandırmayı ve ardından döndürülen türü çağırmayı seçtim. Bu bize lambda yorumunun tam kontrolünü verir. Aşağıda, kullanılan kalıbı ortaya çıkaran (biraz büyük) faydalı sınıfın ilk kısmı verilmiştir.

Tüm sınıfa bir bakın (gist'te).

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

Sen kullanabilirsiniz yüklemler gelen Eclipse Koleksiyonları

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

Dizeleri değiştiremiyorsanız List:

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

Sadece bir olumsuzlamaya ihtiyacınız varsa String.isEmpty()kullanabilirsiniz StringPredicates.notEmpty().

Not: Eclipse Koleksiyonlarına katkıda bulunuyorum.



0

Spring Boot (2.0.0+) kullanıyorsanız şunları kullanabilirsiniz:

import org.springframework.util.StringUtils;

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

Hangisi: return (str != null && !str.isEmpty());

Bu yüzden gerekli olumsuzlama etkisine sahip olacak isEmpty

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.