Saltar a contenido

UD2 · Sección 7 — StringBuilder y concatenación eficiente

Dato Valor
Unidad Didáctica UD2 — Programación Orientada a Objetos con la API de Java
Resultado de Aprendizaje RA2 — Escribe y prueba programas sencillos, reconociendo y aplicando los fundamentos de la programación orientada a objetos.
Sección 7 de 10 — StringBuilder y concatenación eficiente
Evaluación 1.ª Evaluación
Duración estimada 2 horas lectivas
Criterios de evaluación cubiertos CE 2.2 (escribe programas simples), CE 2.3 (instancia objetos a partir de clases predefinidas), CE 2.4 (utiliza métodos y propiedades), CE 2.6 (utiliza parámetros), CE 2.8 (utiliza constructores)
Prerrequisitos UD1 completa, UD2 Secciones 1 a 6
Paquete del código de ejemplo dam.programacion.ud2
Herramientas JDK 25, IntelliJ IDEA Community, Maven 3.9.x

1. Punto de partida

En la Sección 3 descubriste que String es inmutable: cualquier operación que parezca modificar un string (concatenar, sustituir, transformar) en realidad crea un String nuevo. Para operaciones puntuales eso no es problema. Pero ¿qué pasa si construyes un texto dentro de un bucle que se ejecuta mil veces? Cada iteración crea un String intermedio que se descarta en la siguiente, y al final tienes mil objetos en el heap esperando a que el garbage collector los limpie. Eso es ineficiente en memoria y en tiempo.

Para esos casos, Java ofrece StringBuilder: una clase mutable diseñada específicamente para construir y modificar cadenas de forma eficiente. En esta sección verás por qué StringBuilder es mejor que la concatenación con + en bucles, cómo se usa, y qué alternativas más modernas existen (StringJoiner y String.join, que ya conoces parcialmente de la Sección 3).

Al terminar la sección serás capaz de construir textos largos de forma eficiente, elegir entre String, StringBuilder y StringJoiner según el caso, y entender por qué la concatenación con + no siempre es mala (de hecho, el compilador la optimiza en muchos casos).


2. El problema de la concatenación con + en bucles

2.1. Recordatorio: inmutabilidad de String

String s = "Hola";
s = s + ", mundo";   // crea un nuevo String "Hola, mundo"; el original "Hola" queda para GC

Cada + genera un nuevo String. En una operación aislada no pasa nada: el coste es mínimo. Pero en un bucle, el coste se acumula.

2.2. El escenario problemático

Imagina que construyes un informe uniendo los nombres de 1000 clientes:

1
2
3
4
5
6
7
String[] nombres = /* array de 1000 nombres */;
String informe = "";
for (String nombre : nombres) {
    informe = informe + nombre + ", ";
}
System.out.println(informe);
}

¿Qué pasa en cada iteración?

  1. Se crea un String intermedio con informe + nombre.
  2. Se crea otro String intermedio con el resultado anterior + ", ".
  3. Ese resultado se asigna a informe.
  4. El informe anterior queda sin referencia y se marca para GC.

En total, para 1000 nombres, el programa crea 2000 strings (los intermedios y los finales) y los descarta. El coste en memoria es enorme y el tiempo de ejecución crece cuadráticamente (el primer nombre se copia 1000 veces, el segundo 999, el tercero 998...).

2.3. La solución: StringBuilder

StringBuilder es mutable: vas añadiendo texto a un mismo objeto sin crear nuevos. Al final, llamas a toString() para obtener el String resultante.

1
2
3
4
5
StringBuilder sb = new StringBuilder();
for (String nombre : nombres) {
    sb.append(nombre).append(", ");
}
String informe = sb.toString();

Aquí solo se crea un objeto StringBuilder y un String final. Los fragmentos se añaden al buffer interno del StringBuilder, que se redimensiona automáticamente cuando hace falta.

