Instanz von im Stream prüfen

Lesezeit: 4 Minuten

Benutzer-Avatar
quma

Ich habe folgenden Ausdruck:

scheduleIntervalContainers.stream()
        .filter(sic -> ((ScheduleIntervalContainer) sic).getStartTime() != ((ScheduleIntervalContainer)sic).getEndTime())
        .collect(Collectors.toList());

…wo scheduleIntervalContainers Elementtyp hat ScheduleContainer:

final List<ScheduleContainer> scheduleIntervalContainers

Ist es möglich, den Typ vor dem Filter zu überprüfen?

Benutzer-Avatar
Eran

Sie können eine andere anwenden filter um nur die zu behalten ScheduleIntervalContainer Instanzen und Hinzufügen von a map erspart Ihnen die späteren Besetzungen :

scheduleIntervalContainers.stream()
    .filter(sc -> sc instanceof ScheduleIntervalContainer)
    .map (sc -> (ScheduleIntervalContainer) sc)
    .filter(sic -> sic.getStartTime() != sic.getEndTime())
    .collect(Collectors.toList());

Oder, wie Holger kommentierte, Sie können die Lambda-Ausdrücke durch Methodenreferenzen ersetzen, wenn Sie diesen Stil bevorzugen:

scheduleIntervalContainers.stream()
    .filter(ScheduleIntervalContainer.class::isInstance)
    .map (ScheduleIntervalContainer.class::cast)
    .filter(sic -> sic.getStartTime() != sic.getEndTime())
    .collect(Collectors.toList());

  • Oder .filter(ScheduleIntervalContainer.class::isInstance) .map(ScheduleIntervalContainer.class::cast)welchen Stil Sie bevorzugen.

    – Holger

    2. März 2016 um 9:30 Uhr

  • Wenn das obige Snippet in IDEA und Java8 List scheduleIntervalContainers zugewiesen ist, werde ich dennoch aufgefordert, das Ergebnis explizit in List scheduleIntervalContainers umzuwandeln. Wissen Sie warum?

    – K. Symbol

    23. April 2020 um 10:07 Uhr

  • @K.Symbol Haben Sie versucht, a zuzuweisen List<ScheduleContainer> oder ein List<ScheduleIntervalContainer>? Letzteres sollte es sein.

    – Eran

    23. April 2020 um 10:10 Uhr


Eine ziemlich elegante Option ist die Verwendung der Methodenreferenz der Klasse:

scheduleIntervalContainers
  .stream()
  .filter( ScheduleIntervalContainer.class::isInstance )
  .map( ScheduleIntervalContainer.class::cast )
  .filter( sic -> sic.getStartTime() != sic.getEndTime())
  .collect(Collectors.toList() );

  • Welchen Vorteil hat dieser Stil im Vergleich zur Verwendung von instanceof und (ScheduleIntervalContainer) zum Casten?

    – MageWind

    21. August 2018 um 22:38 Uhr

  • @MageWind das ist hauptsächlich eine Frage des Stils. Einige Leute bevorzugen es, weil Sie keinen weiteren Variablennamen (für den Lambda-Parameter) einführen müssen, andere, weil es etwas weniger Bytecode generiert (allerdings nicht genug Unterschied, um wirklich relevant zu sein).

    – Holger

    24. August 2018 um 16:53 Uhr

  • Das ist wirklich cool! Aber warum ist die .class erforderlich? ist nicht isInstance Teil von Object? Ist Class eine Klasse in Java?

    – Selbst posten

    13. Januar 2019 um 10:34 Uhr

  • @PostSelf In der Tat ist es und ScheduleIntervalContainer wäre nicht wirklich eine Instanz.

    – Namann

    5. November 2019 um 15:51 Uhr


  • Die Verwendung von Methodenreferenzen anstelle von Lambdas ist normalerweise lesbarer 😉

    – Oswaldo Junior

    22. Juli um 14:00 Uhr

Benutzer-Avatar
Andrej

Es gibt ein kleines Problem mit der @Eran-Lösung – das Eingeben des Klassennamens in beiden filter und map ist fehleranfällig – es kann leicht vergessen werden, den Namen der Klasse an beiden Stellen zu ändern. Eine verbesserte Lösung wäre etwa so:

private static <T, R> Function<T, Stream<R>> select(Class<R> clazz) {
    return e -> clazz.isInstance(e) ? Stream.of(clazz.cast(e)) : null;
}

scheduleIntervalContainers
  .stream()
  .flatMap(select(ScheduleIntervalContainer.class))
  .filter( sic -> sic.getStartTime() != sic.getEndTime())
  .collect(Collectors.toList());   

Es kann jedoch zu Leistungseinbußen beim Erstellen einer Stream für jedes passende Element. Seien Sie vorsichtig, wenn Sie es bei großen Datensätzen verwenden. Ich habe diese Lösung von @Tagir Vailev gelernt

  • Bei diesem Ansatz müssen Sie sich um NullPointerExceptions kümmern, da select(A.class) wird zurückkehren null für alles, was keine ist A. Hinzufügen .filter(Objects::nonNull) würde helfen. Übrigens: Der Ansatz von @Eran ist nullsicher.

    – Lars Gendner

    24. Mai 2019 um 9:39 Uhr


  • Entschuldigung, mein schlechtes … JavaDoc von flatMap sagt “Wenn ein zugeordneter Stream null ist, wird stattdessen ein leerer Stream verwendet.”. Ihre Lösung war also korrekt, auch ohne den Nullfilter.

    – Lars Gendner

    24. Mai 2019 um 11:16 Uhr

  • Trotzdem (IMO) seltsam, zurückzukehren null wenn Sie einfach einen leeren Stream hätten zurückgeben können

    – Alowaniak

    25. Februar 2020 um 22:20 Uhr

Anstelle eines Filters + einer Karte, wie andere Antworten vorschlagen, würde ich diese Hilfsmethode empfehlen:

public static <Super, Sub extends Super> Function<Super, Stream<Sub>> filterType(Class<Sub> clz) {
  return obj -> clz.isInstance(obj) ? Stream.of(clz.cast(obj)) : Stream.empty();
}

Verwenden Sie es als:

Stream.of(dog, cat fish)
  .flatMap(filterType(Dog.class));

Im Vergleich zu filter + map hat es folgende Vorteile:

  • Wenn die Klasse Ihre Klasse nicht erweitert, erhalten Sie einen Kompilierungsfehler
  • An einem einzigen Ort können Sie nie vergessen, eine Klasse in einem Filter oder einer Karte zu ändern
1299590cookie-checkInstanz von im Stream prüfen

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy