Métodos de String parte 2
flowchart TB
Str[String avanzado] --> Split[split regex]
Str --> Repl[replace replaceAll]
Str --> Join[String.join]
Str --> Form[format formatted]
Str --> Repl2[repeat 11]
Str --> Lines[lines stream líneas]
Str --> Ind[indent 12]
Str --> Codp[codePoints]
Str --> Bytes[getBytes UTF 8]
Str --> Chars[chars IntStream]
Métodos de transformación (toUpperCase, replace, split)
La clase String en Java ofrece un conjunto de métodos de transformación que permiten modificar el contenido de las cadenas de texto. Debido a la inmutabilidad de los objetos String, estos métodos no alteran la cadena original, sino que devuelven una nueva cadena con las transformaciones aplicadas.
Conversión de mayúsculas y minúsculas
Los métodos toUpperCase() y toLowerCase() permiten convertir todos los caracteres de una cadena a mayúsculas o minúsculas respectivamente.
String texto = "Java Programming";
String mayusculas = texto.toUpperCase(); // "JAVA PROGRAMMING"
String minusculas = texto.toLowerCase(); // "java programming"
System.out.println(mayusculas);
System.out.println(minusculas);
Estos métodos son útiles para realizar comparaciones sin distinguir entre mayúsculas y minúsculas:
String entrada = "Java";
String busqueda = "java";
// Forma incorrecta (distingue mayúsculas/minúsculas)
if (entrada.equals(busqueda)) {
System.out.println("Coincidencia encontrada");
}
// Forma correcta (no distingue mayúsculas/minúsculas)
if (entrada.toLowerCase().equals(busqueda.toLowerCase())) {
System.out.println("Coincidencia encontrada");
}
También se puede especificar una configuración regional (Locale) para manejar correctamente caracteres específicos de diferentes idiomas:
String texto = "españa";
String mayusculasEspanol = texto.toUpperCase(Locale.forLanguageTag("es-ES")); // "ESPAÑA"
Reemplazo de caracteres y subcadenas
Java ofrece varios métodos para reemplazar caracteres o subcadenas dentro de un String.
replace()
El método replace() tiene dos variantes:
replace(char oldChar, char newChar): Reemplaza todas las ocurrencias de un carácter por otro.replace(CharSequence target, CharSequence replacement): Reemplaza todas las ocurrencias de una subcadena por otra.
String texto = "Hello World";
// Reemplazo de caracteres
String textoModificado = texto.replace('l', 'L'); // "HeLLo WorLd"
// Reemplazo de subcadenas
String nuevoTexto = texto.replace("World", "Java"); // "Hello Java"
replaceFirst() y replaceAll()
Estos métodos utilizan expresiones regulares para realizar reemplazos más avanzados:
String codigo = "var x = 10; var y = 20;";
// Reemplaza solo la primera ocurrencia
String primerReemplazo = codigo.replaceFirst("var", "let"); // "let x = 10; var y = 20;"
// Reemplaza todas las ocurrencias
String todosReemplazos = codigo.replaceAll("var", "let"); // "let x = 10; let y = 20;"
El método replaceAll() es muy potente cuando se combina con expresiones regulares:
String texto = "El precio es 15.99€ y el descuento es 3.50€";
// Reemplazar todos los números decimales por su valor redondeado
String resultado = texto.replaceAll("(\\d+)\\.\\d+", "$1");
System.out.println(resultado); // "El precio es 15€ y el descuento es 3€"
Casos de uso comunes
// Normalización de texto para búsquedas
String busqueda = " Machine Learning ";
String normalizado = busqueda.trim().toLowerCase().replace(" ", "-");
System.out.println(normalizado); // "machine-learning"
// Sanitización básica de entrada HTML
String comentarioUsuario = "<script>alert('XSS')</script>Buen artículo!";
String seguro = comentarioUsuario.replace("<", "<").replace(">", ">");
System.out.println(seguro); // "<script>alert('XSS')</script>Buen artículo!"
División de cadenas con split()
El método split(String regex) divide una cadena en un array de subcadenas utilizando una expresión regular como delimitador.
String frase = "Java es un lenguaje de programación versátil";
String[] palabras = frase.split(" ");
// Resultado: ["Java", "es", "un", "lenguaje", "de", "programación", "versátil"]
for (String palabra : palabras) {
System.out.println(palabra);
}
Se puede limitar el número de divisiones especificando un segundo parámetro:
String datos = "Juan,25,Madrid,Ingeniero";
String[] campos = datos.split(",", 3);
// Resultado: ["Juan", "25", "Madrid,Ingeniero"]
for (String campo : campos) {
System.out.println(campo);
}
Consideraciones con caracteres especiales
Cuando se utilizan caracteres que tienen significado especial en expresiones regulares (como ., *, +, ?, etc.), se deben escapar con \\:
String ruta = "C:\\Documentos\\Java\\ejemplos.txt";
String[] partes = ruta.split("\\\\");
// Resultado: ["C:", "Documentos", "Java", "ejemplos.txt"]
Aplicaciones prácticas
El método split() es fundamental para procesar datos estructurados:
// Procesamiento de CSV
String linea = "Juan,Pérez,30,Madrid";
String[] datos = linea.split(",");
String nombre = datos[0];
String apellido = datos[1];
int edad = Integer.parseInt(datos[2]);
String ciudad = datos[3];
// Análisis de logs
String logEntry = "2023-05-15 14:30:45 [ERROR] Connection refused: 192.168.1.1:8080";
String[] partes = logEntry.split(" ", 4);
String fecha = partes[0];
String hora = partes[1];
String nivel = partes[2].replace("[", "").replace("]", "");
String mensaje = partes[3];
Combinación de métodos de transformación
Los métodos de transformación se pueden encadenar para realizar operaciones complejas en una sola línea:
String entrada = " usuario@DOMINIO.com ";
String normalizado = entrada.trim() // Eliminar espacios
.toLowerCase() // Convertir a minúsculas
.replace(" ", ""); // Eliminar espacios internos
System.out.println(normalizado); // "usuario@dominio.com"
Un ejemplo más complejo que combina varios métodos:
public String formatearNombreArchivo(String nombreOriginal) {
return nombreOriginal.trim()
.toLowerCase()
.replaceAll("[áàäâã]", "a")
.replaceAll("[éèëê]", "e")
.replaceAll("[íìïî]", "i")
.replaceAll("[óòöôõ]", "o")
.replaceAll("[úùüû]", "u")
.replaceAll("[^a-z0-9\\.]", "-")
.replaceAll("-+", "-");
}
// Uso:
String archivo = " Currículum Vitáe (2023).pdf ";
String formateado = formatearNombreArchivo(archivo);
// Resultado: "curriculum-vitae-2023.pdf"
Procesamiento de texto estructurado
La combinación de split() con otros métodos de transformación permite procesar texto estructurado:
String json = "{\"nombre\":\"Juan\",\"edad\":30,\"ciudad\":\"Madrid\"}";
// Extraer pares clave-valor de forma básica
String contenido = json.substring(1, json.length() - 1); // Eliminar llaves
String[] pares = contenido.split(",");
Map<String, String> datos = new HashMap<>();
for (String par : pares) {
String[] keyValue = par.split(":");
String clave = keyValue[0].replace("\"", "");
String valor = keyValue[1].replace("\"", "");
datos.put(clave, valor);
}
System.out.println("Nombre: " + datos.get("nombre"));
System.out.println("Edad: " + datos.get("edad"));
Consideraciones de rendimiento
Al trabajar con transformaciones de cadenas, se deben tener en cuenta algunas consideraciones:
- Las operaciones de transformación crean nuevos objetos String, lo que puede afectar al rendimiento si se realizan muchas operaciones consecutivas.
- Para transformaciones múltiples, es más eficiente utilizar
StringBuilder:
// Enfoque ineficiente
String resultado = "";
for (int i = 0; i < 1000; i++) {
resultado += "a";
}
// Enfoque eficiente
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.append("a");
}
String resultado = sb.toString();
- El método
split()con expresiones regulares complejas puede ser costoso para cadenas muy largas. En estos casos, se puede considerar el uso deStringTokenizeroScannercomo alternativas.
Rendimiento y optimización con String, StringBuilder y StringBuffer
Java ofrece tres clases principales para el manejo de texto, cada una con características específicas que afectan directamente al rendimiento de nuestras aplicaciones.
Inmutabilidad de String y sus implicaciones
La clase String en Java es inmutable, lo que significa que una vez creado un objeto String, su contenido no puede ser modificado. Cada operación que parece modificar una cadena en realidad crea un nuevo objeto String.
String nombre = "Java";
nombre = nombre + " Programming"; // Crea un nuevo objeto String
Esta inmutabilidad tiene consecuencias:
- Seguridad: Los objetos String pueden compartirse de forma segura entre diferentes partes de una aplicación.
- Hashcode: El valor hash de un String se calcula una sola vez y se almacena en caché.
- Pool de Strings: Java mantiene un pool de Strings para reutilizar objetos con el mismo contenido.
Sin embargo, la inmutabilidad también presenta desafíos de rendimiento:
String resultado = "";
for (int i = 0; i < 10000; i++) {
resultado += i; // Crea 10000 objetos String intermedios
}
Este código crea miles de objetos String temporales que deben ser recolectados por el garbage collector, lo que afecta negativamente al rendimiento.
StringBuilder: la alternativa mutable
La clase StringBuilder fue diseñada específicamente para abordar las limitaciones de rendimiento de String cuando se realizan múltiples modificaciones. A diferencia de String, StringBuilder es mutable, lo que significa que puede modificar su contenido sin crear nuevos objetos.
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) {
sb.append(i); // Modifica el mismo objeto StringBuilder
}
String resultado = sb.toString(); // Conversión final a String
Los métodos más utilizados de StringBuilder incluyen:
- append(): Añade contenido al final del StringBuilder.
- insert(): Inserta contenido en una posición específica.
- delete(): Elimina caracteres en un rango determinado.
- replace(): Reemplaza caracteres en un rango por una nueva cadena.
- reverse(): Invierte el contenido del StringBuilder.
StringBuilder builder = new StringBuilder("Hola");
builder.append(" Mundo") // "Hola Mundo"
.insert(0, "¡") // "¡Hola Mundo"
.append("!") // "¡Hola Mundo!"
.delete(5, 10) // "¡Hola!"
.replace(1, 5, "Java"); // "¡Java!"
String resultado = builder.toString(); // Conversión final a String
StringBuffer: la alternativa thread-safe
StringBuffer ofrece la misma funcionalidad que StringBuilder, pero con una diferencia crucial: es thread-safe. Todos sus métodos están sincronizados, lo que garantiza la seguridad en entornos multihilo.
StringBuffer buffer = new StringBuffer();
// Seguro para usar en múltiples hilos
buffer.append("Thread-safe ");
buffer.append("string manipulation");
La sincronización tiene un costo en términos de rendimiento, por lo que StringBuffer es más lento que StringBuilder en aplicaciones de un solo hilo.
Comparativa de rendimiento
Para entender mejor las diferencias de rendimiento, veamos una comparación práctica:
// Medición con String
long inicio = System.nanoTime();
String s = "";
for (int i = 0; i < 100000; i++) {
s += "a";
}
long fin = System.nanoTime();
System.out.println("String: " + (fin - inicio) / 1000000 + " ms");
// Medición con StringBuilder
inicio = System.nanoTime();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100000; i++) {
sb.append("a");
}
String resultadoSb = sb.toString();
fin = System.nanoTime();
System.out.println("StringBuilder: " + (fin - inicio) / 1000000 + " ms");
// Medición con StringBuffer
inicio = System.nanoTime();
StringBuffer sbf = new StringBuffer();
for (int i = 0; i < 100000; i++) {
sbf.append("a");
}
String resultadoSbf = sbf.toString();
fin = System.nanoTime();
System.out.println("StringBuffer: " + (fin - inicio) / 1000000 + " ms");
Los resultados típicos muestran que StringBuilder es más rápido que String para operaciones múltiples, y StringBuffer es ligeramente más lento que StringBuilder debido a la sincronización.
Optimización de la capacidad inicial
Tanto StringBuilder como StringBuffer permiten especificar una capacidad inicial, lo que puede mejorar el rendimiento cuando se conoce aproximadamente el tamaño final de la cadena:
// Sin capacidad inicial especificada
StringBuilder sb1 = new StringBuilder();
// Con capacidad inicial de 1000 caracteres
StringBuilder sb2 = new StringBuilder(1000);
Cuando no se específica una capacidad, se utiliza un valor predeterminado (generalmente 16 caracteres). Si las operaciones de append exceden esta capacidad, se debe realizar una expansión interna (realocar memoria y copiar el contenido), lo que afecta al rendimiento.
// Optimización para concatenar 1000 números
StringBuilder optimizado = new StringBuilder(5000); // Estimación del tamaño final
for (int i = 0; i < 1000; i++) {
optimizado.append(i).append(", ");
}
Cuándo usar cada clase
La elección entre String, StringBuilder y StringBuffer depende del contexto específico:
- String: Ideal para cadenas que no cambiarán o cambiarán muy poco. También es la mejor opción cuando se necesita pasar cadenas entre métodos o almacenarlas en colecciones.
- StringBuilder: La mejor opción para manipulaciones intensivas de cadenas en un entorno de un solo hilo. Ofrece el mejor rendimiento para concatenaciones y modificaciones.
- StringBuffer: Necesario cuando se manipulan cadenas en un entorno multihilo donde varios hilos pueden modificar la misma cadena simultáneamente.
Patrones de optimización comunes
Concatenación en bucles
// Mal (crea muchos objetos String)
String resultado = "";
for (String item : items) {
resultado += item + ", ";
}
// Bien (usa un solo StringBuilder)
StringBuilder sb = new StringBuilder();
for (String item : items) {
sb.append(item).append(", ");
}
String resultado = sb.toString();
Construcción de consultas SQL
// Construcción eficiente de una consulta SQL dinámica
StringBuilder query = new StringBuilder("SELECT * FROM usuarios WHERE 1=1");
if (nombre != null) {
query.append(" AND nombre = '").append(nombre).append("'");
}
if (edad > 0) {
query.append(" AND edad > ").append(edad);
}
String sqlFinal = query.toString();
Generación de documentos
// Generación eficiente de un documento HTML
StringBuilder html = new StringBuilder(1024); // Capacidad inicial estimada
html.append("<!DOCTYPE html>\n")
.append("<html>\n")
.append(" <head>\n")
.append(" <title>").append(titulo).append("</title>\n")
.append(" </head>\n")
.append(" <body>\n")
.append(" <h1>").append(encabezado).append("</h1>\n");
// Añadir contenido dinámico
for (String parrafo : parrafos) {
html.append(" <p>").append(parrafo).append("</p>\n");
}
html.append(" </body>\n")
.append("</html>");
String documentoFinal = html.toString();
Optimizaciones avanzadas
Reutilización de StringBuilder
En operaciones repetitivas, se puede reutilizar el mismo objeto StringBuilder para evitar crear instancias innecesarias:
StringBuilder reutilizable = new StringBuilder(100);
List<String> resultados = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
reutilizable.setLength(0); // Vacía el StringBuilder sin crear uno nuevo
reutilizable.append("Prefijo-").append(i).append("-Sufijo");
resultados.add(reutilizable.toString());
}
Concatenación de cadenas en expresiones
El compilador de Java optimiza automáticamente algunas concatenaciones simples de cadenas utilizando StringBuilder internamente:
// El compilador optimiza esto automáticamente usando StringBuilder
String mensaje = "Hola " + nombre + ", tienes " + edad + " años.";
Sin embargo, esta optimización no se aplica en bucles o expresiones complejas, donde se debe usar StringBuilder explícitamente.
Uso de String.join() para concatenaciones simples
Para concatenaciones simples con un delimitador común, Java ofrece el método String.join() que es más legible y eficiente que la concatenación manual:
// Más eficiente y legible que concatenación manual
String[] palabras = {"Java", "es", "un", "lenguaje", "de", "programación"};
String frase = String.join(" ", palabras);
// También funciona con colecciones
List<String> nombres = List.of("Ana", "Juan", "María");
String listaDeNombres = String.join(", ", nombres);
Consideraciones de memoria
Además del rendimiento en tiempo de ejecución, es importante considerar el uso de memoria:
- String: Cada operación crea un nuevo objeto, lo que puede llevar a un alto consumo de memoria si se realizan muchas operaciones.
- StringBuilder/StringBuffer: Utilizan un buffer interno que puede crecer según sea necesario, lo que generalmente resulta en un menor consumo de memoria para operaciones múltiples.
// Monitoreo básico de memoria
Runtime runtime = Runtime.getRuntime();
long memoriaAntes = runtime.totalMemory() - runtime.freeMemory();
// Operaciones con cadenas...
long memoriaDespues = runtime.totalMemory() - runtime.freeMemory();
System.out.println("Memoria utilizada: " + (memoriaDespues - memoriaAntes) / 1024 + " KB");
Formateo de Strings
El formateo de cadenas permite crear textos estructurados combinando valores variables con plantillas predefinidas. Java ofrece diversas técnicas para dar formato a las cadenas, desde métodos tradicionales hasta enfoques más modernos, cada uno con sus propias ventajas en diferentes contextos.
Método String.format()
El método String.format() proporciona una forma flexible de crear cadenas formateadas utilizando especificadores de formato similares a los de C. Este método acepta una cadena de formato y una serie de argumentos que se incorporan en la cadena resultante.
String nombre = "Ana";
int edad = 28;
double salario = 2500.75;
String mensaje = String.format("Empleado: %s, Edad: %d, Salario: %.2f€", nombre, edad, salario);
System.out.println(mensaje); // Empleado: Ana, Edad: 28, Salario: 2500,75€
Los especificadores de formato más comunes incluyen:
%s- Para cadenas%d- Para enteros%f- Para números de punto flotante%b- Para valores booleanos%c- Para caracteres%t- Para fechas y horas (con subformatos)%%- Para imprimir el símbolo %
Se pueden aplicar modificadores a estos especificadores para controlar con precisión la salida:
// Ancho y alineación
String alineado = String.format("|%-10s|%10s|", "izquierda", "derecha");
System.out.println(alineado); // |izquierda | derecha|
// Precisión decimal
double pi = Math.PI;
String piFormateado = String.format("Pi con 2 decimales: %.2f", pi);
System.out.println(piFormateado); // Pi con 2 decimales: 3,14
// Formato de números con separadores de miles
long poblacion = 47_350_000;
String pobFormateada = String.format("Población: %,d habitantes", poblacion);
System.out.println(pobFormateada); // Población: 47.350.000 habitantes
Formateo de fechas y horas
El formateo de fechas y horas merece especial atención por su complejidad. El especificador %t se combina con letras adicionales para controlar el formato:
LocalDateTime ahora = LocalDateTime.now();
String fechaFormateada = String.format("Fecha: %tD, Hora: %tT", ahora, ahora);
System.out.println(fechaFormateada); // Fecha: 05/15/23, Hora: 14:30:45
// Formatos más específicos
String formatoCompleto = String.format(
"Día: %td, Mes: %tB, Año: %tY, Hora: %tH:%tM",
ahora, ahora, ahora, ahora, ahora
);
Para evitar repetir la referencia al mismo objeto, se puede usar el índice de argumento:
String formatoMejorado = String.format(
"Fecha: %1$td/%1$tm/%1$tY, Hora: %1$tH:%1$tM:%1$tS",
ahora
);
Método printf() para salida formateada
El método System.out.printf() utiliza la misma sintaxis de formateo que String.format(), pero imprime directamente en la salida estándar en lugar de devolver una cadena:
double precio = 19.99;
int cantidad = 3;
System.out.printf("Subtotal: %.2f€ × %d unidades = %.2f€%n",
precio, cantidad, precio * cantidad);
// Subtotal: 19,99€ × 3 unidades = 59,97€
El especificador %n se utiliza para insertar un salto de línea independiente de la plataforma, lo que es preferible a usar \n directamente.
Formateo con String.formatted() (Java 15+)
A partir de Java 15, se introdujo el método de instancia formatted() que simplifica el formateo al permitir llamarlo directamente sobre una cadena de formato:
String plantilla = "Usuario: %s (ID: %d)";
String resultado = plantilla.formatted("carlos_dev", 12345);
System.out.println(resultado); // Usuario: carlos_dev (ID: 12345)
Este enfoque es más conciso y mejora la legibilidad del código en muchos casos.
Formateo con StringBuilder
Para escenarios donde se necesita construir cadenas formateadas de manera dinámica, StringBuilder ofrece métodos de formateo similares:
StringBuilder sb = new StringBuilder();
Formatter formatter = new Formatter(sb);
formatter.format("Lista de precios:%n");
String[] productos = {"Teclado", "Ratón", "Monitor"};
double[] precios = {45.99, 22.50, 199.99};
for (int i = 0; i < productos.length; i++) {
formatter.format("- %-10s %8.2f€%n", productos[i], precios[i]);
}
System.out.println(sb.toString());
// Lista de precios:
// - Teclado 45,99€
// - Ratón 22,50€
// - Monitor 199,99€
Plantillas de texto (Java 21+)
Java 21 introdujo las plantillas de texto (Text Blocks con interpolación), una característica que simplifica el formateo de cadenas:
String nombre = "María";
int puntuacion = 95;
String mensaje = STR."¡Felicidades \{nombre}! Has obtenido \{puntuacion} puntos.";
System.out.println(mensaje); // ¡Felicidades María! Has obtenido 95 puntos.
Las plantillas de texto permiten la interpolación de expresiones directamente en la cadena, lo que resulta en un código más legible y menos propenso a errores que los métodos tradicionales de formateo.
int x = 10;
int y = 20;
String operacion = STR."\{x} + \{y} = \{x + y}";
System.out.println(operacion); // 10 + 20 = 30
// Expresiones más complejas
LocalDate fecha = LocalDate.now();
String informe = STR."""
Informe generado el \{fecha.format(DateTimeFormatter.ISO_LOCAL_DATE)}
Temperatura: \{String.format("%.1f", 22.75)}°C
Estado: \{Math.random() > 0.5 ? "Óptimo" : "Requiere revisión"}
""";
MessageFormat para localización
La clase MessageFormat es útil para crear mensajes localizados y parametrizados:
String patron = "El archivo {0} tiene {1,number,integer} bytes y fue modificado el {2,date,long}.";
Object[] valores = {"config.xml", 1234, new Date()};
String mensaje = MessageFormat.format(patron, valores);
System.out.println(mensaje);
// El archivo config.xml tiene 1.234 bytes y fue modificado el 15 de mayo de 2023.
MessageFormat se integra con el sistema de internacionalización de Java, permitiendo adaptar los mensajes a diferentes idiomas y convenciones regionales:
// Formateo según configuración regional
MessageFormat mf = new MessageFormat("El precio es {0,number,currency}.", new Locale("es", "ES"));
String precioEspanol = mf.format(new Object[]{29.99});
System.out.println(precioEspanol); // El precio es 29,99 €.
mf = new MessageFormat("The price is {0,number,currency}.", Locale.US);
String precioUSA = mf.format(new Object[]{29.99});
System.out.println(precioUSA); // The price is $29.99.
Formateo de números con DecimalFormat
Para un control más preciso sobre el formato de números, la clase DecimalFormat ofrece opciones avanzadas:
// Formato de porcentajes
DecimalFormat dfPorcentaje = new DecimalFormat("##0.00'%'");
System.out.println(dfPorcentaje.format(0.7625)); // 76,25%
// Formato de moneda personalizado
DecimalFormat dfMoneda = new DecimalFormat("###,###.## €");
System.out.println(dfMoneda.format(1250.75)); // 1.250,75 €
// Números con prefijos y sufijos
DecimalFormat dfTamano = new DecimalFormat("### KB");
System.out.println(dfTamano.format(512)); // 512 KB
Los patrones de formato en DecimalFormat utilizan símbolos especiales:
0- Dígito obligatorio#- Dígito opcional.- Separador decimal,- Separador de agrupación'- Escape para texto literal%- Multiplicar por 100 y mostrar como porcentaje‰- Multiplicar por 1000 y mostrar como por mil
// Diferentes patrones para el mismo número
double valor = 12345.6789;
String[] patrones = {"###,###.###", "000,000.000", "#,##0.00", "0.0"};
for (String patron : patrones) {
DecimalFormat df = new DecimalFormat(patron);
System.out.println(patron + ": " + df.format(valor));
}
// ###,###.###: 12.345,679
// 000,000.000: 012.345,679
// #,##0.00: 12.345,68
// 0.0: 12345,7
Casos de uso prácticos
Generación de informes tabulares
String[] encabezados = {"Producto", "Precio", "Stock", "Valor"};
Object[][] datos = {
{"Laptop", 899.99, 15, 899.99 * 15},
{"Smartphone", 499.50, 30, 499.50 * 30},
{"Auriculares", 79.99, 50, 79.99 * 50}
};
// Formato de tabla
System.out.printf("| %-15s | %10s | %5s | %12s |%n",
(Object[])encabezados);
System.out.println("|-----------------|------------|-------|--------------|");
for (Object[] fila : datos) {
System.out.printf("| %-15s | %,10.2f€ | %5d | %,12.2f€ |%n",
fila[0], fila[1], fila[2], fila[3]);
}
Formateo de datos científicos
double[] mediciones = {0.0000123, 4.567e6, 2.998e8};
String[] unidades = {"s", "Hz", "m/s"};
for (int i = 0; i < mediciones.length; i++) {
// Formato científico
System.out.printf("Valor: %e %s%n", mediciones[i], unidades[i]);
// Formato adaptativo
System.out.printf("Valor: %g %s%n", mediciones[i], unidades[i]);
}
Generación de URLs y consultas
String baseUrl = "https://api.ejemplo.com/buscar";
String query = "java programación";
int pagina = 2;
int resultadosPorPagina = 25;
String url = String.format("%s?q=%s&page=%d&size=%d",
baseUrl,
URLEncoder.encode(query, StandardCharsets.UTF_8),
pagina,
resultadosPorPagina);
System.out.println(url);
Consideraciones de rendimiento
El formateo de cadenas tiene implicaciones de rendimiento que deben considerarse en aplicaciones críticas:
String.format()es conveniente pero relativamente costoso en términos de rendimiento debido a la necesidad de analizar la cadena de formato y convertir los argumentos.- Para operaciones de formateo intensivas dentro de bucles, considere reutilizar un
FormatteroStringBuilder. - Las plantillas de texto (Java 21+) ofrecen un rendimiento mejorado en comparación con
String.format()para muchos casos de uso.
// Comparación de rendimiento básica
long inicio, fin;
inicio = System.nanoTime();
for (int i = 0; i < 10000; i++) {
String.format("%d + %d = %d", i, i+1, i*2+1);
}
fin = System.nanoTime();
System.out.println("String.format: " + (fin - inicio) / 1_000_000 + " ms");
// Con Java 21+
inicio = System.nanoTime();
for (int i = 0; i < 10000; i++) {
STR."\{i} + \{i+1} = \{i*2+1}";
}
fin = System.nanoTime();
System.out.println("Plantillas de texto: " + (fin - inicio) / 1_000_000 + " ms");
Buenas prácticas
- Elija el método adecuado según el contexto:
String.format()para casos generales, plantillas de texto para código más legible, yMessageFormatpara contenido localizable. - Reutilice patrones de formato cuando sea posible para mejorar el rendimiento.
- Considere la localización desde el principio si su aplicación será utilizada internacionalmente.
- Valide los datos de entrada antes de formatearlos para evitar excepciones inesperadas.
- Utilice constantes para patrones de formato complejos o reutilizados.
// Buena práctica: constantes para patrones reutilizados
private static final String FORMATO_MONEDA = "%,.2f€";
private static final String FORMATO_FECHA = "%1$td/%1$tm/%1$tY";
private static final DecimalFormat FORMATO_PORCENTAJE = new DecimalFormat("##0.00'%'");
// Uso
String precio = String.format(FORMATO_MONEDA, 1299.99);
String fecha = String.format(FORMATO_FECHA, LocalDate.now());
String tasa = FORMATO_PORCENTAJE.format(0.2175);
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Java es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de Java
Explora más contenido relacionado con Java y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
Transformar cadenas con toUpperCase(), toLowerCase(), replace(), replaceAll() y split(). Comprender cuándo usar StringBuilder o StringBuffer en lugar de String. Comparar el rendimiento de las tres clases de texto de Java. Formatear cadenas con String.format(), printf() y String.formatted(). Aplicar MessageFormat y DecimalFormat para localización y formato numérico avanzado.
Cursos que incluyen esta lección
Esta lección forma parte de los siguientes cursos estructurados con rutas de aprendizaje