Überprüfen Sie die Instanz im Stream

79

Ich habe folgenden Ausdruck:

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

... wo scheduleIntervalContainershat Elementtyp ScheduleContainer:

final List<ScheduleContainer> scheduleIntervalContainers

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

Quma
quelle

Antworten:

128

Sie können eine andere anwenden filter, um nur die ScheduleIntervalContainerInstanzen beizubehalten. mapWenn Sie eine hinzufügen, werden die späteren Besetzungen gespeichert:

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

Wie Holger kommentierte, können Sie 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());
Eran
quelle
121
Oder .filter(ScheduleIntervalContainer.class::isInstance) .map(ScheduleIntervalContainer.class::cast)welchen Stil Sie bevorzugen.
Holger
Wenn in IDEA und Java8 das obige Snippet List <ScheduleContainer> ScheduleIntervalContainers zugewiesen ist, werde ich dennoch aufgefordert, das Ergebnis explizit in List <ScheduleContainer> ScheduleIntervalContainers umzuwandeln. Wissen Sie warum?
K. Symbol
@ K.Symbol Hast du versucht, einem List<ScheduleContainer>oder einem zuzuweisen List<ScheduleIntervalContainer>? Es sollte das letztere sein.
Eran
119

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() );
Ricardo Gasca
quelle
Welchen Vorteil hat dieser Stil im Vergleich zur Verwendung von instanceof und (ScheduleIntervalContainer) zum Casting?
MageWind
@MageWind das ist meistens eine Frage des Stils. Einige Leute bevorzugen es, weil Sie keinen anderen Variablennamen (für den Lambda-Parameter) einführen müssen, andere, weil er etwas weniger Bytecode generiert (nicht genug Unterschied, um wirklich relevant zu sein).
Holger
Das ist wirklich cool! Aber warum ist das .classerforderlich? ist nicht isInstanceTeil von Object? Ist Classeine Klasse in Java?
Post Self
@ PostSelf In der Tat ist und ScheduleIntervalContainerwäre es nicht wirklich eine Instanz.
Naman
14

Es gibt ein kleines Problem mit der @ Eran- Lösung - die Eingabe des Klassennamens in beiden filterund mapist fehleranfällig - es ist leicht zu vergessen, den Namen der Klasse an beiden Stellen zu ändern. Eine verbesserte Lösung wäre ungefähr 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 kommen, wenn Streamfür jedes übereinstimmende Element ein erstellt wird. Seien Sie vorsichtig, wenn Sie es für große Datenmengen verwenden. Ich habe diese Lösung von @ Tagir Vailev gelernt

Andrey
quelle
Bei diesem Ansatz müssen Sie kümmern sich um Nullpointerexceptions nehmen, da select(A.class)kehrt nullfür alles , was nicht ist A. Hinzufügen .filter(Objects::nonNull)würde helfen. Übrigens: @ Erans Ansatz ist nullsicher.
Lars Gendner
Entschuldigung, mein schlechtes ... JavaDoc von flatMapsagt "Wenn ein zugeordneter Stream null ist, wird stattdessen ein leerer Stream verwendet.". Ihre Lösung war also auch ohne den Nullfilter korrekt.
Lars Gendner
3
Trotzdem (IMO) seltsam zurückzukehren, nullwenn Sie gerade einen leeren Stream hätten zurückgeben können
Alowaniak