11 - Estructuras de datos en distintos lenguajes

Comparar las estructuras disponibles en distintos lenguajes evita suposiciones peligrosas y permite aprovechar mejor las bibliotecas estándar. Cada ecosistema ofrece colecciones predeterminadas con reglas de memoria, genéricos e iteradores pensados para casos de uso concretos.

Conocer estas diferencias acelera migraciones entre proyectos, ayuda a escribir código idiomático y evita reconstruir colecciones ya probadas por la comunidad.

11. Estructuras de datos en distintos lenguajes

Las secciones siguientes resumen ventajas, precauciones y fragmentos de código que sirven como referencia rápida.

11.1 Java: Collections Framework

El Java Collections Framework agrupa interfaces como List, Set y Map con implementaciones diseñadas para escenarios generales y concurrentes.

  • Las interfaces definen operaciones (insertar, iterar, ordenar) y las clases (ArrayList, LinkedList, HashSet, TreeMap) permiten elegir entre memoria contigua, enlazada u ordenada.
  • Los genéricos obligan a declarar tipos y evitan conversiones inseguras, lo que mantiene la integridad en tiempo de compilación.
  • Las variantes concurrentes (ConcurrentHashMap, CopyOnWriteArrayList) implementan bloqueos finos; conviene usarlas solo cuando hay competencia real por los datos.
Map<String, Integer> inventario = new HashMap<>();
inventario.put("switch", 3);
int piezas = inventario.getOrDefault("switch", 0);

11.2 Python: list, dict, set

En Python las estructuras básicas vienen integradas en el lenguaje y aprovechan tipado dinámico y sintaxis concisa.

  • list usa arreglos dinámicos contiguos, crece por bloques y permite slicing sin copiar de inmediato gracias a vistas.
  • dict y set operan sobre tablas hash abiertas con sondeos y guardan el orden de inserción desde Python 3.7.
  • Las comprensiones y desempaquetados facilitan transformar colecciones sin escribir bucles verbosos.
inventario = {"ssd": 12, "ram": 48}
pedidos = ["cola", "pila"]
pedidos.append("grafo")
unicos = set(pedidos)

11.3 C++: STL (vector, map, etc.)

La STL ofrece contenedores templados, iteradores y algoritmos que trabajan juntos siguiendo el paradigma RAII.

  • std::vector, std::deque y std::array cubren memorias contiguas, mientras que std::list y std::forward_list manejan nodos enlazados.
  • std::map y std::unordered_map representan diccionarios ordenados y hash; la elección depende de si necesitas ordenamiento o rapidez promedio.
  • Los iteradores permiten combinar contenedores con algoritmos como std::sort, std::accumulate o std::find_if sin reescribir lógica.
std::map<std::string, int> ranking{{"AVL", 1}};
ranking.emplace("Trie", 2);
for (const auto &par : ranking) {
  std::cout << par.first << " => " << par.second << '\n';
}

11.4 C: enfoque de bajo nivel manual

En el lenguaje C estándar no existen colecciones listas para usar: cada estructura se implementa con struct, punteros y gestión manual de memoria.

  • Necesitas reservar y liberar memoria con malloc/free; cualquier olvido provoca fugas o corrupción.
  • Los punteros dobles y las funciones que reciben callbacks permiten simular polimorfismo, aunque incrementan la verbosidad.
  • Las bibliotecas compartidas en proyectos existentes suelen encapsular estas tareas; reusar una implementación probada reduce defectos.
typedef struct Nodo {
  int dato;
  struct Nodo *sig;
} Nodo;

Nodo *push(Nodo *pila, int valor) {
  Nodo *nuevo = malloc(sizeof(Nodo));
  if (!nuevo) {
    return pila;
  }
  nuevo->dato = valor;
  nuevo->sig = pila;
  return nuevo;
}

11.5 Diferencias importantes entre lenguajes

Aunque resuelven problemas similares, cada lenguaje impone compromisos que conviene tener presentes al diseñar estructuras o migrar código.

  • Abstracción: Java y C++ exponen APIs de alto nivel, mientras que C obliga a construir cada operación desde cero.
  • Memoria: Java y Python delegan la liberación al recolector, C++ usa determinismo por alcance (RAII) y C depende del programador en cada llamada.
  • Tipado: Java y C++ usan genéricos o plantillas que protegen el tipo en compilación; Python confía en duck typing y validaciones en tiempo de ejecución.
  • Herramientas adicionales: las colecciones de Java incluyen sincronización opcional, C++ integra algoritmos genéricos, Python aporta expresiones concisas y C se apoya en macros o bibliotecas de terceros.

Elegir la API nativa adecuada reduce incompatibilidades, simplifica el mantenimiento y permite invertir el tiempo en la lógica del problema en lugar de en detalles de infraestructura.