Analogía del cuaderno. Imagina que quieres copiar 1000 frases en un cuaderno.

  • Con + (concatenación): cada frase que copias requiere pasar a una hoja nueva. Al final tienes 1000 hojas usadas (los String intermedios) y has copiado la primera frase 1000 veces, la segunda 999, etc. Un desastre.
  • Con StringBuilder: escribes todas las frases seguidas en el mismo cuaderno. Cuando una hoja se llena, pasas a la siguiente (redimensión del buffer). Al final tienes un cuaderno con todas las frases, en orden, sin desperdiciar.

3. La clase StringBuilder

3.1. Constructores

Constructor Qué hace
StringBuilder() Crea un StringBuilder vacío (capacidad inicial por defecto: 16 caracteres)
StringBuilder(int capacidad) Crea un StringBuilder vacío con la capacidad inicial indicada
StringBuilder(String s) Crea un StringBuilder con el contenido inicial de s
StringBuilder(CharSequence cs) Crea un StringBuilder con el contenido de cualquier CharSequence
1
2
3
StringBuilder sb1 = new StringBuilder();               // vacio
StringBuilder sb2 = new StringBuilder(1000);           // vacio, con capacidad 1000
StringBuilder sb3 = new StringBuilder("Hola");         // inicia con "Hola"

Capacidad. Internamente, StringBuilder mantiene un array de caracteres. Si lo llenas, Java crea un array nuevo más grande y copia el contenido. Para evitar redimensionamientos innecesarios (que son costosos), conviene crear el StringBuilder con una capacidad inicial cercana al tamaño final esperado.

3.2. Métodos principales

Método Qué hace
append(tipo) Añade al final (admite String, int, double, boolean, char, Object, etc.)
insert(int offset, tipo) Inserta en la posición indicada
delete(int start, int end) Elimina los caracteres entre start (inclusivo) y end (exclusivo)
deleteCharAt(int index) Elimina un carácter concreto
replace(int start, int end, String s) Sustituye el rango por s
reverse() Invierte el contenido
setCharAt(int index, char c) Cambia un carácter concreto
substring(int start) / substring(int start, int end) Devuelve un String (sin modificar el StringBuilder)
charAt(int index) Devuelve el carácter en la posición
length() Longitud actual
capacity() Capacidad actual del buffer
toString() Devuelve el String resultante
indexOf(String) / lastIndexOf(String) Búsqueda (como en String)

Ejemplos:

StringBuilder sb = new StringBuilder("Hola");
sb.append(", mundo");             // "Hola, mundo"
sb.append("!");                   // "Hola, mundo!"
System.out.println(sb);

sb.insert(5, "querido ");         // "Hola, querido mundo!"
System.out.println(sb);

sb.delete(5, 12);                 // "Hola, mundo!"
System.out.println(sb);

sb.reverse();                     // "!odnum ,aloH"
System.out.println(sb);

sb.reverse();                     // "Hola, mundo!" (lo dejamos como estaba)
System.out.println(sb);

String resultado = sb.toString();
System.out.println(resultado);

Observa que los métodos modifican el StringBuilder (es mutable) y devuelven una referencia al propio StringBuilder, lo que permite encadenar llamadas:

1
2
3
StringBuilder sb = new StringBuilder();
sb.append("Hola").append(", ").append("mundo").append("!");
// Equivalente a cuatro append() sucesivos

3.3. append admite muchos tipos

A diferencia de +, que solo funciona entre String y otros tipos mediante conversión automática, append está sobrecargado para todos los tipos primitivos y Object:

1
2
3
StringBuilder sb = new StringBuilder();
sb.append("Edad: ").append(23).append(", altura: ").append(1.75).append(", activo: ").append(true);
System.out.println(sb);   // Edad: 23, altura: 1.75, activo: true

Esto es más eficiente que concatenar con + en un bucle, porque cada append solo añade al buffer interno sin crear String intermedios.


4. Cuándo usar + y cuándo StringBuilder

Una idea muy extendida entre principiantes es que "concatenar con + es siempre malo". Es falso. El compilador de Java optimiza la concatenación con + en muchos casos, convirtiéndola internamente en StringBuilder automáticamente.

4.1. Caso optimizado: concatenación en una sola línea

1
2
3
String nombre = "Maria";
int edad = 23;
String mensaje = "Hola, " + nombre + ". Tienes " + edad + " anos.";

El compilador transforma esta línea en algo equivalente a:

1
2
3
String mensaje = new StringBuilder()
    .append("Hola, ").append(nombre).append(". Tienes ").append(edad).append(" anos.")
    .toString();

Es decir, una sola línea con varios + es tan eficiente como un StringBuilder manual. No tienes que preocuparte por optimizarla.

4.2. Caso NO optimizado: concatenación en un bucle

1
2
3
4
String informe = "";
for (String nombre : nombres) {
    informe = informe + nombre + ", ";   // se crea un StringBuilder NUEVO en cada iteracion
}

El compilador traduce cada informe = informe + nombre + ", " como un StringBuilder nuevo, lo que da lugar a 1000 StringBuilder y 1000 String intermedios. Ahí sí conviene reescribir:

1
2
3
4
5
StringBuilder sb = new StringBuilder();
for (String nombre : nombres) {
    sb.append(nombre).append(", ");
}
String informe = sb.toString();

4.3. Reglas prácticas

Situación Recomendación
Concatenar 2-3 strings en una sola línea + (lo optimiza el compilador)
Construir un mensaje con + en una sola expresión + o String.format
Construir un texto en un bucle StringBuilder
Unir elementos de una colección con separador String.join o StringJoiner
Construir un texto condicional con muchas ramas StringBuilder
Construir SQL, JSON, HTML manualmente StringBuilder (o mejor, una librería específica)

Analogía de los cubiertos. Imagina que + es un tenedor y StringBuilder es un cuchillo de cocina.

  • Para comer un trozo de carne (operación simple), el tenedor basta.
  • Para filetear una pieza entera (muchas operaciones encadenadas), necesitas el cuchillo.

No uses el cuchillo para comer un trozo de carne; no uses el tenedor para filetear.


5. StringJoiner: unir con separador, prefijo y sufijo

Java 8 introdujo StringJoiner, una clase pensada para unir varios elementos con un separador, opcionalmente con un prefijo y un sufijo. Es especialmente útil cuando quieres producir un texto con un formato fijo, como una lista entre corchetes.

5.1. Constructor y métodos básicos

Constructor Qué hace
StringJoiner(CharSequence separador) Crea un StringJoiner con el separador indicado, sin prefijo ni sufijo
StringJoiner(CharSequence separador, CharSequence prefijo, CharSequence sufijo) Igual, con prefijo y sufijo

Métodos principales:

Método Qué hace
add(CharSequence) Añade un elemento
merge(StringJoiner otro) Fusiona otro StringJoiner
setEmptyValue(CharSequence) Valor a devolver si está vacío
toString() Devuelve el String resultante
length() Longitud total

5.2. Ejemplo

import java.util.StringJoiner;

public class EjemploStringJoiner {
    public static void main(String[] args) {
        // Sin prefijo ni sufijo
        StringJoiner sj1 = new StringJoiner(", ");
        sj1.add("Maria").add("Carlos").add("Ana").add("Luis");
        System.out.println(sj1);   // Maria, Carlos, Ana, Luis

        // Con prefijo y sufijo
        StringJoiner sj2 = new StringJoiner(", ", "[", "]");
        sj2.add("Maria").add("Carlos").add("Ana");
        System.out.println(sj2);   // [Maria, Carlos, Ana]

        // Vacio con valor por defecto
        StringJoiner sj3 = new StringJoiner(", ").setEmptyValue("(vacio)");
        System.out.println(sj3);   // (vacio)
    }
}

