Optimización de código

Tiempo de Acceso

El tiempo de acceso es un factor clave en la optimización del código. Dependiendo del tipo de acceso que se realice, el rendimiento del programa puede verse afectado significativamente. Algunos de los accesos más costosos incluyen:

  1. Acceso a bases de datos
  2. Acceso a archivos en disco
  3. Acceso a memoria (RAM vs caché)
  4. Acceso a redes (API, peticiones HTTP)

Optimizar estos accesos reduce el tiempo de ejecución del programa y mejora su eficiencia.


1. Acceso a Archivos: Minimizar las Operaciones de Entrada Salida (I O)

La lectura y escritura en disco son operaciones lentas en comparación con la memoria RAM. Para mejorar la eficiencia, debemos minimizar los accesos al disco.

Ejemplo incorrecto: Lectura de un archivo byte a byte

Este código lee el archivo una línea a la vez, lo cual es ineficiente si se debe leer un archivo grande.

1import java.io.*;
2
3public class AccesoArchivo {
4    void leerArchivoIncorrecto(String ruta) throws IOException {
5        FileReader fr = new FileReader(ruta);
6        int caracter;
7        while ((caracter = fr.read()) != -1) { // Lee carácter por carácter (ineficiente)
8            System.out.print((char) caracter);
9        }
10        fr.close();
11    }
12}

Ejemplo optimizado: Uso de buffers para mejorar la lectura

En lugar de leer carácter por carácter, se lee en bloques grandes, lo que mejora la eficiencia.

1import java.io.*;
2
3public class AccesoArchivo {
4    void leerArchivoOptimizado(String ruta) throws IOException {
5        BufferedReader br = new BufferedReader(new FileReader(ruta));
6        String linea;
7        while ((linea = br.readLine()) != null) { // Lee línea por línea (más eficiente)
8            System.out.println(linea);
9        }
10        br.close();
11    }
12}

2. Acceso a Bases de Datos: Evitar Conexiones Repetidas

Abrir y cerrar conexiones de bases de datos repetidamente puede degradar el rendimiento. Es mejor utilizar una única conexión para múltiples consultas.

Ejemplo incorrecto: Abrir y cerrar la conexión en cada consulta

1import java.sql.*;
2
3public class AccesoBD {
4    void consultaIncorrecta(String query) throws SQLException {
5        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/miDB", "user", "password");
6        Statement stmt = conn.createStatement();
7        ResultSet rs = stmt.executeQuery(query);
8
9        while (rs.next()) {
10            System.out.println(rs.getString("nombre"));
11        }
12        conn.close(); // Cierra la conexión después de cada consulta (ineficiente)
13    }
14}

Ejemplo optimizado: Uso de conexión persistente

Abrimos la conexión una sola vez y la reutilizamos para múltiples consultas.

1import java.sql.*;
2
3public class AccesoBD {
4    private Connection conn;
5
6    public AccesoBD() throws SQLException {
7        this.conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/miDB", "user", "password");
8    }
9
10    void consultaOptimizada(String query) throws SQLException {
11        Statement stmt = conn.createStatement();
12        ResultSet rs = stmt.executeQuery(query);
13
14        while (rs.next()) {
15            System.out.println(rs.getString("nombre"));
16        }
17    }
18
19    void cerrarConexion() throws SQLException {
20        conn.close();
21    }
22}

3. Acceso a Redes: Optimizar Peticiones HTTP

Cada solicitud HTTP tiene un costo alto en latencia, por lo que es mejor evitar peticiones innecesarias y usar caché.

Ejemplo incorrecto: Realizar múltiples peticiones innecesarias

1import java.net.http.*;
2import java.net.URI;
3import java.io.IOException;
4import java.net.http.HttpResponse;
5
6public class Main {
7    public static void main(String[] args) {
8        try {
9            obtenerDatosIncorrecto();
10        } catch (IOException | InterruptedException e) {
11            e.printStackTrace();
12        }
13    }
14
15    public static void obtenerDatosIncorrecto() throws IOException, InterruptedException {
16        HttpClient client = HttpClient.newHttpClient();
17        
18        for (int i = 0; i < 5; i++) {
19            HttpRequest request = HttpRequest.newBuilder()
20                .uri(URI.create("https://api.ejemplo.com/datos"))
21                .GET()
22                .build();
23
24            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
25            System.out.println(response.body());
26        }
27    }
28}

Ejemplo optimizado: Uso de caché para reducir llamadas innecesarias

1import java.net.http.*;
2import java.net.URI;
3import java.io.IOException;
4import java.net.http.HttpResponse;
5
6public class Main {
7    public static void main(String[] args) {
8        try {
9            obtenerDatosOptimizado();
10        } catch (IOException | InterruptedException e) {
11            e.printStackTrace();
12        }
13    }
14
15    public static void obtenerDatosOptimizado() throws IOException, InterruptedException {
16        HttpClient client = HttpClient.newHttpClient();
17        HttpRequest request = HttpRequest.newBuilder()
18            .uri(URI.create("https://api.ejemplo.com/datos"))
19            .GET()
20            .build();
21
22        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
23        String data = response.body(); // Obtener una única respuesta
24
25        for (int i = 0; i < 5; i++) {
26            System.out.println(data); // Usar la misma respuesta en cada iteración
27        }
28    }
29}