La forma más sencilla de imprimir un `IntStream` como `String`

7 minutos de lectura

avatar de usuario
viejocascarrabias

Con Java-8 puedo tratar fácilmente un String (o cualquier CharSequence) como un IntStream usando el chars o el codePoints método.

IntStream chars = "Hello world.".codePoints();

Entonces puedo manipular el contenido de la transmisión.

IntStream stars = chars.map(c -> c == ' ' ? ' ': '*');

He estado buscando una manera ordenada de imprimir los resultados y ni siquiera puedo encontrar una manera simple. ¿Cómo pongo este flujo de ints de nuevo en un formulario que se puede imprimir como yo puedo un String.

de lo anterior stars espero imprimir

***** ******

  • Por cierto, esta fue una etapa en el desarrollo de esta respuesta. Agradecería comentarios allí.

    – Viejo Cascarrabias

    28 de noviembre de 2013 a las 16:11

avatar de usuario
Holger

String result = "Hello world."
  .codePoints()
//.parallel()  // uncomment this line for large strings
  .map(c -> c == ' ' ? ' ': '*')
  .collect(StringBuilder::new,
           StringBuilder::appendCodePoint, StringBuilder::append)
  .toString();

Pero aún, "Hello world.".replaceAll("[^ ]", "*") es más simple No todo se beneficia de las lambdas.

  • Estoy mucho más feliz por eso. Tomo su punto sobre el uso excesivo de lambdas: mi ejemplo fue solo un ejemplo.

    – Viejo Cascarrabias

    28 de noviembre de 2013 a las 14:45

  • Lo sé, no pude resistirme. Incluso una transformación de personaje más compleja podría beneficiarse de la ejecución paralela…

    – Holger

    28 de noviembre de 2013 a las 14:52

  • @GeorgiosPligoropoulos, este no es el lugar adecuado para discutir qué debería ser Java o no.

    – Holger

    29 de agosto de 2019 a las 15:56

  • @Holger Creo que ya es hora de llevar todos los principios de la experiencia del usuario a los lenguajes de programación porque si lo ves desde una perspectiva diferente, los usuarios son usuarios del producto y los programadores siguen siendo usuarios pero de lenguajes de programación, API, bibliotecas, etc. y todos estos deben estar en forma para minimizar la carga mental en lugar de… ¡esto!

    – George Pligoropoulos

    30 de agosto de 2019 a las 18:04

  • @GeorgiosPligoropoulos Stackoverflow todavía no es el lugar adecuado para tales discusiones

    – Holger

    2 de septiembre de 2019 a las 7:03

avatar de usuario
Lukasz Wiktor

Una solución un poco menos eficiente pero más concisa a la de Holger:

String result = "Hello world."
    .codePoints()
    .mapToObj(c -> c == ' ' ? " ": "*")
    .collect(Collectors.joining());