StringJoiner se usa internamente por el método String.join (Sección 3) y por el Collectors.joining de la Streams API (UD6). Rara vez lo usarás directamente, pero conviene saber que existe.


6. String.join: la alternativa concisa

Ya lo vimos en la Sección 3, lo recordamos aquí porque encaja temáticamente. Es un método estático que usa StringJoiner internamente y permite unir elementos con un separador en una sola línea.

// Unir literales o variables
String resultado = String.join(", ", "Maria", "Carlos", "Ana", "Luis");
System.out.println(resultado);   // Maria, Carlos, Ana, Luis

// Unir elementos de un array
String[] partes = {"2026", "07", "14"};
String fecha = String.join("-", partes);
System.out.println(fecha);   // 2026-07-14

// Unir elementos de una coleccion (List, Set...)
// (Lo veras en UD6; aqui solo mencion)

Cuándo usar String.join vs StringJoiner:

  • Si ya tienes los elementos en un array o varargs, usa String.join.
  • Si necesitas prefijo/sufijo o vas añadiendo elementos dinámicamente, usa StringJoiner.

7. StringBuffer: el primo thread-safe

Existe una tercera clase similar: StringBuffer. Es idéntica a StringBuilder en API, pero es sincronizada (thread-safe). Esto significa que varios hilos pueden usar el mismo StringBuffer simultáneamente sin corromper su estado.

Clase Mutable Thread-safe Rendimiento Cuándo usar
String No Sí (por inmutabilidad) Bajo en concatenaciones repetidas Para textos fijos
StringBuilder Sí No Alto Caso general
StringBuffer Sí Sí Medio (sincronización) Multihilo (raro en código moderno)

Recomendación práctica: usa StringBuilder siempre. Si en algún momento necesitas compartir el buffer entre hilos (avanzado, se verá más adelante en el curso), entonces reconsidera StringBuffer. Pero hoy en día, en la mayoría de casos multihilo se prefieren otras aproximaciones (variables locales, inmutabilidad, etc.).


8. Diagrama comparativo

Use mouse to pan and zoom
graph TD
    A["Necesito construir/modificar un texto"]
    A --> B{"Es en un bucle o<br/>muchas operaciones?"}
    B -->|No, una sola linea| C["Usa + o String.format"]
    B -->|Si| D{"Quiero unir elementos<br/>con separador?"}
    D -->|Si, en una linea| E["String.join"]
    D -->|Si, con prefijo/sufijo<br/>o elementos dinamicos| F["StringJoiner"]
    D -->|No, es construccion<br/>paso a paso| G["StringBuilder"]
    B -->|Si, y necesito<br/>thread-safety| H["StringBuffer"]

    C --> R["String final"]
    E --> R
    F --> R
    G --> R
    H --> R

    style C fill:#2e7d32,color:#fff,stroke:#1b5e20
    style E fill:#2e7d32,color:#fff,stroke:#1b5e20
    style F fill:#ef6c00,color:#fff,stroke:#e65100
    style G fill:#1565c0,color:#fff,stroke:#0d47a1
    style H fill:#6a1b9a,color:#fff,stroke:#4a148c

9. Ejemplo integrador

Programa que demuestra los conceptos de la sección: construcción de un informe con + (ineficiente), con StringBuilder (eficiente), con StringJoiner y con String.join.

package dam.programacion.ud2;

import java.util.StringJoiner;

/**
 * Ejemplo integrador de la Seccion 7 de UD2.
 * Demuestra:
 * - El problema de concatenar con + en bucles
 * - StringBuilder: append, insert, delete, reverse
 * - StringJoiner con prefijo y sufijo
 * - String.join como alternativa concisa
 */
public class ConstruyendoTextos {

