¿Es más rápido usar un array nativo o una colección como ArrayList? Aunque las colecciones encapsulan estructuras de datos optimizadas, su implementación interna suele basarse en arrays. Este artículo evalúa experimentalmente el rendimiento en operaciones de escritura y lectura con un millón de elementos.
Rendimiento en escritura
El siguiente código mide el tiempo para asignar 1.000.000 de elementos a un array de cadenas y a un ArrayList. Se incluye un bucle de calentamiento para estabilizar JVM y CPU.
public class PerformanceTest {
public static void main(String[] args) {
// Calentamiento
long sum = 0;
for (int i = 0; i < 1_000_000; i++) {
sum += i;
}
System.out.println(sum);
String[] nativeArray = new String[1_000_000];
java.util.Collection<String> arrayList = new java.util.ArrayList<>();
long start = System.currentTimeMillis();
for (int i = 0; i < 1_000_000; i++) {
nativeArray[i] = String.valueOf(i);
}
System.out.println("Array write time: " + (System.currentTimeMillis() - start) + " ms");
start = System.currentTimeMillis();
for (int i = 0; i < 1_000_000; i++) {
arrayList.add(String.valueOf(i));
}
System.out.println("ArrayList write time: " + (System.currentTimeMillis() - start) + " ms");
}
}
Resultado típico:
1783293664
Array write time: 39 ms
ArrayList write time: 44 ms
En operaciones de escritura secuencial, el array nativo supera ligeramente al ArrayList.
Rendimiento en lectura
Ahora evaluamos la lectura aleatoria de elementos. Primero se llenan ambas estructuras, luego se busca un conjunto pequeño de valores.
import java.util.ArrayList;
import java.util.List;
public class ReadTest {
public static void main(String[] args) {
int dummy = 0;
for (int i = 0; i < 1_000_000; i++) {
dummy += i;
}
System.out.println(dummy);
String[] arr = new String[1_000_000];
List<String> list = new ArrayList<>();
// Llenar colección primero (orden invertido para evitar sesgo)
long start = System.currentTimeMillis();
for (int i = 0; i < 1_000_000; i++) {
list.add(String.valueOf(i));
}
System.out.println("List fill: " + (System.currentTimeMillis() - start) + " ms");
start = System.currentTimeMillis();
for (int i = 0; i < 1_000_000; i++) {
arr[i] = String.valueOf(i);
}
System.out.println("Array fill: " + (System.currentTimeMillis() - start) + " ms");
// Valores a buscar
List<String> targets = List.of("5", "999999", "439999", "666666");
start = System.currentTimeMillis();
for (String t : targets) {
if (list.contains(t)) {
dummy++;
}
}
System.out.println("List contains (4 lookups): " + (System.currentTimeMillis() - start) + " ms");
start = System.currentTimeMillis();
for (String t : targets) {
for (int i = 0; i < arr.length; i++) {
if (t.equals(arr[i])) {
dummy++;
break;
}
}
}
System.out.println("Array linear search (4 lookups): " + (System.currentTimeMillis() - start) + " ms");
}
}
Ejecuciones típicas:
1783293664
List fill: 50 ms
Array fill: 59 ms
List contains (4 lookups): 15 ms
Array linear search (4 lookups): 13 ms
La variación entre ejecuciones se debe a la planificación de JVM/CPU, pero en general el array muestra un rendimiento ligeramente superior tanto en escritura como en lectura.
Conclusión
Para volúmenes de un millón de registros la diferencia es marginal. Las colecciones ofrecen métodos convenientes (búsqueda binaria, inserción, etc.) y son más flexibles. Si el rendimiento es crítico (ej. operaciones en tiempo real o microservicios de alta frecuencia), los arrays nativos pueden ser una opción más eficiente. En la mayoría de casos prácticos, la comodidad de las colecciones compensa la mínima pérdida de rendimiento.