For-each is pure compiler syntax sugar. Under the hood, it creates an Iterator object tracking an internal cursor with hasNext() and next().
Lo que vos escribís vs Lo que compilaDesazucarado
// 1. Azúcar sintáctico (Java 5+)
for (String fruta : cesta) {
System.out.println(fruta);
}
// 2. Lo que realmente se ejecuta en Bytecode:
Iterator<String> it = cesta.iterator();
while (it.hasNext()) {
String fruta = it.next();
System.out.println(fruta);
}
Requisito formal: Cualquier clase puede usarse en un bucle for-each siempre que implemente la interfaz Iterable<T>, la cual exige un único método: Iterator<T> iterator().
El Cursor del Iterador en MemoriaCursor: Posición 0
[0] "Manzana"
[1] "Pera"
[2] "Uva"
[3] "Kiwi"
▲ cursor apunta aquí
it.hasNext() retorna true. Presiona it.next() para avanzar el cursor y retornar el elemento.
Slide 2 / 7 • Fail-Fast Iterators
The Nightmare of ConcurrentModificationException
Collections track mutations via modCount. The iterator holds expectedModCount. Altering the collection mid-loop causes divergence and throws an exception.
Odómetros de Mutación (modCount)Sincronizados (5 == 5)
lista.modCount5Mutaciones en la lista
==
it.expectedModCount5Copia que guarda el iterador
Estado normal: modCount coincide con expectedModCount. Todo llamado a next() es seguro.
¿Por qué falla de inmediato (Fail-Fast)?
// Implementación real de ArrayList.Itr:
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
// La forma correcta de borrar mientras iterás:
Iterator<String> it = lista.iterator();
while (it.hasNext()) {
String item = it.next();
if (item.startsWith("X")) {
it.remove(); // Actualiza ambos contadores
}
}
No requiere múltiples hilos: A pesar de llamarse "Concurrent", esta excepción ocurre con frecuencia en un solo hilo cuando intentás borrar de una lista dentro de un `for-each`.
Slide 3 / 7 • Intrinsic Ordering
Comparable: The Natural Order of a Class
Comparable<T> defines a single intrinsic order via compareTo(T other). The sign of the return value governs relative ordering.
Balanza de compareTo(otro): A vs BResultado: +2 (A > B)
8 - 6 = +2 > 0
this.nota (8) - otro.nota (6) = +2. Número positivo indica que Estudiante A va DESPUÉS que B.
La Regla del Signo en compareTo
Negativo (< 0)this < otro (va antes)
Cero (== 0)this == otro (empate)
Positivo (> 0)this > otro (va después)
public class Estudiante implements Comparable<Estudiante> {
private int nota;
@Override
public int compareTo(Estudiante otro) {
return Integer.compare(this.nota, otro.nota);
}
}
Slide 4 / 7 • Flexible Strategies
Comparator: Multiple Sorting Criteria on Demand
Sort by distinct attributes (age, name, salary) without mutating the target class, composing comparisons fluidly.
Lista de Empleados: Reordenamiento DinámicoOrden: Por Edad
Encadenamiento con Lambdas en Java 8+
// Orden por salario descendente, luego por apellido
Comparator<Empleado> comp = Comparator
.comparing(Empleado::getSalario)
.reversed()
.thenComparing(Empleado::getApellido);
// Aplicar a lista:
empleados.sort(comp);
// O en Stream:
empleados.stream()
.sorted(comp)
.forEach(System.out::println);
Comparable vs Comparator: Comparable define el orden por defecto dentro de la clase. Comparator define estrategias externas fuera de la clase sin tocar su código.
Slide 5 / 7 • Object Identity
The equals() Contract: 5 Inviolable Rules
Reflexive, symmetric, transitive, consistent, and null-safe. The subtle trap of breaking symmetry using instanceof with subclasses.
Los 5 Mandamientos de equals(Object o)Java Language Spec
1
Reflexiva:x.equals(x) debe retornar true siempre.
2
Simétrica:x.equals(y) debe retornar lo mismo que y.equals(x).
3
Transitiva: Si x.equals(y) y y.equals(z), entonces x.equals(z).
4
Consistente: Múltiples llamadas retornan lo mismo salvo que se mute el objeto.
// Punto(x, y) usa instanceof
Punto p = new Punto(1, 2);
PuntoColor pc = new PuntoColor(1, 2, "Rojo");
// ¡Rompe la simetría!
p.equals(pc); // true (pc es instanceof Punto)
pc.equals(p); // false (p no tiene color)
// Solución canónica:
if (this.getClass() != obj.getClass())
return false;
Regla arquitectónica: No hay forma perfecta de extender una clase instanciable y agregarle un nuevo componente de valor preservando la simetría y transitividad de `equals` si se usa `instanceof`. Preferí composición sobre herencia.
Slide 6 / 7 • The Golden Rule
The Golden Contract: equals and hashCode
If two objects are equal, their hashCodes MUST match. Breaking this rule causes objects to vanish inside HashSets and HashMaps.
Búsqueda en HashSet: set.contains(new Producto("A1", 50))Modo: Con Violación de Hash
Bucket [0]
vacio
Bucket [1]
Producto("A1")addr: 0x9AF1
Bucket [2]
vacio
Bucket [3]
Producto("A1")addr: 0x22C0
Sobrescribiste equals() pero NO hashCode(). El objeto insertado usó su dirección de memoria (Bucket 1).
El Contrato Formal de Java
1. Si dos objetos son iguales por equals(): a.equals(b) == true ⇒ a.hashCode() == b.hashCode() (MANDATORIO).
2. Si dos objetos tienen diferente hashCode():
Son distintos con total certeza. El Set ni siquiera revisa el bucket.
3. Si tienen el mismo hashCode() pero son distintos:
Es una colisión normal. Se resuelve comparando con equals() dentro del bucket.
Slide 7 / 7 • Silent Memory Leaks
Mutable Keys in Hash Collections: The Trapped Object
Mutating fields on an object already stored in a hash collection changes its computed hash without relocating its bucket, stranding it permanently.
El Objeto Fantasma en el BucketFuga de Memoria
1. Inserción Inicial:
p = new Persona("Carlos"); set.add(p);
"Carlos".hashCode() apunta al Bucket [2]. El objeto descansa ahí.
2. Mutación Prohibida:
p.setNombre("Alberto");
El objeto muta en el Heap. Ahora su nuevo hash apunta al Bucket [6], pero el objeto sigue físicamente atrapado en la cubeta 2.
3. Búsqueda y Borrado Rotos:
set.contains(p) calcula el hash de "Alberto", busca en Bucket 6 (¡está vacío!). Retorna false. Tampoco podés borrarlo con set.remove(p).
Presiona simular para observar cómo mutar una propiedad rompe la colección hash.
Regla de Diseño Inviolable
Claves Inmutables Siempre:
Toda clase utilizada como clave en un Map o elemento en un Set debe ser inmutable (o al menos los campos que participan en hashCode() y equals() deben ser final).
// Diseño seguro: Java 14+ Record
public record UsuarioId(String codigo, int sucursal) {
// Inmutable por diseño:
// equals() y hashCode() generados de forma
// coherente y determinística.
}