    public static void main(String[] args) {

        String[] nombres = {"Maria", "Carlos", "Ana", "Luis", "Elena", "Javier"};

        // 1. Concatenacion con + en bucle (INEFICIENTE)
        // Solo lo mostramos para ilustrar; no lo uses en codigo real
        String informeLento = "";
        for (String nombre : nombres) {
            informeLento = informeLento + nombre + " | ";
        }
        System.out.println("Con + (lento): " + informeLento);

        // 2. StringBuilder (EFICIENTE)
        StringBuilder sb = new StringBuilder(200);  // capacidad inicial holgada
        for (int i = 0; i < nombres.length; i++) {
            sb.append(nombres[i]);
            if (i < nombres.length - 1) {
                sb.append(" | ");
            }
        }
        System.out.println("Con StringBuilder: " + sb.toString());

        // 3. StringBuilder con encadenamiento y operaciones varias
        StringBuilder sb2 = new StringBuilder("Informe de clientes");
        sb2.append("\n").append("Total: ").append(nombres.length).append(" clientes");
        sb2.append("\n").append("Lista: ").append(String.join(", ", nombres));
        sb2.insert(0, "=== ").insert(0, "");  // anadimos prefijo al inicio
        sb2.append("\n=== Fin del informe");
        System.out.println("\nInforme construido:");
        System.out.println(sb2);

        // 4. Operaciones de modificacion
        StringBuilder sb3 = new StringBuilder("Hola mundo cruel");
        sb3.delete(10, 15);              // elimina "cruel"
        System.out.println("\nTras delete: " + sb3);   // "Hola mundo "
        sb3.insert(10, "maravilloso");   // inserta en la posicion 10
        System.out.println("Tras insert: " + sb3);     // "Hola mundo maravilloso"
        sb3.replace(5, 10, "tierra");    // reemplaza "mundo" por "tierra"
        System.out.println("Tras replace: " + sb3);    // "Hola tierra maravilloso"
        sb3.reverse();
        System.out.println("Al reves: " + sb3);

        // 5. StringJoiner con prefijo y sufijo
        StringJoiner sj = new StringJoiner(", ", "Clientes: [", "]");
        for (String nombre : nombres) {
            sj.add(nombre);
        }
        System.out.println("\nCon StringJoiner: " + sj);

        // 6. StringJoiner vacio con valor por defecto
        StringJoiner sjVacio = new StringJoiner(", ").setEmptyValue("(sin clientes)");
        System.out.println("Vacio: " + sjVacio);

        // 7. String.join: la opcion mas concisa
        String listaSimple = String.join(" - ", nombres);
        System.out.println("Con String.join: " + listaSimple);

        // 8. Construir una ruta de fichero con String.join
        String ruta = String.join("/", "home", "usuario", "docs", "informe.txt");
        System.out.println("Ruta: " + ruta);
    }
}

Salida esperada:

Con + (lento): Maria | Carlos | Ana | Luis | Elena | Javier | 
Con StringBuilder: Maria | Carlos | Ana | Luis | Elena | Javier

Informe construido:
=== Informe de clientes
Total: 6 clientes
Lista: Maria, Carlos, Ana, Luis, Elena, Javier
=== Fin del informe

Tras delete: Hola mundo 
Tras insert: Hola mundo maravilloso
Tras replace: Hola tierra maravilloso
Al reves: osilloliravom arret aloH

Con StringJoiner: Clientes: [Maria, Carlos, Ana, Luis, Elena, Javier]
Vacio: (sin clientes)
Con String.join: Maria - Carlos - Ana - Luis - Elena - Javier
Ruta: home/usuario/docs/informe.txt

10. Errores comunes

