streams procesamiento print parte ejemplo datos con and java functional-programming java-8 java-stream

procesamiento - stream java 8 ejemplo



Java 8 streams y mapas que valen la pena? (8)

Aquí hay una solución más corta por StreamEx

StreamEx.of(model.getDeclaredFields()) .filter(field -> !Modifier.isStatic(field.getModifiers())) .map(DartField::getDartField) .toList();

Creo que es más corto / simple, en comparación con el bucle original para.

List<DartField> fields = new ArrayList<>(); for (Field field : model.getDeclaredFields()) { if (!Modifier.isStatic(field.getModifiers())) { fields.add(DartField.getDartField(field)); } } return fields;

Lo más importante es más flexible. Solo piense si desea hacer más filtro / mapa, u ordenar / limitar / agrupar Por / ..., solo necesita agregar más llamadas a la API de flujo continuo, y el código aún debe ser conciso, el bucle anidado para el bucle / si es que será más más complicado.

Parece que java 8 streams y las funciones de mapeo son tan detalladas que no son realmente una mejora. Por ejemplo, escribí un código que usa una colección para generar otra colección modificada:

private List<DartField> getDartFields(Class<?> model) { List<DartField> fields = new ArrayList<>(); for (Field field : model.getDeclaredFields()) { if (!Modifier.isStatic(field.getModifiers())) { fields.add(DartField.getDartField(field)); } } return fields; }

Este parece ser el caso de uso ideal para java 8 streams y sus funciones, así que lo reescribí así:

private List<DartField> getDartFields(Class<?> model) { return Arrays.asList(model.getDeclaredFields()) .stream() .filter(field -> !Modifier.isStatic(field.getModifiers())) .map(field -> DartField.getDartField(field)) .collect(Collectors.toList()); }

Pero no estoy seguro de que me guste más. Tiene 236 caracteres en comparación con 239 en Java de estilo normal. No parece más o menos legible. Es bueno que no tenga que declarar una ArrayList , pero la necesidad de llamar a .collect(Collectors.toList()) y Arrays.asList (dependiendo del tipo de datos) no es mejor.

¿Hay alguna mejora práctica en el uso de .stream() como esta que simplemente no entiendo, o es simplemente una forma divertida de lanzar a cualquier compañero de trabajo para un bucle que no conoce la programación funcional?

Supongo que si pasara dinámicamente alrededor de las lambdas de filtro o mapa sería útil, pero si no necesita hacer eso ...


Con Java 8, el equipo tomó un lenguaje de programación orientado a objetos y aplicó la "Objectificación" para producir programación orientada a objetos funcionales (lol ... FOOP). Tomará algún tiempo acostumbrarse a esto, pero sostengo que cualquier manipulación jerárquica de los objetos debe permanecer en su estado funcional . Desde esta perspectiva, Java siente que cierra la brecha de PHP; Permita que los datos existan en su estado natural y moldéelos en la GUI de la aplicación.

Esta es la verdadera filosofía detrás de una creación de API desde una perspectiva de Ingeniería de Software.


Desde mi perspectiva, las API de java stream (map, filter, forEach, groupBy ...) realmente facilitan el manejo de datos en el proceso de desarrollo diario. En lugar de ensuciarse las manos, simplemente le dice a las API de transmisión lo que quiere y no cómo hacerlo.

Sin embargo, no me siento cómodo al leer los códigos java que se rellenan con varias API de secuencias relacionadas. A veces, está muy conectado cuando se utilizan las API de transmisión en el formato y diseño del código, especialmente junto con el programa de funciones. En breve, se degrada la legibilidad.


El problema es que no estás usando la API de Stream consistente . Está restringiendo el caso de uso a algo que puede describirse mejor como "en realidad no usar la API de Stream " ya que está insistiendo en devolver una Collection . Eso es especialmente absurdo, ya que es un método private , por lo que también puede adaptar a las personas que llaman.

Considera cambiar el método a

private Stream<DartField> getDartFields(Class<?> model) { return Stream.of(model.getDeclaredFields()) .filter(field -> !Modifier.isStatic(field.getModifiers())) .map(field -> DartField.getDartField(field)); }

y mira lo que las personas que llaman realmente quieren hacer. Por lo general, no necesitan una Collection como un fin en sí mismo, pero desean realizar una acción o incluso más operaciones que podrían encadenarse, por ejemplo, imprimirlas:

getDartFields(Foo.class).forEach(System.out::println);

La característica más interesante es la naturaleza perezosa del flujo, lo que implica que en el retorno de getDartFields , aún no se ha realizado ninguna acción y si utiliza operaciones como findFirst , no es necesario procesar todos los elementos. Perderás esta característica si devuelves una Collection contiene todos los elementos.

Esto también se aplica al procesamiento de varios pasos donde el procesamiento de listas ordinarias implica que para cada paso se debe crear una nueva lista y rellenar con los resultados.


Los flujos de Java 8 son particularmente detallados, principalmente debido a la conversión a un flujo y luego a otra estructura. En FunctionalJava, el equivalente es:

private List<DartField> getDartFields(Class<?> model) { return List.list(model.getDeclaredFields()) .filter(field -> !Modifier.isStatic(field.getModifiers())) .map(field -> DartField.getDartField(field)) .toJavaList(); }

Advierto contra el simple hecho de contar caracteres como una medida de complejidad. Esto apenas importa.

La programación funcional le permite razonar acerca de su código usando un modelo de sustitución simple, en lugar de tener que rastrear todo su programa. Esto hace que su programa sea más predecible y más fácil porque necesita menos información en su mente al mismo tiempo.

También advierto contra las corrientes que regresan. Los flujos no se pueden componer arbitrariamente, los flujos son datos mutables donde los llamantes no tienen forma de saber si se ha llamado a una operación de terminal en el flujo. Esto significa que necesitamos saber el estado del programa para razonar sobre lo que está sucediendo. Las secuencias se introdujeron para ayudar a eliminar el estado mutable, pero se implementan utilizando un estado mutable, lejos de lo ideal.

Si desea un flujo inmutable, recomiendo el flujo de Functional Java, https://functionaljava.ci.cloudbees.com/job/master/javadoc/fj/data/Stream.html .


No vale la pena si insistes en volver a las colecciones. Sin embargo, ha perdido una oportunidad: considere lo siguiente y debería ver dónde el uso de secuencias agrega un nivel de flexibilidad y capacidad de composición a su código:

private static final Predicate<Field> isStatic = field -> !Modifier.isStatic(field.getModifiers()); private Stream<Field> getDeclaredFields(Class<?> model) { return Stream.of(model.getDeclaredFields()); } private Stream<Field> getStaticFields(Class<?> model) { return getDeclaredFields(model).filter(isStatic); } private Stream<DartField> getDartFields(Class<?> model) { return getStaticFields(model) .map(field -> DartField.getDartField(field)); }

El punto es que puede usar flujos como colecciones en lugar de mecanismos para construir nuevas colecciones.

Al permitir que todos los métodos naturales simplemente caigan fuera del algoritmo, terminará con un código obvio que es casi inevitablemente reutilizable y cada componente naturalmente hace su única cosa.


Puedes escribirlo de manera diferente (no necesariamente mejor)

private List<DartField> getDartFields(Class<?> model) { return Stream.of(model.getDeclaredFields()) .filter(field -> !Modifier.isStatic(field.getModifiers())) .map(DartField::getDartField) .collect(Collectors.toList()); }

Usando importaciones estáticas esto parece

private static List<DartField> getDartFields(Class<?> model) { return of(model.getDeclaredFields()) .filter(field -> !isStatic(field.getModifiers())) .map(DartField::getDartField) .collect(toList()); }

No parece más o menos legible.

Este es a menudo el caso IMHO. Sin embargo, diría que en> 10% de los casos es significativamente mejor. Al igual que con cualquier característica nueva, es probable que la use en exceso para comenzar hasta que se familiarice con ella y descubra que la usa con la cantidad con la que se sienta cómodo.

¿Hay alguna mejora práctica en el uso de .stream () como esta que simplemente no entiendo, o es simplemente una forma divertida de lanzar a cualquier compañero de trabajo para un bucle que no conoce la programación funcional?

Sospecho de ambos. Si no conoce la programación funcional, tiende a ser un código de solo lectura. es decir, todavía puedes entender lo que hace, el problema es si tienes que mantenerlo.

En mi humilde opinión, vale la pena animar a los desarrolladores a aprender programación funcional, ya que tiene algunas ideas muy útiles sobre cómo estructurar su código y usted se beneficiaría de ello incluso si no usara la sintaxis de PF.

Donde la API de Streams es útil en construcciones que previamente no habría molestado en implementar.

Por ejemplo, digamos que quiere indexar el campo por nombre.

private static Map<String, DartField> getDartFields(Class<?> model) { return of(model.getDeclaredFields()) .filter(field -> !isStatic(field.getModifiers())) .map(DartField::getDartField) .collect(groupingBy(f -> f.getName())); }

En el pasado, es posible que haya usado una Lista en lugar de un Mapa, pero al facilitar el montaje del Mapa, podría usar la estructura de datos que realmente debería usar más a menudo.

Ahora veamos si sería más rápido si usáramos más hilos.

private static Map<String, DartField> getDartFields(Class<?> model) { return of(model.getDeclaredFields()).parallel() .filter(field -> !isStatic(field.getModifiers())) .map(DartField::getDartField) .collect(groupingByConcurrent(f -> f.getName())); }

Ver cuán difícil fue eso, y cambiarlo de nuevo cuando lo encuentre probablemente haga más daño que bien, también es bastante fácil.


Si restringe específicamente su caso de uso a solo lo que ha publicado, entonces un lenguaje basado en Stream no es sustancialmente mejor. Sin embargo, si está interesado en saber dónde la API de Streams es un verdadero beneficio, aquí hay algunos puntos:

  • el lenguaje basado en Stream se puede conectar en paralelo sin que sea virtualmente un esfuerzo de su parte (esta es en realidad la razón más importante por la que Java obtuvo lambdas en primer lugar);
  • Las secuencias se pueden componer: puede pasarlas y agregar etapas de tubería. Esto puede beneficiar enormemente la reutilización del código;
  • como ya se ha señalado, también puede pasar lambdas: es fácil escribir métodos de plantilla donde se conecta solo un aspecto del procesamiento;
  • Una vez que se siente cómodo con el idioma, el código FP es realmente más legible ya que está más estrechamente relacionado con el qué en lugar del cómo . Esta ventaja aumenta con la complejidad de la lógica de procesamiento.

Además, me gustaría señalar que la diferencia en la legibilidad es más un artefacto histórico que intrínseco a los modismos: si a los desarrolladores se les enseñara FP desde el principio y se trabajara con ellos día a día, sería el imperativo lo que era extraño y difícil seguir.