Tabla de Contenidos

02.4 - Estructuras de datos estáticas

1. String

Un String representa una cadena de caracteres (texto). Aunque en Java es un objeto, conceptualmente se comporta como una secuencia de caracteres inmutable y de tamaño fijo.

// Inicialización directa (literal)
String nombre = "Ana";

// Mediante la palabra clave new (crea un objeto explícito en memoria)
String apellido = new String("García");

Inmutabilidad

Una propiedad fundamental de los String es que son inmutables: una vez creada la cadena en memoria, su contenido no se puede modificar.

Cualquier método que apliquemos sobre un String (convertir a mayúsculas, reemplazar letras, etc.) no modifica el texto original, sino que devuelve un nuevo String con la transformación.

String texto = "hola";
texto.toUpperCase(); // NO modifica la variable 'texto'
IO.println(texto);   // Muestra "hola"

// Para conservar el resultado, debemos reasignarlo a la variable:
texto = texto.toUpperCase();
IO.println(texto);   // Muestra "HOLA"

Acceso y longitud

A diferencia de los arrays donde usamos corchetes [] y la propiedad .length, en los String utilizamos métodos:

String mensaje = "Java";

IO.println("Longitud: " + mensaje.length());            // Muestra 4
IO.println("Primer carácter: " + mensaje.charAt(0));     // Muestra 'J'
IO.println("Último carácter: " + mensaje.charAt(mensaje.length() - 1)); // Muestra 'a'

Comparación de Strings

En Java NUNCA debemos usar el operador == para comparar el contenido de dos cadenas. El operador == comprueba si ambas variables apuntan a la misma posición de memoria, no si contienen las mismas letras.

Para comparar texto debemos usar el método .equals() o .equalsIgnoreCase() (para ignorar mayúsculas y minúsculas):

String texto1 = IO.readln("Introduce una palabra: "); // Ejemplo: "hola"
String texto2 = "hola";

// INCORRECTO: Compara referencias de memoria (daría false casi siempre)
if (texto1 == texto2) { 
    IO.println("Apuntan al mismo objeto en memoria");
}

// CORRECTO: Compara el contenido de las cadenas
if (texto1.equals(texto2)) {
    IO.println("El texto es exactamente igual");
}

// Compara ignorando mayúsculas y minúsculas
if (texto1.equalsIgnoreCase("HOLA")) {
    IO.println("Coinciden sin importar mayúsculas/minúsculas");
}

Métodos principales de String

La clase String dispone de multitud de métodos para manipular texto de forma sencilla:

Método Descripción Ejemplo Resultado
contains(texto) Comprueba si contiene una subcadena “Programación”.contains(“grama”) true
indexOf(texto) Devuelve la posición de la primera aparición (o -1 si no existe) “Java”.indexOf(“v”) 2
substring(inicio, fin) Extrae el texto desde inicio hasta fin - 1 “Elefante”.substring(0, 3) “Ele”
toLowerCase() / toUpperCase() Convierte todo el texto a minúsculas / mayúsculas “Java”.toLowerCase() “java”
replace(viejo, nuevo) Reemplaza un texto por otro “Casa”.replace(“a”, “o”) “Coso”
trim() Elimina espacios en blanco al inicio y al final “ Hola ”.trim() “Hola”

Recorrido y conversión a char[]

Podemos recorrer un String carácter a carácter utilizando un bucle for con charAt(), o convirtiéndolo directamente a un array de caracteres (char[]):

String palabra = "Algoritmo";

// Opción 1: Recorrido con bucle for tradicional y charAt()
for (int i = 0; i < palabra.length(); i++) {
    char letra = palabra.charAt(i);
    IO.println("Posición " + i + ": " + letra);
}

// Opción 2: Convertir a char[] y recorrer con for-each
char[] letras = palabra.toCharArray();
for (char letra : letras) {
    IO.print(letra + " ");
}
IO.println("");

2. Arrays

Los array permiten almacenar un conjunto finito de elementos del mismo tipo con un tamaño fijo asignado desde su creación.

Las posiciones se numeran desde la 0 hasta tamaño - 1. Para acceder o modificar un valor usamos los corchetes [].

        int[] numbers;

        numbers = new int[10];
        
        //int[] numbers = new int[10];

Para almacenar valores en cada una de las posiciones del array, usamos corchetes ([]) indicando en su interior la posición en la que queremos leer o escribir.

numbers[3] = 10;

Para acceder a los datos de un array accedemos mediante nombre[posicion].

        int number;

        number = numbers[3];

Propiedad length y recorridos

La propiedad length nos devuelve el número total de elementos. Podemos recorrer el array mediante un bucle for convencional o con un bucle for-each:

// Recorrido con for tradicional
for (int i = 0; i < numbers.length; i++) {
    IO.println("Índice " + i + ": " + numbers[i]);
}

// Recorrido con for-each (más limpio para lectura)
for (int number : numbers) {
    IO.println(number);
}

Inicializar array:

        int[] numbers = new int[5];
        
        for(int i=0; i<numbers.length; i++) {
            numbers[i] = Integer.parseInt(IO.readln("Introduce un número entero: "));
        }

Otra forma de inicializar un array es definiendo sus valores como una lista separada por comas encerrada entre llaves. Ésto lo debemos hacer a la hora de la declaración del array, y no hace falta indicarle la longitud, ya que se lo indicamos con el número de elementos que le pasemos:

int[] numbers = {1, 3, 312, 15, 69, 7, 8, 9, 0, 1};

Arrays multidimensionales

Podemos declarar un array de más de una dimensión (como tablas o matrices):

// Inicialización directa
int[][] table = {
    {3, 5, 6},
    {2, 4, 9}
};

// Recorrido con bucles anidados
for (int[] row : table) {
    for (int cell : row) {
        IO.print(cell + " ");
    }
    IO.println("");
}

Paso de arrays a funciones

A diferencia de los tipos primitivos, los arrays son objetos. Al pasarlos como parámetro a un método, se copia la referencia a la memoria original. Por tanto, cualquier cambio dentro del método modificará el array original.

static void main() {
    int[] numbers = {1, 2, 3};
    updateFirst(numbers);
    IO.println(numbers[0]); // Muestra 99
}


static void updateFirst(int[] data) {
    data[0] = 99; // Modifica el array original
}