Skip to content

Ejercicios Prácticos: Fundamentos de Estructuras de Datos en Java

Introducción al Proyecto: Herramientas de Línea de Comandos para "DevTune"

Bienvenido al equipo de herramientas internas de "DevTune", nuestra nueva app de música. Antes de construir la aplicación principal, necesitamos crear una serie de pequeñas utilidades de línea de comandos (CLI) para gestionar y procesar datos de usuarios, playlists y canciones. En estos ejercicios, desarrollarás programas interactivos que se ejecutan en la consola, solicitando datos al usuario y manipulándolos usando las estructuras de datos más fundamentales: arrays, matrices y cadenas. ¡Prepárate para construir herramientas prácticas desde cero!



Ejercicios de Consolidación


1) Creador de Playlists Fijas | Nivel: Fácil

Objetivo:

Utilizar arrays unidimensionales para almacenar una colección de datos de tamaño fijo introducidos por el usuario.

Tarea a realizar:
  1. Crea un programa que anuncie la creación de una "Playlist de 5 canciones".
  2. Declara un array de String con un tamaño de 5.
  3. Usa un bucle for para pedirle al usuario que introduzca el nombre de una canción 5 veces. Cada nombre debe guardarse en una posición del array.
  4. Una vez que el array esté lleno, usa un bucle for-each para imprimir la playlist completa, precediendo cada canción con su número de pista (ej: "1. Bohemian Rhapsody").
Aplicación en el Mundo Real:

Muchas aplicaciones manejan colecciones de datos con un tamaño predefinido, como los días de la semana, los meses del año o, en un contexto de hardware, la configuración de un número fijo de sensores. Entender los arrays de tamaño fijo es el primer paso antes de manejar datos dinámicos.


2) Analizador de Títulos de Canciones | Nivel: Fácil

Objetivo:

Utilizar métodos esenciales de la clase String para extraer información y analizar texto introducido por el usuario.

Tarea a realizar:
  1. Pide al usuario que introduzca el título de una canción.
  2. Muestra en pantalla la siguiente información:
    • El número total de caracteres (longitud del String).
    • El primer carácter del título.
    • El último carácter del título.
    • El título convertido completamente a mayúsculas.
    • El título convertido completamente a minúsculas.
Aplicación en el Mundo Real:

El procesamiento de texto es una de las tareas más comunes en programación. Desde validar un email, buscar una subcadena en un log, hasta formatear nombres de usuario, los métodos de la clase String se usan constantemente para manipular y verificar datos textuales.


3) Tablero de Tres en Raya | Nivel: Fácil

Objetivo:

Utilizar un array bidimensional (matriz) para representar un estado, como un tablero de juego.

Tarea a realizar:
  1. Crea una matriz de caracteres de 3x3 (char[][]).
  2. Inicializa todas las celdas del tablero con un guion (-) para representar que están vacías.
  3. Modifica el tablero para simular un par de movimientos: coloca una 'X' en la posición central (fila 1, columna 1) y una 'O' en la esquina superior izquierda (fila 0, columna 0).
  4. Crea una función estática imprimirTablero(char[][] tablero) que reciba la matriz y la imprima en la consola de forma ordenada, fila por fila.
  5. Llama a esta función desde main para mostrar el estado final del tablero.
Aplicación en el Mundo Real:

Las matrices son la base para representar cualquier tipo de datos en forma de cuadrícula: desde mapas en videojuegos, hojas de cálculo, hasta imágenes digitales (que son matrices de píxeles) o representaciones de redes.


4) Generador de Nombres de Usuario | Nivel: Fácil

Objetivo:

Crear un String a partir de piezas de otros String introducidos por el usuario, usando concatenación y métodos de subcadena.

Tarea a realizar:
  1. Pide al usuario su nombre.
  2. Pide al usuario su apellido.
  3. Pide al usuario su año de nacimiento.
  4. Genera un nombre de usuario combinando:
    • Las tres primeras letras del nombre, en minúsculas.
    • Las tres primeras letras del apellido, en minúsculas.
    • Los dos últimos dígitos del año de nacimiento.
  5. Muestra el nombre de usuario generado. Por ejemplo, si el usuario introduce "Ana", "López" y "1998", el programa debería mostrar "analop98".
Aplicación en el Mundo Real:

Muchos sistemas de registro automático generan identificadores únicos o sugerencias de nombres de usuario combinando datos proporcionados por el propio usuario, asegurando un formato consistente en toda la plataforma.


5) Calculadora de Estadísticas de Puntuaciones | Nivel: Fácil

Objetivo:

Recorrer un array de números para realizar cálculos básicos como encontrar el mínimo, el máximo y la media.

