logo java lambda functional-programming java-8 java-stream

java - logo - apache hbase download



¿Por qué java.util.Collection no implementa la nueva interfaz de Stream? (1)

Me tomé un tiempo para empezar a investigar el zumbido de java-8 acerca de las transmisiones y las lambdas. Lo que me sorprendió es que no puede aplicar las operaciones de Stream, como .map() , .filter() directamente en java.util.Collection . ¿Existe alguna razón técnica por la que la interfaz java.util.Collection no se haya ampliado con las implementaciones predeterminadas de estas operaciones de Stream?

Buscando en Google un poco, veo muchos ejemplos de personas que codifican según el patrón de:

List<String> list = someListExpression; List<String> anotherList = list.stream().map(x -> f(x)).collect(Collectors.toList());

que se torna muy torpe, si tienes muchas de estas operaciones de flujo en tu código. Como .stream() y .collect() son completamente irrelevantes para lo que desea expresar, le .collect() que dijera:

List<String> list = someListExpression; List<String> anotherList = list.map(x -> f(x));


Sí, hay excelentes razones para estas decisiones :)

La clave es la diferencia entre las operaciones impacientes y perezosas . Los ejemplos que da en la primera pregunta muestran operaciones ansiosas donde mapear o filtrar una lista produce una nueva lista. No hay nada de malo en esto, pero a menudo no es lo que quieres, porque a menudo trabajas mucho más de lo que necesitas; una operación ansiosa debe operar en cada elemento y producir una nueva colección. Si está componiendo varias operaciones (filter-map-reduce), está haciendo mucho trabajo extra. Por otro lado, las operaciones perezosas componen maravillosamente; si lo haces:

Optional<Person> tallestGuy = people.stream() .filter(p -> p.getGender() == MALE) .max(comparing(Person::getHeight));

las operaciones de filtro y reducción (máximo) se fusionan en una sola pasada. Esto es muy eficiente.

Entonces, ¿por qué no exponer los métodos de Stream directamente en List? Bueno, lo intentamos así. Entre muchas otras razones, encontramos que mezclar métodos perezosos como filter() y métodos ansiosos como removeAll() era confuso para los usuarios. Al agrupar los métodos perezosos en una abstracción separada, se vuelve mucho más claro; los métodos en List son aquellos que mutan la lista; los métodos en Stream son aquellos que se ocupan de operaciones perecederas y composibles en secuencias de datos, independientemente de dónde vivan esos datos.

Entonces, la forma en que lo sugieres es genial si quieres hacer cosas realmente simples, pero comienza a desmoronarse cuando tratas de construir sobre él. ¿El método extra stream() es molesto? Por supuesto. Pero mantener las abstracciones para las estructuras de datos (que en gran medida se trata de organizar los datos en la memoria) y los flujos (que en gran medida se trata de componer el comportamiento agregado) separan las escalas mejor que las operaciones más sofisticadas.

Para su segunda pregunta, puede hacerlo de manera relativamente fácil: implemente los métodos de transmisión de esta manera:

public<U> Stream<U> map(Function<T,U> mapper) { return convertToStream().map(mapper); }

Pero eso es solo nadar contra la corriente; es mejor implementar un método stream () eficiente.