Verifique instanceof en la secuencia


80

Tengo la siguiente expresión:

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

... donde scheduleIntervalContainerstiene el tipo de elemento ScheduleContainer:

final List<ScheduleContainer> scheduleIntervalContainers

¿Es posible comprobar el tipo antes del filtro?

Respuestas:


132

Puede aplicar otro filterpara mantener solo las ScheduleIntervalContainerinstancias, y agregar un maple ahorrará los últimos lanzamientos:

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

O, como comentó Holger, puede reemplazar las expresiones lambda con referencias de método si prefiere ese estilo:

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

122
O el .filter(ScheduleIntervalContainer.class::isInstance) .map(ScheduleIntervalContainer.class::cast)estilo que prefieras.
Holger

En IDEA y Java8, si el fragmento anterior se asigna a List <ScheduleContainer> scheduleIntervalContainers, todavía me pide que envíe el resultado a List <ScheduleContainer> scheduleIntervalContainers explícitamente, ¿sabe por qué?
K. Symbol

@ K.Symbol ¿Intentó asignar a a List<ScheduleContainer>o a List<ScheduleIntervalContainer>? Debería ser lo último.
Eran

119

Una opción bastante elegante es usar la referencia de método de la clase:

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

¿Qué beneficio tiene este estilo en comparación con el uso de instanceof y (ScheduleIntervalContainer) para emitir?
MageWind

@MageWind eso es principalmente una cuestión de estilo. Algunas personas lo prefieren, porque no es necesario introducir otro nombre de variable (para el parámetro lambda), otras, porque genera un código de bytes ligeramente menor (aunque no hay suficiente diferencia para ser realmente relevante).
Holger

¡Esto es realmente genial! Pero, ¿por qué es .classnecesario? no es isInstanceparte de Object? ¿Es Classuna clase en Java?
Publicación propia el

@PostSelf De hecho, es y ScheduleIntervalContainerno sería realmente una instancia.
Naman

15

Hay un pequeño problema con la solución @ Eran : escribir el nombre de la clase en ambos filtery mapes propenso a errores: es fácil olvidarse de cambiar el nombre de la clase en ambos lugares. Una solución mejorada sería algo como esto:

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());   

Sin embargo, puede haber una penalización de rendimiento al crear un Streampara cada elemento coincidente. Tenga cuidado de usarlo en grandes conjuntos de datos. Aprendí esta solución de @ Tagir Vailev


En este enfoque debes encargarte de NullPointerExceptions, ya select(A.class)que devolverá nullcualquier cosa que no sea un A. Agregar .filter(Objects::nonNull)ayudaría. Por cierto: el enfoque de @Eran es seguro para nulos.
Lars Gendner

Lo siento, mi mal ... JavaDoc de flatMapdice "Si una secuencia asignada es nula, se usa una secuencia vacía". Entonces su solución fue correcta, incluso sin el filtro nulo.
Lars Gendner

3
Aún (en mi opinión) es extraño volver nullcuando podría haber devuelto una transmisión vacía
Alowaniak
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.