Collectors.joining() usos internos StringBuilderal menos en Fuentes de OpenJDK.

  • ¡Agradable! Gracias por eso, ciertamente un poco más ordenado.

    – Viejo Cascarrabias

    9 mayo 2014 a las 12:55

  • ¿Por qué sería esto menos eficiente?

    -Maarten Bodewes

    21 de noviembre de 2016 a las 23:10

  • @MaartenBodewes porque convierte cada carácter en String antes de unirlos.

    – Lukasz Wiktor

    22 de noviembre de 2016 a las 9:37

  • Está bien. ¿Qué opinas de mi solución aquí: stackoverflow.com/a/40731255/589259 No uso transmisiones regularmente.

    -Maarten Bodewes

    22 de noviembre de 2016 a las 9:48

  • customTag = secureRandom.ints(9, 0, chrs.length()).mapToObj(i -> chrs.charAt(i)).collect(Collectors.joining()); produce un error de compilación IDE The method collect(Collector<? super Character,A,R>) in the type Stream<Character> is not applicable for the arguments (Collector<CharSequence,capture#1-of ?,String>)

    – Cloe

    3 abr 2019 a las 20:35

avatar de usuario
Lii

Otras respuestas muestran cómo recopilar una secuencia de cadenas en una sola cadena y cómo recopilar caracteres de un IntStream. Esta respuesta muestra cómo usar un recopilador personalizado en una secuencia de caracteres.

Si desea recopilar un flujo de enteros en una cadena, creo que la solución más limpia y general es crear un método de utilidad estático que devuelva un recopilador. Entonces puedes usar el Stream.collect método como de costumbre.

Esta utilidad se puede implementar y utilizar así:

public static void main(String[] args){
    String s = "abcacb".codePoints()
        .filter(ch -> ch != 'b')
        .boxed()
        .collect(charsToString());

    System.out.println("s: " + s); // Prints "s: acac"
}

public static Collector<Integer, ?, String> charsToString() {
    return Collector.of(
        StringBuilder::new,
        StringBuilder::appendCodePoint,
        StringBuilder::append,
        StringBuilder::toString);
}

Es un poco sorprendente que no haya algo como esto en la biblioteca estándar.

Una desventaja de este enfoque es que requiere que los caracteres estén en cajas ya que el IntStream la interfaz no funciona con los colectores.

Un problema difícil y sin resolver es cuál debería ser el nombre del método de utilidad. La convención para los métodos de utilidad del recopilador es llamarlos toXXXpero toString ya está elegido.

  • mi solucion hace no coleccionar cuerdas pero ints. De hecho, utiliza casi exactamente las mismas operaciones que su solución. Las únicas diferencias son que no requiere boxing, maneja correctamente los puntos de código suplementarios y es más compacto. La razón por la que no hay un colector incorporado para este propósito es exactamente la desventaja que ha mencionado: dado que IntStream no es compatible Collectors, requeriría boxeo. Por otro lado, si no le importa la sobrecarga de boxeo, la solución usando Collectors.joining() sería suficiente.

    – Holger

    27 de mayo de 2015 a las 8:26

  • @Holger: cuando trabaja Collectors.joining tienes que envolver los caracteres en cadenas, lo cual es un poco más caro y no expresa la intención con tanta claridad. Cuando se trabaja con flujos int, no se pueden usar colectores. Esto da cierta justificación al enfoque de coleccionista en caracteres en caja. Y también podría ser una buena idea demostrar una técnica que generalmente es útil.

    – Lii

    27 mayo 2015 a las 9:40

  • Es bueno mostrar enfoques alternativos, aún así su primera oración “Otras respuestas muestran cómo recopilar un flujo de cadenas…” es incorrecta. Para el alcance de la pregunta, no hay diferencia entre boxear y Integer o mapear a Strings. Este último se asigna a uno de dos constante cadenas como en la solución de Lukasz Wiktor y su código utilizará la instancia de la obligatoria Integer caché ya que solo está usando caracteres ASCII. Para caracteres arbitrarios, mapeo a cadenas puede que ser un poco más caro que el boxeo a instancias enteras, pero si te preocupas por el rendimiento, evitarás ambos.

    – Holger

    27 mayo 2015 a las 10:40

  • @Holger: probablemente sea una buena idea corregir la primera oración de la respuesta.

    – Lii

    28 de mayo de 2015 a las 7:51

Hay una respuesta simple que es un poco menos inclinada a hacer todo con transmisión. Por lo tanto, no es de una sola línea, pero probablemente sea más eficiente y muy fácil de leer:

public static String stars(String t) {
    StringBuilder sb = new StringBuilder(t.length());
    t.codePoints().map(c -> c == ' ' ? ' ' : '*').forEach(sb::appendCodePoint);
    return sb.toString();
}

A veces corto no es lo mismo que conciso, no creo que nadie tenga que preguntarse cómo funciona la función anterior.

Esta solución garantiza que los puntos de código nunca se vuelvan a convertir en caracteres. Por lo tanto, es algo más genérico que algunas otras soluciones enumeradas aquí.

avatar de usuario
Maarten Bodewes

Si es necesario, podemos hacer una sola línea de esta manera extremadamente fea:

public static String stars(String t) {
    return t.codePoints().map(c -> c == ' ' ? ' ': '*').mapToObj(i -> new String(new int[] { i }, 0, 1)).collect(Collectors.joining());
}

Realiza exactamente la misma versión que mi otra respuesta, pero utiliza la transmisión en todo momento. Obviamente, se necesita alguna función para convertir un solo punto de código en una cadena:

public static String stars(String t) {
    return t.codePoints().map(c -> c == ' ' ? ' ': '*').mapToObj(Stars::codePointToString).collect(Collectors.joining());
}

private static String codePointToString(int codePoint) {
    return new String(new int[] { codePoint }, 0, 1);
}

que coloca estas funciones en la clase Stars por supuesto.

avatar de usuario
Ahmad

La forma en que lo hago es usando reducir. Llegar * para cada carácter y espacio para cada espacio se verá así

    String hello = "hello world";
    String result = hello.chars()
        .map(val -> (val == ' ') ?  ' ' : '*')
        .mapToObj(val -> String.valueOf((char) val))
        .reduce("",(s1, s2) -> s1+s2);

El punto es hacer lo que quieras con el enteros que echarlo a caracteres luego asignarlo a Cuerda después concatenar eso, puedes usarlo reducir o Coleccionistas.joining()

avatar de usuario
miket

Puede hacerlo directamente con el siguiente código: –

"Hello world".codePoints().forEach(n -> System.out.print(n == ' ' ? ' ':'*'));

  • Cierto, pero creo que la idea general era convertirlo primero en una cadena y luego imprimirlo (la conversión a cadena es el problema subyacente más interesante). Por otra parte, podrías usar un StringWriter instancia, supongo.

    -Maarten Bodewes

    21 de noviembre de 2016 a las 23:12


¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad