Equivalentes Java de C # String.Format () y String.Join ()

111

Sé que esta es una pregunta para principiantes, pero ¿hay equivalentes a las operaciones de cadena de C # en Java?

Específicamente, estoy hablando de String.Formaty String.Join.

Omar Kooheji
fuente
Así que hay un String.format pero tendré que enrollar mi propia combinación.
Omar Kooheji
1
Para join (), me gusta esta respuesta: stackoverflow.com/a/6116469/562139
scorpiodawg

Respuestas:

92

El objeto String de Java tiene un formatmétodo (a partir de la 1.5), pero ningún joinmétodo.

Para obtener un montón de métodos de utilidad String útiles que aún no están incluidos, puede usar org.apache.commons.lang.StringUtils .

Grant Wagner
fuente
13
colecciones de google: google-collections.googlecode.com también tiene un Joiner.
Ron
10
Para su información sobre el comentario de Ron, hace un tiempo que google-collections pasó a llamarse Guava.
Kevin Bourrillion
3
Debe actualizar esta respuesta para reflejar que Java 8 presenta un String.join()método.
Duncan Jones
46

String.format . En cuanto a unirse, debe escribir el suyo propio:

 static String join(Collection<?> s, String delimiter) {
     StringBuilder builder = new StringBuilder();
     Iterator<?> iter = s.iterator();
     while (iter.hasNext()) {
         builder.append(iter.next());
         if (!iter.hasNext()) {
           break;                  
         }
         builder.append(delimiter);
     }
     return builder.toString();
 }

Lo anterior proviene de http://snippets.dzone.com/posts/show/91

Allain Lalonde
fuente
6
Más precisamente: StringBuffer para jdk1.4 y posteriores, StringBuilder para jdk1.5 y posteriores, ya que este último no está sincronizado, por lo que es un poco más rápido.
VonC
2
En lugar de dos invocaciones iter.hasNext (), normalmente agrego delímetro y luego "return buf.substring (0, buf.length () - delimeter.length ())".
Vilmantas Baranauskas
2
Puede simplificarlo un poco saliendo temprano para evitar el delimitador: while (true) (add_iter; if (! Iter.hasNext ()) break; add_delim;}
13ren
29

A partir de Java 8, join()ahora está disponible como dos métodos de clase en la clase String. En ambos casos, el primer argumento es el delimitador.

Puede pasar CharSequences individuales como argumentos adicionales :

String joined = String.join(", ", "Antimony", "Arsenic", "Aluminum", "Selenium");
// "Antimony, Arsenic, Alumninum, Selenium"

O puede pasar unIterable<? extends CharSequence> :

List<String> strings = new LinkedList<String>();
strings.add("EX");
strings.add("TER");
strings.add("MIN");
strings.add("ATE");

String joined = String.join("-", strings);
// "EX-TER-MIN-ATE"

Java 8 también agrega una nueva clase StringJoiner, que puede usar así:

StringJoiner joiner = new StringJoiner("&");
joiner.add("x=9");
joiner.add("y=5667.7");
joiner.add("z=-33.0");

String joined = joiner.toString();
// "x=9&y=5667.7&z=-33.0"
qntm
fuente
12

También puede utilizar argumentos variables para cadenas de la siguiente manera:

  String join (String delim, String ... data) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < data.length; i++) {
      sb.append(data[i]);
      if (i >= data.length-1) {break;}
      sb.append(delim);
    }
    return sb.toString();
  }
sgsweb
fuente
4

En cuanto a unirse, creo que esto podría parecer un poco menos complicado:

public String join (Collection<String> c) {
    StringBuilder sb=new StringBuilder();
    for(String s: c)
        sb.append(s);
    return sb.toString();
}

No puedo usar la sintaxis de Java 5 tanto como me gustaría (lo crea o no, he estado usando 1.0.x últimamente), así que puede que esté un poco oxidado, pero estoy seguro de que el concepto es correcto .

adición de edición: las adiciones de cadenas pueden ser lentas, pero si está trabajando en un código GUI o alguna rutina de ejecución corta, realmente no importa si toma .005 segundos o .006, por lo que si tenía una colección llamada "joinMe" que desea agregar a un "destino" de cadena existente, no sería horrible simplemente incluir esto:

for(String s : joinMe)
    target += s;

Es bastante ineficiente (y un mal hábito), pero no es nada que pueda percibir a menos que haya miles de cadenas o esto esté dentro de un bucle enorme o su código sea realmente crítico para el rendimiento.

Más importante aún, es fácil de recordar, breve, rápido y muy legible. El rendimiento no siempre es el ganador automático en las opciones de diseño.

Bill K
fuente
El bucle for es correcto, pero también debería convertirlo en una Colección <String>. Si no lo hace, tendría que decir "for (Object o: c)", porque no puede garantizar que todo en c sea una cadena.
Michael Myers
Buen punto, estoy aún más oxidado con los genéricos. Lo editaré en.
Bill K
Cuando se concatena en línea: cadena + cadena + cadena, el compilador realmente usa StringBuilder para agregar los valores. Me pregunto si en el método de bucle for tienes el segundo allí, si el compilador haría lo mismo.
Spencer Kormos
@Spencer K: Utiliza un StringBuilder, pero crea uno nuevo para cada iteración (no es el método más eficiente, pero ¿cómo se supone que debe saber el compilador?). No sé si el compilador JIT podría optimizar eso en tiempo de ejecución.
Michael Myers
2
Oh espera. ¿Estás hablando + =? Sí, eso apesta. El bucle y el anexo actualmente en mi respuesta usan StringBuilder y eso es de lo que estoy hablando. Aunque + = ha mejorado mucho, realmente no desea usarlo dentro de un bucle, no tanto porque tenga errores, sino porque puede funcionar como una mierda (Bug no es un término que se usa generalmente para problemas de rendimiento, al menos no en mis 20 años de programación). Además, el JIT puede mejorar esto enormemente, pero no confiaría en eso.
Bill K
4

Aquí hay una respuesta bastante simple. Úselo +=ya que es menos código y deje que el optimizador lo convierta a StringBuilderpor usted. Con este método, no tiene que hacer ninguna comprobación "es la última" en su ciclo (mejora del rendimiento) y no tiene que preocuparse por eliminar los delimitadores al final.

        Iterator<String> iter = args.iterator();
        output += iter.hasNext() ? iter.next() : "";
        while (iter.hasNext()) {
            output += "," + iter.next();
        }
Encadenar
fuente
1
¡Solución muy elegante, que no involucra bibliotecas de terceros!
Denis Itskovich
2

No quería importar una biblioteca de Apache completa para agregar una función de unión simple, así que aquí está mi truco.

    public String join(String delim, List<String> destinations) {
        StringBuilder sb = new StringBuilder();
        int delimLength = delim.length();

        for (String s: destinations) {
            sb.append(s);
            sb.append(delim);
        }

        // we have appended the delimiter to the end 
        // in the previous for-loop. Let's now remove it.
        if (sb.length() >= delimLength) {
            return sb.substring(0, sb.length() - delimLength);
        } else {
            return sb.toString();
        }
    }
Martin Konecny
fuente
@MrSnowflake ¿El generador de cadenas estándar tiene un método "removeCharAt (int index)"? No aparece en la versión que estoy ejecutando
NSjonas
@NSjonas: sí, su edición de mi respuesta es incorrecta. El removeCharAtno existe, y la función completa ya no devuelve un String ... Arreglará esto.
Martin Konecny
mientras está en eso ... esta solución actual arrojará una "excepción de índice fuera de los límites si pasa una lista vacía"
NSjonas
hizo una edición para recortar la longitud correcta si el delimitador tiene más de 1 carácter. También se solucionó un problema en el que estaba recortando el último carácter y no el primero como realmente lo necesitaba
NSjonas
Gracias por los comentarios. Actualizado.
Martin Konecny
1

Si desea unir (concatenar) varias cadenas en una, debe usar un StringBuilder. Es mucho mejor que usar

for(String s : joinMe)
    target += s;

También hay una ligera ventaja de rendimiento sobre StringBuffer, ya que StringBuilder no usa la sincronización.

Para un método de utilidad de propósito general como este, (eventualmente) se llamará muchas veces en muchas situaciones, por lo que debe hacerlo eficiente y no asignar muchos objetos transitorios. Hemos perfilado muchas, muchas aplicaciones Java diferentes y casi siempre encontramos que la concatenación de cadenas y las asignaciones de cadenas / caracteres [] consumen una cantidad significativa de tiempo / memoria.

Nuestra colección reutilizable -> método string primero calcula el tamaño del resultado requerido y luego crea un StringBuilder con ese tamaño inicial; esto evita la duplicación / copia innecesaria del carácter interno [] que se usa al agregar cadenas.

djb
fuente
re: cálculo previo del tamaño: cuando funciona, es genial. No siempre es posible. Recuerdo haber leído en algún lugar que duplicar el tamaño del búfer cada vez (o realmente multiplicarlo por cualquier factor fijo) da algo como un rendimiento de n log n, que en realidad no es tan malo. En el caso general, la alternativa es hacer una lista de todas las cadenas que planea concatenar. Si usa un ArrayList, tendrá que copiar nuevamente (a menos que sepa la longitud de su secuencia de antemano) y si usa LinkedList, entonces usa más ram y recolección de basura con los objetos de nodo. A veces no puedes ganar. ¡Inténtalo!
Ian
Los ejemplos / consultas anteriores asumieron alguna colección ordenada o matriz de cadenas para unirse. Además, al calcular previamente el tamaño, evita caracteres adicionales no utilizados en los que el crecimiento de la matriz char [] interna suele
producir
1

Escribí propio:

public static String join(Collection<String> col, String delim) {
    StringBuilder sb = new StringBuilder();
    Iterator<String> iter = col.iterator();
    if (iter.hasNext())
        sb.append(iter.next().toString());
    while (iter.hasNext()) {
        sb.append(delim);
        sb.append(iter.next().toString());
    }
    return sb.toString();
}

pero Collectionno es compatible con JSP, por lo que para la función de etiqueta escribí:

public static String join(List<?> list, String delim) {
    int len = list.size();
    if (len == 0)
        return "";
    StringBuilder sb = new StringBuilder(list.get(0).toString());
    for (int i = 1; i < len; i++) {
        sb.append(delim);
        sb.append(list.get(i).toString());
    }
    return sb.toString();
}

y .tldarchivar:

<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.1" xmlns="http://java.sun.com/xml/ns/javaee"
    <function>
        <name>join</name>
        <function-class>com.core.util.ReportUtil</function-class>
        <function-signature>java.lang.String join(java.util.List, java.lang.String)</function-signature>
    </function>
</taglib>

y utilícelo en archivos JSP como:

<%@taglib prefix="funnyFmt" uri="tag:com.core.util,2013:funnyFmt"%>
${funnyFmt:join(books, ", ")}
Gavenkoa
fuente
0

Veo muchas implementaciones demasiado complejas de String. Únete aquí. Si no tiene Java 1.8 y no desea importar una nueva biblioteca, la siguiente implementación debería ser suficiente.

public String join(Collection<String> col, String delim) {
    StringBuilder sb = new StringBuilder();
    for ( String s : col ) {
        if ( sb.length() != 0 ) sb.append(delim);
        sb.append(s);
    }
    return sb.toString();
}
Brandon Dutton
fuente
-1
ArrayList<Double> j=new ArrayList<>; 
j.add(1);
j.add(.92);
j.add(3); 
String ntop=j.toString(); //ntop= "[1, 0.92, 3]" 

Básicamente, String ntop almacena el valor de toda la colección con separadores de coma y corchetes.

Edward Karak
fuente
1
No estoy seguro de qué parte de la pregunta responde esto. No es un String.format a menos que planee agregar los bits de la cadena bit a bit a la matriz, y [1,0.92,3] no es tan versátil como una cadena genérica .join.
Omar Kooheji
-8

Solo usaría el operador de concatenación de cadenas "+" para unir dos cadenas. s1 += s2;

Amir Bashir
fuente
Porque es una mala práctica y lenta.
MrSnowflake