Tarea a realizar:
  1. Crea un array de int con las siguientes puntuaciones de un test de música: {8, 9, 7, 10, 8, 9, 9, 6, 5, 10}.
  2. Recorre el array para calcular:
    • La puntuación más alta.
    • La puntuación más baja.
    • La suma total de todas las puntuaciones.
  3. Calcula la media (recuerda que la media puede tener decimales).
  4. Imprime todos los resultados de forma clara.
Aplicación en el Mundo Real:

El procesamiento de listas de números para extraer estadísticas es una tarea fundamental en el análisis de datos, finanzas, ciencia e ingeniería. Desde calcular la media de ventas de un producto hasta encontrar el valor máximo de un sensor, el recorrido de arrays es la base de todo.


6) Divisor de Tags de Géneros Musicales | Nivel: Fácil

Objetivo:

Utilizar el método split() de la clase String para convertir una cadena de texto en un array de cadenas.

Tarea a realizar:
  1. Pide al usuario que introduzca una lista de géneros musicales separados por comas (ej: "Rock, Pop, Jazz, Clásica, Hip-Hop").
  2. Utiliza el método split(",") para dividir la entrada del usuario en un array de String.
  3. Recorre el nuevo array y muestra cada género en una línea separada, eliminando posibles espacios en blanco al principio o al final de cada género con el método trim().
Aplicación en el Mundo Real:

El método split() es extremadamente útil para procesar datos que vienen en formatos estructurados como CSV (valores separados por comas) o logs, donde un carácter específico se usa como delimitador para separar diferentes campos de información.


7) Pasando la Lista de Canciones por Referencia | Nivel: Fácil

Objetivo:

Observar cómo la modificación de un array dentro de una función afecta al array original fuera de ella (paso por referencia).

Tarea a realizar:
  1. En el método main, crea un array de String con tres canciones.
  2. Imprime el contenido del array usando Arrays.toString().
  3. Crea una función estática public static void capitalizarTitulos(String[] playlist) que reciba el array.
  4. Dentro de esta función, usa un bucle para recorrer el array y convertir cada título de canción a mayúsculas. No devuelvas nada (void).
  5. Llama a esta función desde main, pasándole tu array de canciones.
  6. Después de llamar a la función, vuelve a imprimir el array en main. Observa y explica por qué ha cambiado si la función no devolvía nada.
Aplicación en el Mundo Real:

Entender el paso por referencia es crucial para evitar bugs inesperados. En aplicaciones grandes, muchas partes del código pueden tener acceso al mismo objeto de datos (como el perfil de un usuario). Si una función lo modifica sin que el programador sea consciente, puede causar efectos secundarios difíciles de depurar.


8) Debugging: Índice Fuera de Límites | Nivel: Fácil

Objetivo:

Encontrar y corregir un error común en el manejo de arrays: ArrayIndexOutOfBoundsException.

Setup Inicial (Código con Bug):
public class DebuggingIndices {
    public static void main(String[] args) {
        String[] topArtistas = {"Queen", "Led Zeppelin", "The Beatles", "Pink Floyd"};

        // El objetivo es imprimir la lista de artistas con su posición (del 1 al 4)
        System.out.println("--- Top Artistas de DevTune ---");
        for (int i = 1; i <= topArtistas.length; i++) {
            // Hay un error en la siguiente línea
            System.out.println(i + ". " + topArtistas[i]);
        }
    }
}
Tarea a realizar:
  1. Copia, compila y ejecuta el código anterior. Observarás que lanza una excepción.
  2. Lee el mensaje de error. ¿Qué significa ArrayIndexOutOfBoundsException y en qué valor de i ocurre el problema?
  3. Explica en un comentario por qué el bucle for y el acceso al array topArtistas[i] están causando este error.
  4. Corrige el código para que imprima correctamente la lista numerada del 1 al 4 sin lanzar una excepción.
Aplicación en el Mundo Real:

Los errores "off-by-one" (error por uno) son una de las fuentes de bugs más comunes al trabajar con bucles y arrays. Comprender que los índices de los arrays en Java van de 0 a longitud - 1 es absolutamente fundamental para escribir código correcto y robusto.



Ejercicios de Refuerzo


9) Buscador de Artistas | Nivel: Medio

Objetivo:

Recorrer un array para buscar un elemento específico introducido por el usuario.

Tarea a realizar:
  1. Define un array de String con una lista de nombres de artistas: {"Daft Punk", "Gorillaz", "The Chemical Brothers", "Massive Attack"}.
  2. Pide al usuario que introduzca el nombre de un artista que desea buscar.
  3. Recorre el array para comprobar si el artista introducido se encuentra en la lista.
  4. Utiliza equalsIgnoreCase() para que la búsqueda no distinga entre mayúsculas y minúsculas (ej: buscar "daft punk" debería encontrar "Daft Punk").
  5. Muestra un mensaje indicando si el artista fue encontrado y, si es así, en qué posición del ranking (índice + 1). Si no, informa al usuario que no se encontró.

  6. Pista: Necesitarás una variable booleana (ej: boolean encontrado = false;) y una variable para guardar el índice, que puedes cambiar dentro del bucle si encuentras una coincidencia.

Aplicación en el Mundo Real:

La búsqueda de elementos en una colección es una operación omnipresente. Desde buscar un producto en una tienda online, encontrar un fichero en un sistema de archivos, hasta verificar si un usuario existe en una base de datos. La búsqueda lineal (recorrer uno a uno) es el algoritmo más simple para ello.


10) Matriz de Horarios de Conciertos | Nivel: Medio

Objetivo:

Manejar una matriz para almacenar y consultar datos tabulados, interactuando con el usuario para obtener las coordenadas.

Tarea a realizar:
  1. Crea una matriz de String de 4x3 para representar un horario de conciertos. Las filas representan 4 escenarios ("Principal", "Alternativo", "Electrónico", "Acústico") y las columnas 3 franjas horarias ("18:00", "20:00", "22:00").
  2. Rellena la matriz con nombres de artistas ficticios.
  3. Muestra al usuario los escenarios y las franjas horarias disponibles numeradas (ej: "Escenario 1: Principal", "Hora 1: 18:00").
  4. Pide al usuario que introduzca el número del escenario (1-4) y el número de la franja horaria (1-3) que desea consultar.
  5. Valida que la entrada del usuario es correcta (resta 1 para obtener el índice del array). Si es inválida, muestra un error.
  6. Si la entrada es válida, muestra el artista que toca en ese escenario y a esa hora.

  7. Pista: Recuerda que los índices de los arrays van de 0 a N-1. Si el usuario introduce 1, deberás acceder al índice 0.

Aplicación en el Mundo Real:

Cualquier sistema que muestre información en tablas, como horarios de clases, parrillas de programación de TV, calendarios o tablas de precios, utiliza internamente una estructura de datos bidimensional para almacenar y acceder a la información de forma eficiente a través de sus coordenadas.


11) Cifrador César Simple | Nivel: Medio

Objetivo:

Manipular un String a nivel de caracteres, convirtiéndolo a un array de char, modificándolo y reconstruyéndolo.

Tarea a realizar:
  1. Pide al usuario un mensaje secreto.
  2. Pide al usuario un número para el desplazamiento (la "clave" del cifrado).
  3. Convierte el mensaje a un array de caracteres usando toCharArray().
  4. Recorre el array de caracteres. Para cada carácter, si es una letra, súmale el desplazamiento. (Ej: cifrado[i] = (char) (original[i] + desplazamiento);).
  5. Convierte el array de caracteres modificado de nuevo a un String.
  6. Muestra el mensaje cifrado.

  7. Pista: Este cifrado simple no manejará el "desbordamiento" del alfabeto (si 'Z' + 2 debería ser 'B'). No te preocupes por eso, el objetivo es practicar la manipulación del array de caracteres. Solo cifra las letras para evitar modificar espacios o símbolos. Puedes usar Character.isLetter().

Aplicación en el Mundo Real:

El cifrado de datos es un campo enorme de la seguridad informática. Aunque el Cifrado César es muy simple, el principio de transformar datos a nivel de sus componentes básicos (caracteres, bytes) es fundamental en algoritmos de encriptación y compresión de datos mucho más complejos y seguros.


12) Función de Suma con Varargs | Nivel: Medio

Objetivo:

Crear y utilizar una función que acepte un número variable de argumentos (varargs).

Tarea a realizar:
  1. Crea una función estática public static int sumar(int... numeros) que pueda sumar una cantidad variable de números enteros.
  2. Dentro de la función, los varargs se comportan como un array. Usa un bucle for-each para recorrerlos y calcular la suma total.
  3. Devuelve la suma.
  4. En el método main, demuestra que tu función sumar funciona correctamente llamándola de diferentes maneras:
    • Sin argumentos: sumar()
    • Con dos argumentos: sumar(10, 20)
    • Con cinco argumentos: sumar(5, 10, 15, 20, 25)
  5. Imprime el resultado de cada llamada para verificar que son correctos (0, 30 y 75).
Aplicación en el Mundo Real:

Los varargs son muy útiles para crear APIs o librerías más flexibles y fáciles de usar. Funciones de logging (donde puedes pasar varios datos a registrar), constructores de consultas a bases de datos, o métodos para añadir múltiples elementos a una colección de una sola vez son casos de uso comunes para esta característica.



Ejercicios de Ampliación


