java - secuencia - manual de programacion android pdf
Agregar dos flujos de Java 8 o un elemento adicional a una secuencia (7)
Puedo agregar secuencias o elementos adicionales, como este:
Stream stream = Stream.concat(stream1, Stream.concat(stream2, Stream.of(element));
Y puedo agregar cosas nuevas a medida que avanzo, así:
Stream stream = Stream.concat(
Stream.concat(
stream1.filter(x -> x!=0), stream2)
.filter(x -> x!=1),
Stream.of(element))
.filter(x -> x!=2);
Pero esto es feo, porque concat
es estático. Si concat
fuera un método de instancia, los ejemplos anteriores serían mucho más fáciles de leer:
Stream stream = stream1.concat(stream2).concat(element);
Y
Stream stream = stream1
.filter(x -> x!=0)
.concat(stream2)
.filter(x -> x!=1)
.concat(element)
.filter(x -> x!=2);
Mi pregunta es:
1) ¿Hay alguna buena razón por la cual el concat
es estático? ¿O hay algún método de instancia equivalente que me falta?
2) En cualquier caso, ¿hay una mejor manera de hacer esto?
¿Qué le parece escribir su propio método concat?
public static Stream<T> concat(Stream<? extends T> a,
Stream<? extends T> b,
Stream<? extends T> args)
{
Stream<T> concatenated = Stream.concat(a, b);
for (Stream<T> stream : args)
{
concatenated = Stream.concat(concatenated, stream);
}
return concatenated;
}
Esto al menos hace que tu primer ejemplo sea mucho más legible.
Desafortunadamente, esta respuesta probablemente sea de poca o ninguna ayuda, pero hice un análisis forense de la lista de envío Java Lambda para ver si podía encontrar la causa de este diseño. Esto es lo que descubrí.
Al principio había un método de instancia para Stream.concat (Stream)
En la lista de correo puedo ver claramente que el método se implementó originalmente como un método de instancia, como puede leer en este hilo de Paul Sandoz, sobre la operación concat.
En él, discuten los problemas que podrían surgir de aquellos casos en los que el flujo podría ser infinito y qué concatenación significaría en esos casos, pero no creo que esa sea la razón de la modificación.
En este otro hilo se ve que algunos usuarios iniciales del JDK 8 cuestionaron el comportamiento del método de instancia concat cuando se usa con argumentos nulos.
Este otro hilo revela, sin embargo, que el diseño del método concat estaba en discusión.
Refactorizado a Streams.concat (Stream, Stream)
Pero sin ninguna explicación, de repente, los métodos se cambiaron a métodos estáticos, como se puede ver en este hilo sobre la combinación de secuencias . Este es quizás el único hilo de correo que arroja un poco de luz sobre este cambio, pero no fue lo suficientemente claro como para determinar el motivo de la refactorización. Pero podemos ver que hicieron una confirmación en la que sugirieron mover el método concat
de Stream
y pasar a la clase de ayuda Streams
.
Refactorizado a Stream.concat (Stream, Stream)
Más tarde, se movió nuevamente de Streams
a Stream
, pero una vez más, no hay explicación para eso.
Entonces, en conclusión, el motivo del diseño no está del todo claro para mí y no pude encontrar una buena explicación. Supongo que todavía puedes hacer la pregunta en la lista de correo.
Algunas alternativas para la concatenación de flujo
Este otro hilo de Michael Hixson discute / pregunta sobre otras formas de combinar / concatenar flujos
Para combinar dos flujos, debo hacer esto:
Stream.concat(s1, s2)
no esta:
Stream.of(s1, s2).flatMap(x -> x)
... ¿derecho?
Para combinar más de dos flujos, debo hacer esto:
Stream.of(s1, s2, s3, ...).flatMap(x -> x)
no esta:
Stream.of(s1, s2, s3, ...).reduce(Stream.empty(), Stream::concat)
... ¿derecho?
Mi biblioteca StreamEx amplía la funcionalidad de Stream API. En particular, ofrece métodos como append y prepend que resuelven este problema (internamente usan concat
). Estos métodos pueden aceptar otra secuencia o colección o matriz varargs. Usando mi biblioteca tu problema puede ser resuelto de esta manera (nótese que x != 0
parece extraño para un flujo no primitivo):
Stream<Integer> stream = StreamEx.of(stream1)
.filter(x -> !x.equals(0))
.append(stream2)
.filter(x -> !x.equals(1))
.append(element)
.filter(x -> !x.equals(2));
Por cierto, también hay un atajo para su operación de filter
:
Stream<Integer> stream = StreamEx.of(stream1).without(0)
.append(stream2).without(1)
.append(element).without(2);
Puedes usar los Streams de Guava. método concat (Stream ... streams) , que será muy corto con importaciones estáticas:
Stream stream = concat(stream1, stream2, of(element));
Si agrega importaciones estáticas para Stream.concat y Stream.of , el primer ejemplo podría escribirse de la siguiente manera:
Stream<Foo> stream = concat(stream1, concat(stream2, of(element)));
Es una mala idea importar métodos estáticos con nombres, que se usan para diferentes propósitos. Por lo tanto, podría ser mejor crear sus propios métodos estáticos con nombres más significativos. Sin embargo, para la demostración, me quedaré con este nombre.
public static <T> Stream<T> concat(Stream<? extends T> lhs, Stream<? extends T> rhs) {
return Stream.concat(lhs, rhs);
}
public static <T> Stream<T> concat(Stream<? extends T> lhs, T rhs) {
return Stream.concat(lhs, Stream.of(rhs));
}
Con estos dos métodos estáticos (opcionalmente en combinación con importaciones estáticas), los dos ejemplos podrían escribirse de la siguiente manera:
Stream<Foo> stream = concat(stream1, concat(stream2, element));
Stream<Foo> stream = concat(
concat(stream1.filter(x -> x!=0), stream2).filter(x -> x!=1),
element)
.filter(x -> x!=2);
El código ahora es significativamente más corto. Sin embargo, estoy de acuerdo en que la legibilidad no ha mejorado. Entonces tengo otra solución.
En muchas situaciones, los recopiladores se pueden usar para ampliar la funcionalidad de las transmisiones. Con los dos recopiladores en la parte inferior, los dos ejemplos podrían escribirse de la siguiente manera:
Stream<Foo> stream = stream1.collect(concat(stream2)).collect(concat(element));
Stream<Foo> stream = stream1
.filter(x -> x!=0)
.collect(concat(stream2))
.filter(x -> x!=1)
.collect(concat(element))
.filter(x -> x!=2);
La única diferencia entre su sintaxis deseada y la sintaxis anterior es que debe reemplazar concat (...) con collect (concat (...)) . Los dos métodos estáticos se pueden implementar de la siguiente manera (opcionalmente se utiliza en combinación con importaciones estáticas):
private static <T,A,R,S> Collector<T,?,S> combine(Collector<T,A,R> collector, Function<? super R, ? extends S> function) {
return Collector.of(
collector.supplier(),
collector.accumulator(),
collector.combiner(),
collector.finisher().andThen(function));
}
public static <T> Collector<T,?,Stream<T>> concat(Stream<? extends T> other) {
return combine(Collectors.toList(),
list -> Stream.concat(list.stream(), other));
}
public static <T> Collector<T,?,Stream<T>> concat(T element) {
return concat(Stream.of(element));
}
Por supuesto, hay una desventaja con esta solución que debe mencionarse. collect es una operación final que consume todos los elementos de la secuencia. Además de eso, el colector concat crea una ArrayList intermedia cada vez que se usa en la cadena. Ambas operaciones pueden tener un impacto significativo en el comportamiento de su programa. Sin embargo, si la legibilidad es más importante que el rendimiento , podría ser un enfoque muy útil.
Si no te molesta usar Bibliotecas de terceros, cyclops-react tiene un tipo de transmisión extendida que te permitirá hacer eso a través de los operadores de agregar / anteponer.
Los valores individuales, arrays, iterables, Streams o streams reactivos Publishers se pueden agregar y anexar como métodos de instancia.
Stream stream = ReactiveSeq.of(1,2)
.filter(x -> x!=0)
.append(ReactiveSeq.of(3,4))
.filter(x -> x!=1)
.append(5)
.filter(x -> x!=2);
[Divulgación Soy el desarrollador principal de cyclops-react]
Solo haz:
Stream.of(stream1, stream2, Stream.of(element)).flatMap(identity());
donde identity()
es una importación estática de Function.identity()
.
Concatenar múltiples flujos en una secuencia es lo mismo que acoplar una secuencia.
Sin embargo, desafortunadamente, por alguna razón no hay un método flatten()
en Stream
, por lo que debe usar flatMap()
con la función de identidad.