Error Qué pasa Cómo evitarlo
Concatenar con + en un bucle Ineficiente: crea muchos String intermedios Usa StringBuilder con append
Olvidar toString() al final Tienes un StringBuilder pero no un String Llama siempre a toString() para obtener el String final
Crear StringBuilder sin capacidad inicial cuando conoces el tamaño aproximado Redimensionamientos internos Pasa la capacidad: new StringBuilder(1000)
Confundir replace(int, int, String) de StringBuilder con replace(char, char) de String Firmas distintas Lee el Javadoc: en StringBuilder recibe posiciones y un String
Usar StringBuilder para 2-3 concatenaciones en una línea Código más verboso sin beneficio Usa + (el compilador lo optimiza)
Usar StringBuffer por defecto Sobrecoste innecesario por sincronización Usa StringBuilder salvo que necesites thread-safety
Olvidar el import java.util.StringJoiner; "cannot find symbol" Añade el import (o Alt+Enter en IntelliJ)
Modificar un String en un bucle esperando que cambie el original El cambio se pierde Recuerda: String es inmutable, hay que asignar
Llamar a substring de StringBuilder pensando que modifica el objeto Devuelve un String nuevo, no afecta al StringBuilder Si quieres recortar, usa delete

11. Resumen

Concepto Idea clave
String inmutable Cualquier operación crea un String nuevo
Concatenación con + en una sola línea El compilador la optimiza a StringBuilder
Concatenación con + en bucle Ineficiente: crea muchos objetos intermedios
StringBuilder Clase mutable para construir textos eficientemente
StringBuilder.append(tipo) Añade al final (admite cualquier tipo)
StringBuilder.insert/delete/replace/reverse Operaciones de modificación
StringBuilder.toString() Obtiene el String final
Capacidad inicial Especificarla reduce redimensionamientos
Encadenamiento sb.append(a).append(b).append(c)
StringJoiner Une elementos con separador, prefijo y sufijo
String.join(separador, elementos) Atajo estático para unir con separador
StringBuffer Igual que StringBuilder pero thread-safe (rara vez necesario)
Regla práctica + para casos simples, StringBuilder para bucles, String.join para unir con separador

12. Cuestionario de autoevaluación

Preguntas

  1. ¿Por qué concatenar con + en un bucle es ineficiente? Explica qué ocurre en cada iteración.

  2. ¿Es StringBuilder mutable o inmutable? ¿Y String?

  3. Escribe un fragmento de código que construya la cadena "Hola, Maria. Tienes 23 anos." usando StringBuilder con append.

  4. ¿El compilador optimiza la concatenación con + en una sola línea? Si es así, ¿a qué la convierte?

  5. Nombra tres métodos de StringBuilder distintos de append y toString.

  6. ¿Qué diferencia hay entre StringBuilder y StringBuffer?

  7. Si vas a construir un texto de aproximadamente 5000 caracteres, ¿por qué conviene crear el StringBuilder con new StringBuilder(5000)?

  8. Escribe una línea de código que una los strings "Maria", "Carlos" y "Ana" con el separador ", " usando String.join.

  9. ¿Qué método de StringBuilder usarías para invertir el contenido? ¿Devuelve un String o modifica el propio StringBuilder?

  10. ¿Cómo crearías un StringJoiner que produzca [A, B, C] (con corchetes como prefijo/sufijo)?

  11. ¿Qué pasa si llamas a substring(2, 5) sobre un StringBuilder? ¿Se modifica el objeto?

  12. Para construir el mensaje "Hola, " + nombre + ". Tienes " + edad + " anos." en una sola línea, ¿usarías + o StringBuilder? ¿Por qué?

  13. ¿Por qué append de StringBuilder acepta int, double, boolean y otros tipos, no solo String?

  14. Si en un bucle de 1000 iteraciones concatenas con +, ¿cuántos objetos String intermedios se crean aproximadamente?

  15. ¿Cuál es el equivalente en String.join del siguiente código?

    1
    2
    3
    StringJoiner sj = new StringJoiner(", ");
    sj.add("a").add("b").add("c");
    String r = sj.toString();
    

