Laboratorio 08 — Colecciones en Java - jorgealbertopineda78-del/java GitHub Wiki

Laboratorio 08 — Colecciones en Java

Unidad: 8 — Colecciones
Duración estimada: 90 minutos
Nivel: Intermedio-Avanzado


Conexión con laboratorios anteriores

En los labs anteriores el sistema RPG almacenaba personajes en arreglos de tamaño fijo:

Personaje[] equipo = new Personaje[3];   // tamaño fijo — inflexible
equipo[0] = new Guerrero(...);

Y la bitácora de MotorCombate usaba un ArrayList simple. En un sistema real necesitamos estructuras más poderosas:

  • Un roster de personajes que crezca dinámicamente → ArrayList
  • Una cola de turnos que respete el orden de llegada → LinkedList
  • Un inventario que acceda a items por nombre clave → HashMap
  • Un registro de habilidades únicas sin duplicados → HashSet

Objetivo

  • Usar ArrayList, LinkedList, HashMap y HashSet para resolver problemas reales
  • Recorrer colecciones con for-each e iteradores
  • Elegir la colección correcta según el caso de uso
  • Integrar colecciones al sistema RPG existente

Contexto del problema

El gremio de aventureros necesita un Sistema de Gestión de Gremio que administre:

  1. El roster de miembros activos (puede crecer o encogerse)
  2. La cola de espera para entrar al gremio (orden de llegada)
  3. El inventario de objetos de cada personaje (nombre → cantidad)
  4. El catálogo de habilidades únicas registradas en el gremio

Diagrama UML

┌─────────────────────────────────────────────────────┐
│                   GestionGremio                     │
│  - roster:   ArrayList<Personaje>                   │
│  - colaTurnos: LinkedList<String>                   │
│  - inventario: HashMap<String, Integer>             │
│  - habilidades: HashSet<String>                     │
│                                                     │
│  + agregarMiembro(Personaje)                        │
│  + eliminarMiembro(String nombrePersonaje)          │
│  + buscarPorNombre(String nombre): Personaje        │
│  + mostrarRoster()                                  │
│                                                     │
│  + encolarSolicitante(String nombre)                │
│  + atenderSiguiente(): String                       │
│  + mostrarCola()                                    │
│                                                     │
│  + agregarItem(String item, int cantidad)           │
│  + usarItem(String item)                            │
│  + mostrarInventario()                              │
│                                                     │
│  + registrarHabilidad(String habilidad)             │
│  + tieneHabilidad(String habilidad): boolean        │
│  + mostrarHabilidades()                             │
└─────────────────────────────────────────────────────┘
         │ usa
         ▼
┌───────────────────────┐
│  «abstract» Personaje │  ← clases del Lab06/07
│  Guerrero, Arquero    │
│  Druida, Nigromante   │
└───────────────────────┘

Cuándo usar cada colección

Colección Estructura interna Acceso Duplicados Orden Úsala cuando...
ArrayList Arreglo dinámico O(1) índice Sí Inserción Necesitas acceso rápido por posición
LinkedList Lista doblemente enlazada O(n) Sí Inserción Insertas/eliminas mucho al inicio/final
HashMap Tabla hash O(1) clave No (claves) Ninguno Buscas por clave (nombre → valor)
HashSet Tabla hash O(1) No Ninguno Necesitas elementos únicos

Instrucciones

Parte 1 — ArrayList: el roster del gremio

Crea GestionGremio.java con la sección de roster:

import java.util.ArrayList;
import java.util.Iterator;

public class GestionGremio {

    private ArrayList<Personaje> roster;
    private java.util.LinkedList<String> colaTurnos;
    private java.util.HashMap<String, Integer> inventario;
    private java.util.HashSet<String> habilidades;

    public GestionGremio() {
        roster      = new ArrayList<>();
        colaTurnos  = new java.util.LinkedList<>();
        inventario  = new java.util.HashMap<>();
        habilidades = new java.util.HashSet<>();
    }

    // ──────────────────────────────────────────
    // SECCIÓN 1 — ArrayList: roster de personajes
    // ──────────────────────────────────────────

    public void agregarMiembro(Personaje p) {
        roster.add(p);
        System.out.println("[Gremio] " + p.getNombre() + " se unió al gremio.");
    }

    public void eliminarMiembro(String nombre) {
        Iterator<Personaje> it = roster.iterator();
        while (it.hasNext()) {
            Personaje p = it.next();
            if (p.getNombre().equalsIgnoreCase(nombre)) {
                it.remove();   // forma segura de eliminar durante iteración
                System.out.println("[Gremio] " + nombre + " abandonó el gremio.");
                return;
            }
        }
        System.out.println("[Gremio] No se encontró: " + nombre);
    }

    public Personaje buscarPorNombre(String nombre) {
        for (Personaje p : roster) {       // for-each
            if (p.getNombre().equalsIgnoreCase(nombre)) {
                return p;
            }
        }
        return null;
    }

    public void mostrarRoster() {
        System.out.println("\n=== Roster del Gremio (" + roster.size() + " miembros) ===");
        for (int i = 0; i < roster.size(); i++) {
            Personaje p = roster.get(i);
            System.out.println((i + 1) + ". " + p.getNombre() +
                               " | Nivel: " + p.getNivel() +
                               " | Vida: " + p.getPuntosVida());
        }
    }
}

💡 ¿Por qué usar Iterator en lugar de for-each para eliminar?
Si eliminas un elemento con roster.remove() dentro de un for-each, Java lanza
ConcurrentModificationException. El Iterator tiene el método it.remove() que
es la forma segura de eliminar durante la iteración.


Parte 2 — LinkedList: cola de espera (en clase con el profesor)

Agrega estos métodos a GestionGremio:

// ──────────────────────────────────────────
// SECCIÓN 2 — LinkedList: cola de turnos
// ──────────────────────────────────────────

public void encolarSolicitante(String nombre) {
    colaTurnos.addLast(nombre);    // agrega al final
    System.out.println("[Cola] " + nombre +
                       " en posición " + colaTurnos.size());
}

public String atenderSiguiente() {
    if (colaTurnos.isEmpty()) {
        System.out.println("[Cola] No hay solicitantes en espera.");
        return null;
    }
    String atendido = colaTurnos.removeFirst();   // saca del frente
    System.out.println("[Cola] Atendiendo a: " + atendido);
    return atendido;
}

public void mostrarCola() {
    System.out.println("\n=== Cola de Espera (" + colaTurnos.size() + ") ===");
    int pos = 1;
    for (String nombre : colaTurnos) {    // for-each sobre LinkedList
        System.out.println(pos++ + ". " + nombre);
    }
}

💡 LinkedList como cola (FIFO):
addLast() agrega al final, removeFirst() saca del frente.
Esto implementa el patrón FIFO (First In, First Out) — el primero
en llegar es el primero en ser atendido, igual que una fila real.


Parte 3 — HashMap y HashSet (trabajo independiente)

3a — HashMap: inventario de objetos

Implementa en GestionGremio los métodos del inventario:

  • agregarItem(String item, int cantidad):

    • Si la clave ya existe → suma la cantidad al valor actual con getOrDefault
    • Si no existe → inserta con put
    • Imprime el estado actual del item
  • usarItem(String item):

    • Si el item existe y cantidad > 0 → resta 1 con put
    • Si la cantidad llega a 0 → elimina la entrada con remove
    • Si no existe → imprime mensaje de error
  • mostrarInventario():

    • Itera con for (Map.Entry<String, Integer> entry : inventario.entrySet())
    • Imprime entry.getKey() → entry.getValue()

3b — HashSet: habilidades únicas

Implementa en GestionGremio los métodos de habilidades:

  • registrarHabilidad(String habilidad):

    • habilidades.add() retorna true si fue nueva, false si ya existía
    • Imprime el resultado apropiado
  • tieneHabilidad(String habilidad): boolean:

    • Usa habilidades.contains()
  • mostrarHabilidades():

    • Itera con for-each e imprime cada habilidad

💡 HashSet.add() retorna un booleano:
boolean nueva = habilidades.add("Curación");
Si nueva es false, la habilidad ya estaba registrada.
No hay duplicados en un Set.


Parte 4 — Clase principal Main (trabajo independiente)

Demuestra el sistema con estos 5 bloques:

Bloque 1 — Gestión de roster con ArrayList:

GestionGremio gremio = new GestionGremio();
gremio.agregarMiembro(new Druida("Sylva", 10, 300, 100));
gremio.agregarMiembro(new Nigromante("Malachar", 8, 250, 120));
gremio.agregarMiembro(new Arquero("Legolas", 6, 150, "Arco", 20, 95));
gremio.agregarMiembro(new Guerrero("Thorin", 9, 400, "Hacha", 80));
gremio.mostrarRoster();

gremio.eliminarMiembro("Malachar");
gremio.mostrarRoster();

Personaje encontrado = gremio.buscarPorNombre("Legolas");
if (encontrado != null)
    System.out.println("Encontrado: " + encontrado.getNombre());

Bloque 2 — Cola de espera con LinkedList:

gremio.encolarSolicitante("Gandalf");
gremio.encolarSolicitante("Aragorn");
gremio.encolarSolicitante("Gimli");
gremio.mostrarCola();

gremio.atenderSiguiente();   // atiende a Gandalf (FIFO)
gremio.mostrarCola();

Bloque 3 — Inventario con HashMap:

gremio.agregarItem("Poción de vida", 5);
gremio.agregarItem("Flecha élfica", 30);
gremio.agregarItem("Poción de vida", 3);  // suma → 8

gremio.mostrarInventario();

gremio.usarItem("Poción de vida");
gremio.usarItem("Pergamino de fuego");    // no existe
gremio.mostrarInventario();

Bloque 4 — Habilidades únicas con HashSet:

gremio.registrarHabilidad("Curación");
gremio.registrarHabilidad("Magia oscura");
gremio.registrarHabilidad("Curación");    // duplicado — no se agrega

gremio.mostrarHabilidades();

System.out.println("¿Tiene flecha? " + gremio.tieneHabilidad("Tiro con arco"));
System.out.println("¿Tiene curación? " + gremio.tieneHabilidad("Curación"));

Bloque 5 — Resumen del gremio:

gremio.mostrarRoster();
gremio.mostrarCola();
gremio.mostrarInventario();
gremio.mostrarHabilidades();

Ejemplo de salida esperada

[Gremio] Sylva se unió al gremio.
[Gremio] Malachar se unió al gremio.
[Gremio] Legolas se unió al gremio.
[Gremio] Thorin se unió al gremio.

=== Roster del Gremio (4 miembros) ===
1. Sylva    | Nivel: 10 | Vida: 300
2. Malachar | Nivel: 8  | Vida: 250
3. Legolas  | Nivel: 6  | Vida: 150
4. Thorin   | Nivel: 9  | Vida: 400

[Gremio] Malachar abandonó el gremio.

=== Roster del Gremio (3 miembros) ===
1. Sylva   | Nivel: 10 | Vida: 300
2. Legolas | Nivel: 6  | Vida: 150
3. Thorin  | Nivel: 9  | Vida: 400

Encontrado: Legolas

[Cola] Gandalf en posición 1
[Cola] Aragorn en posición 2
[Cola] Gimli   en posición 3

=== Cola de Espera (3) ===
1. Gandalf
2. Aragorn
3. Gimli

[Cola] Atendiendo a: Gandalf

=== Cola de Espera (2) ===
1. Aragorn
2. Gimli

[Inventario] Poción de vida: 5
[Inventario] Flecha élfica: 30
[Inventario] Poción de vida: 8  (acumulada)

=== Inventario del Gremio ===
Flecha élfica   → 30
Poción de vida  → 8

[Inventario] Poción de vida usada. Restante: 7
[Inventario] No existe en inventario: Pergamino de fuego

[Habilidades] Curación registrada.
[Habilidades] Magia oscura registrada.
[Habilidades] Curación ya estaba registrada.

=== Habilidades del Gremio ===
- Curación
- Magia oscura

¿Tiene tiro con arco? false
¿Tiene curación?      true

Criterios de evaluación

Criterio Puntos
ArrayList: agregar, eliminar con Iterator, buscar, mostrar con for-each e índice 20
LinkedList: encolar, atender (FIFO), mostrar cola con for-each 20
HashMap: agregarItem con getOrDefault, usarItem, mostrar con entrySet 25
HashSet: registrar (detecta duplicados), contiene, mostrar 20
Main demuestra los 5 bloques con salida coherente 15
Total 100

Preguntas de reflexión

Responde en respuestas.docx:

  1. ¿Cuál es la diferencia entre ArrayList y LinkedList? Describe un escenario concreto del juego donde preferirías cada uno.
  2. ¿Por qué HashMap no garantiza un orden de inserción al imprimir con entrySet()? ¿Qué colección usarías si el orden importa?
  3. ¿Qué ventaja tiene HashSet sobre ArrayList para registrar habilidades únicas? ¿Qué pierdes al usar HashSet?

Entregable

Sube al repositorio en la carpeta lab08_colecciones:

lab08_colecciones/
├── Personaje.java
├── Guerrero.java
├── Arquero.java
├── Druida.java
├── Nigromante.java
├── RpgException.java
├── (demás excepciones del Lab07)
├── GestionGremio.java
└── Main.java

💡 Puedes reutilizar todas las clases de los labs anteriores sin modificarlas.


Laboratorio preparado para el curso de Programación Orientada a Objetos — Ago-Dic 2025

⚠️ **GitHub.com Fallback** ⚠️