13) Batalla Naval Simplificada | Nivel: Alto

Objetivo:

Diseñar e implementar un juego interactivo simple en la consola, utilizando una matriz para el tablero y manejando la lógica del juego, incluyendo la entrada del usuario y la validación.

Tarea a realizar:
  1. Crea un tablero de juego de 5x5 usando una matriz char[][].
  2. Inicializa el tablero con un carácter que represente el agua (ej: ~).
  3. "Esconde" un barco de 1x1 en una posición aleatoria del tablero. Para ello, genera dos números aleatorios entre 0 y 4 (para la fila y la columna) y coloca una 'B' en esa celda.
  4. Crea un bucle de juego que le permita al usuario 5 intentos para encontrar el barco.
  5. En cada intento, pide al usuario una coordenada de fila y una de columna (0-4).
  6. Comprueba la celda elegida por el usuario:
    • Si contiene la 'B', el usuario ha ganado. Imprime un mensaje de victoria y termina el juego.
    • Si no, marca esa celda con una 'X' (para "fallo") e informa al usuario. Imprime el tablero actualizado para que vea sus intentos.
  7. Si el usuario agota sus 5 intentos sin encontrar el barco, el juego termina. Imprime un mensaje de derrota y revela dónde estaba el barco.
Revisión por Pares (Peer Review):

Intercambia tu código con un compañero. Revisa su implementación:
* ¿El código es claro y está bien comentado?
* ¿Maneja correctamente la entrada del usuario? ¿Qué pasa si el usuario introduce un número fuera del rango (ej: 5) o texto en lugar de un número?
* ¿La lógica del bucle de juego es correcta? ¿Termina en las condiciones adecuadas (victoria o derrota)?
* Sugiere una mejora, como permitir barcos más grandes (de 2 o 3 celdas) o añadir un segundo jugador.

Aplicación en el Mundo Real:

El desarrollo de juegos es un campo importante de la programación. Incluso un juego simple como este requiere una gestión cuidadosa del "estado" (la posición de los barcos, los intentos del jugador), la lógica de las reglas y la interacción con el usuario. Es un excelente ejercicio para combinar todo lo aprendido sobre matrices, bucles, condicionales y entrada/salida.


14) Analizador de Palíndromos | Nivel: Alto

Objetivo:

Resolver un problema clásico de algoritmia de cadenas, prestando atención a la "limpieza" de los datos de entrada antes de procesarlos.

Tarea a realizar:
  1. Investigación: Un palíndromo es una palabra o frase que se lee igual de izquierda a derecha que de derecha a izquierda, ignorando espacios, puntuación y mayúsculas/minúsculas. Ejemplos: "Anita lava la tina", "Radar", "A man, a plan, a canal: Panama".
  2. Crea un programa que pida al usuario una frase.
  3. Limpieza de datos: Antes de verificar, debes procesar la entrada del usuario:
    • Conviértela a minúsculas.
    • Elimina todos los caracteres que no sean letras (espacios, comas, etc.). Puedes hacerlo creando un String nuevo y recorriendo el original, añadiendo solo los caracteres que sean letras.
  4. Verificación: Implementa una función public static boolean esPalindromo(String texto) que reciba el texto ya limpio.
    • Dentro de la función, la forma más eficiente es usar dos "punteros" (variables de índice): uno que empiece en 0 y otro en texto.length() - 1.
    • Compara los caracteres en ambas posiciones. Si son diferentes, no es un palíndromo y puedes devolver false.
    • Si son iguales, mueve los punteros uno hacia el otro (el del inicio se incrementa, el del final se decrementa) y repite hasta que se crucen.
    • Si el bucle termina sin encontrar diferencias, es un palíndromo y debes devolver true.
  5. Llama a la función desde main y muestra al usuario si su frase es un palíndromo o no.
Revisión por Pares (Peer Review):

Intercambia tu código con un compañero.
* ¿Su método de "limpieza" es robusto? ¿Qué pasa con los acentos o caracteres especiales como 'ñ'? (No es necesario que lo resuelva, pero es un buen punto de discusión).
* ¿La lógica de los dos punteros es correcta y eficiente? ¿Podría haber una forma más simple pero menos eficiente de hacerlo (ej: creando un String inverso)?
* ¿El código es legible? ¿Los nombres de las variables son claros?

Aplicación en el Mundo Real:

Este tipo de problema, aunque parezca académico, entrena habilidades cruciales en el mundo real: la limpieza y normalización de datos de entrada (un paso esencial en casi cualquier aplicación que procese datos de usuario), y la implementación de algoritmos eficientes para el procesamiento de datos. Los algoritmos de dos punteros son una técnica común para resolver muchos problemas de arrays y cadenas de manera óptima.