¿Es sensato devolver Streams donde normalmente devolvemos Colecciones?

19

Mientras desarrollo mi API que no está vinculada a ningún código heredado, a menudo me encuentro escribiendo métodos que son puramente canales terminados mediante la recopilación de los resultados. Como éste:

ImmutableSet<T> deriveSomethingMeaningfulFromPrivateState() {
    return myPrivateThingies.stream()
        .map(this::ownerOfThing)
        .map(Owner::socialStatus)
        .filter(SocialStatus::isHeAFineMatey)
        .collect(MyCustomCollectors.toImmutableSet());
}

Ahora, la mayoría de los clientes de esta clase generalmente necesitarán la Colección (en este caso, un ImmutableSet) para buscar elementos e iterar sobre ella, pero algunos clientes pueden beneficiarse de tener un Stream para poder canalizar algunas operaciones más. Transmita sin la necesidad de obtener una nueva transmisión de la Colección. Por lo tanto, devolver un Stream les da a los clientes un superconjunto de opciones que tendrían si solo tuvieran la Colección (después de todo, siempre pueden ellos collect()mismos:

Stream<T> deriveSomethingMeaningfulFromPrivateState() {
    return myPrivateThingies.stream()
        .map(this::ownerOfthing)
        .map(Owner::socialStatus)
        .filter(SocialStatus::isHeAFineMatey);
        // No collect
}

Este enfoque es tentador para mí, ya que no veo ningún defecto potencial que pueda tener. Sin embargo, nunca he visto este enfoque en ninguna biblioteca (probablemente porque no se lanzaron muchas bibliotecas después de la aparición de Java 8), por lo que me da un poco de miedo adoptarlo. Las clases de biblioteca existentes generalmente devuelven colecciones cuando derivan algo del estado privado.

¿Hay algo malo que pueda suceder si decido devolver un Stream donde mi yo anterior a Java-8 devolvería una Colección? ¿O probablemente estoy haciendo algo así como un antipatrón aquí con todo lo que deriva del estado privado?

jojman
fuente

Respuestas:

14

Si myPrivateThingieses mutable, ha creado una dependencia oculta entre su estado privado y los resultados de la transmisión. Si es posible que el cliente provoque indirectamente myPrivateThingiesun cambio de estado, obtendrá un resultado diferente al llamar collectque el que originalmente pretendía dar.

Si myPrivateThingieses inmutable, el resultado será referencialmente transparente, pero hay un problema más que debe tener en cuenta: la basura semántica , es decir, conservar grandes cantidades de memoria que ya no son necesarias. Supongamos que myPrivateThingieses muy grande y el resultado de recopilar la secuencia es pequeño. El cliente puede retener el flujo mucho después de haber descartado todas las referencias al objeto que lo produjo, pero eso streamsigue evitando que se myPrivateThingiesrecolecte basura. Recoger los resultados con entusiasmo permitiría myPrivateThingiesser liberado.

Esto realmente sucedió antes de Java 7 al llamar substring. Oracle decidió que el ahorro potencial de eficiencia al no copiar la subcadena cada vez no vale la pena sorprender ocasionalmente al usuario promedio con un consumo excesivo de memoria. Eso no quiere decir que no hubo casos de uso reales para el comportamiento anterior (p. Ej. Analizadores), pero a menudo recopilar los resultados con entusiasmo es lo suficientemente rápido, y cuando eso sucede, no tiene ventajas ni desventajas potenciales.

Por otro lado, devolver un flujo le da al cliente la capacidad de elegir qué estructura de datos desea usar para mantener los resultados, en lugar de que usted elija uno para él. Puede valer la pena ofrecer ambas opciones.

Doval
fuente
4

Lo más importante a tener en cuenta: Streams solo se puede iterar una vez, mientras que tiene más flexibilidad sobre a Collection: puede continuar creando más Streams o incluso Iterators para realizar un procesamiento repetitivo adicional en los resultados.

Entonces, si no está seguro de si las personas que llaman del método van a usar los resultados una vez y solo una vez, es mejor devolver a Collection.


Su código de muestra tiene un error obvio: ¿por qué a SocialStatustiene el concepto de persona he?

hjk
fuente
3

En mi opinión, no. Las cosas que puede hacer con las transmisiones son un superconjunto estricto de las cosas que puede hacer con las colecciones, y a menudo pueden hacerse más eficientes, por lo que no hay razón para no usarlas, excepto la falta de familiaridad. "Las expresiones Lambda son la droga de entrada a Java 8, pero las transmisiones son la verdadera adicción". (Venkat Subramaniam, Programación funcional en Java )

Kilian Foth
fuente