Soluciones

  1. En cada iteración, la expresión informe + nombre crea un nuevo String. Ese nuevo String se asigna a informe, y el informe anterior queda sin referencia, esperando al garbage collector. En 1000 iteraciones se crean ~1000 String intermedios, y como cada uno copia el contenido de los anteriores, el coste total crece cuadráticamente.

  2. StringBuilder es mutable: los métodos modifican el propio objeto. String es inmutable: cualquier operación devuelve un String nuevo.

  3. Una solución:

    1
    2
    3
    StringBuilder sb = new StringBuilder();
    sb.append("Hola, ").append("Maria").append(". Tienes ").append(23).append(" anos.");
    String resultado = sb.toString();
    

  4. Sí. El compilador transforma una concatenación con + en una sola línea en una secuencia de llamadas a StringBuilder.append terminada con toString(). Por eso en esos casos no hay pérdida de rendimiento.

  5. Posibles: insert(int, tipo), delete(int, int), deleteCharAt(int), replace(int, int, String), reverse(), setCharAt(int, char), substring(int), charAt(int), length(), capacity().

  6. StringBuffer es sincronizado (thread-safe): varios hilos pueden usarlo a la vez sin corromper su estado. StringBuilder no lo es. StringBuilder es más rápido; StringBuffer se reserva para casos multihilo donde se comparte el mismo objeto entre hilos (raro en código moderno).

  7. Porque StringBuilder internamente mantiene un array de caracteres. Si lo llenas, Java crea un array nuevo más grande y copia el contenido. Indicar la capacidad inicial evita redimensionamientos intermedios, que son costosos.

  8. String.join(", ", "Maria", "Carlos", "Ana");

  9. reverse(). Modifica el propio StringBuilder y devuelve una referencia a sí mismo (para permitir encadenamiento). No devuelve un String.

  10. StringJoiner sj = new StringJoiner(", ", "[", "]"); y luego sj.add("A").add("B").add("C");.

  11. No se modifica. substring en StringBuilder devuelve un String nuevo con la porción indicada; el StringBuilder original queda intacto. Si quisieras recortar el StringBuilder, tendrías que usar delete.

  12. Usaría +. En una sola línea, el compilador optimiza la concatenación a un StringBuilder interno, así que es igual de eficiente que escribirlo manualmente y queda más legible.

  13. Porque append está sobrecargado con muchas versiones (append(int), append(double), append(boolean), append(char), append(Object), etc.). Cada versión convierte su argumento a String (usando String.valueOf) y lo añade al buffer. Esto evita tener que llamar a String.valueOf manualmente.

  14. Aproximadamente 1000 (uno por iteración, el informe intermedio). En realidad el doble si contamos el String que se descarta y el nuevo que se asigna, pero la idea es: cada iteración produce un String nuevo y descarta el anterior.

  15. String.join(", ", "a", "b", "c"); o, con un array, String.join(", ", new String[]{"a","b","c"});.

Evaluación del cuestionario

Aciertos Nivel
14-15 Dominas la sección. Adelante con la siguiente.
10-13 Bien, repasa los puntos fallados.
6-9 Repasa la sección, especialmente cuándo usar cada alternativa.
Menos de 6 Vuelve a leer con calma y ejecuta el ejemplo integrador paso a paso en IntelliJ.

13. Información ampliada y próximos pasos

Próximos pasos:

  1. Practica en IntelliJ. Copia el ejemplo integrador y modifícalo: cambia los nombres, los separadores, prueba a construir un pequeño informe con varias líneas usando \n como separador.
  2. Compara rendimiento. Si te animas, escribe dos versiones de un programa que concatene 10.000 números en un String: una con + en bucle y otra con StringBuilder. Mide el tiempo con System.currentTimeMillis() antes y después. Verás la diferencia.
  3. Avanza a la Sección 8 de UD2. Wrappers y la clase Objects: las clases Integer, Double, Boolean, Character; métodos útiles como Integer.parseInt, Double.isNaN; el autoboxing/unboxing y sus riesgos; y los métodos estáticos de Objects como requireNonNull e isNull. Es una sección corta pero conceptualmente importante, porque cierra el bloque de "clases predefinidas del API de Java".