Skip to content

Instantly share code, notes, and snippets.

@RaniAgus
Last active October 10, 2025 02:20
Show Gist options
  • Select an option

  • Save RaniAgus/883388e4db0810e0f8c919529af3a2d2 to your computer and use it in GitHub Desktop.

Select an option

Save RaniAgus/883388e4db0810e0f8c919529af3a2d2 to your computer and use it in GitHub Desktop.
String.concat vs StringBuilder.append en Java
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
List<String> pocosStrings = Arrays.asList("0", null, "2", null, null, "5");
List<String> muchosStrings = Stream.generate(() -> pocosStrings)
.flatMap(List::stream)
.limit(1000000)
.collect(Collectors.toList());
streamConLogs(pocosStrings);
stream(muchosStrings);
reduceConLogs(pocosStrings);
reduce(muchosStrings);
}
private static void streamConLogs(List<String> strings) {
System.out.println("============= STREAM =============");
String errores = strings.stream()
.map(Optional::ofNullable)
.filter(it -> {
System.out.println("Voy a filtrar: " + it);
return it.isPresent();
})
.map(it -> {
System.out.println("Voy a concatenar: " + it.get());
return it.get();
})
.collect(Collectors.joining());
System.out.println("Resultado: " + errores);
}
private static String stream(List<String> strings) {
System.out.println("============= STREAM =============");
long startTime = System.currentTimeMillis();
String errores = strings.stream()
.map(Optional::ofNullable)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.joining());
long endTime = System.currentTimeMillis();
System.out.println("That took " + (endTime - startTime) + " milliseconds");
return errores;
}
private static void reduceConLogs(List<String> strings) {
System.out.println("============= REDUCE =============");
String errores = strings.stream()
.reduce("", (errorsAccumulator, currentValidacion) -> {
Optional<String> error = Optional.ofNullable(currentValidacion);
System.out.println("Voy a filtrar: " + error);
if (error.isPresent()) {
System.out.println("Voy a concatenar: " + error.get());
errorsAccumulator += error.get();
}
return errorsAccumulator;
}, (s1, s2) -> {
System.out.println("Voy a concatenar: " + s2);
return s1.concat(s2);
});
System.out.println("Resultado: " + errores);
}
private static String reduce(List<String> strings) {
System.out.println("============= REDUCE =============");
long startTime = System.currentTimeMillis();
String errores = strings.stream().reduce(
"",
(errorsAccumulator, currentValidacion) -> {
Optional<String> error = Optional.ofNullable(currentValidacion);
if (error.isPresent()) {
errorsAccumulator += error.get();
}
return errorsAccumulator;
},
String::concat
);
long endTime = System.currentTimeMillis();
System.out.println("That took " + (endTime - startTime) + " milliseconds");
return errores;
}
}
============= STREAM =============
Voy a filtrar: Optional[0]
Voy a concatenar: 0
Voy a filtrar: Optional.empty
Voy a filtrar: Optional[2]
Voy a concatenar: 2
Voy a filtrar: Optional.empty
Voy a filtrar: Optional.empty
Voy a filtrar: Optional[5]
Voy a concatenar: 5
Resultado: 025
============= STREAM =============
That took 32 milliseconds
============= REDUCE =============
Voy a filtrar: Optional[0]
Voy a concatenar: 0
Voy a filtrar: Optional.empty
Voy a filtrar: Optional[2]
Voy a concatenar: 2
Voy a filtrar: Optional.empty
Voy a filtrar: Optional.empty
Voy a filtrar: Optional[5]
Voy a concatenar: 5
Resultado: 025
============= REDUCE =============
That took 25662 milliseconds
Process finished with exit code 0

Solía pensar que la razón por la que los Streams eran más rápidos en este escenario era porque podían procesarse en paralelo...

Pero en realidad los streams son secuenciales.

¿Entonces por qué es más rápido?

La razón es que Collectors.joining() usa StringBuilder por detrás. No forma un string con cada concatenación, sino que colecta y luego lo retorna.

PD: El uso de Optional es al pedo...

import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;
class ZMain {
public static void main(String[] args) {
List<String> pocosStrings = Arrays.asList("0", null, "2", null, null, "5");
List<String> muchosStrings = Stream.generate(() -> pocosStrings)
.flatMap(List::stream)
.limit(1000000)
.collect(Collectors.toList());
streamConLogs(pocosStrings);
stream(muchosStrings);
reduceConLogs(pocosStrings);
reduce(muchosStrings);
}
private static void streamConLogs(List<String> strings) {
System.out.println("============= STREAM =============");
String errores = strings.stream()
.filter(it -> {
System.out.println("Voy a filtrar: " + it);
return it != null;
})
.peek(it -> System.out.println("Voy a concatenar: " + it))
.collect(Collectors.joining());
System.out.println("Resultado: " + errores);
}
private static String stream(List<String> strings) {
System.out.println("============= STREAM =============");
long startTime = System.currentTimeMillis();
String errores = strings.stream()
.filter(Objects::nonNull)
.collect(Collectors.joining());
long endTime = System.currentTimeMillis();
System.out.println("That took " + (endTime - startTime) + " milliseconds");
return errores;
}
private static void reduceConLogs(List<String> strings) {
System.out.println("============= REDUCE =============");
String errores = strings.stream()
.reduce(new StringBuilder(), (errorsAccumulator, currentValidacion) -> {
System.out.println("Voy a filtrar: " + currentValidacion);
if (currentValidacion == null) {
return errorsAccumulator;
}
System.out.println("Voy a concatenar: " + currentValidacion);
return errorsAccumulator.append(currentValidacion);
}, (s1, s2) -> {
System.out.println("Voy a concatenar: " + s2);
return s1.append(s2);
})
.toString();
System.out.println("Resultado: " + errores);
}
private static String reduce(List<String> strings) {
System.out.println("============= REDUCE =============");
long startTime = System.currentTimeMillis();
String errores = strings.stream()
.reduce(new StringBuilder(), (b, s) -> s == null ? b : b.append(s), StringBuilder::append)
.toString();
long endTime = System.currentTimeMillis();
System.out.println("That took " + (endTime - startTime) + " milliseconds");
return errores;
}
}
============= STREAM =============
Voy a filtrar: 0
Voy a concatenar: 0
Voy a filtrar: null
Voy a filtrar: 2
Voy a concatenar: 2
Voy a filtrar: null
Voy a filtrar: null
Voy a filtrar: 5
Voy a concatenar: 5
Resultado: 025
============= STREAM =============
That took 13 milliseconds
============= REDUCE =============
Voy a filtrar: 0
Voy a concatenar: 0
Voy a filtrar: null
Voy a filtrar: 2
Voy a concatenar: 2
Voy a filtrar: null
Voy a filtrar: null
Voy a filtrar: 5
Voy a concatenar: 5
Resultado: 025
============= REDUCE =============
That took 12 milliseconds
Process finished with exit code 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment