,

Tipos avanzados de datos en Java

·

Aprender los tipos avanzados de datos en Java es uno de esos pasos que marcan un antes y un después cuando empiezas a programar en serio. Al principio todo parece girar alrededor de variables sueltas: un int, un double, un boolean, un String… pero en cuanto el programa crece un poco, esa forma de pensar se queda corta.

En aplicaciones reales rara vez trabajamos con un único dato aislado. Lo normal es gestionar notas de alumnos, productos de un inventario, usuarios de una aplicación, códigos únicos, textos que hay que validar o información que debe guardarse en un formato estructurado como XML. Por eso, los tipos avanzados de datos en Java no son un tema decorativo: son una herramienta básica para escribir programas más útiles, ordenados y mantenibles. El documento base del tema plantea precisamente esa idea: no basta con preguntarse “cómo guardo este dato”, sino también cuántos datos habrá, si necesito orden, si puede haber repetidos, si buscaré por clave o si tendré que intercambiar la información en un formato estándar.

En mi caso, después de pasar por desarrollo software, consultoría tecnológica, proyectos web y docencia en FP Informática, cada vez tengo más claro que programar bien no consiste en memorizar clases, sino en elegir con criterio. Esa mezcla entre construir soluciones y explicarlas de forma clara es parte de mi forma de trabajar.

Vamos a ver los tipos avanzados de datos en Java desde una perspectiva práctica: qué son, cuándo usar cada uno y qué errores conviene evitar.

Qué son los tipos avanzados de datos en Java y por qué son importantes

Un tipo de dato básico permite almacenar un único valor. Por ejemplo, una edad, una nota, una letra o una respuesta de verdadero/falso. Eso está bien para programas sencillos, pero no es suficiente cuando queremos representar datos más complejos.

Los tipos avanzados de datos en Java permiten organizar varios valores relacionados bajo una estructura común. Esa estructura puede ser fija, dinámica, ordenada, sin duplicados, basada en claves o pensada para procesar elementos por turnos.

Por ejemplo:

SituaciónEstructura adecuada
Guardar las notas de 30 alumnosarray o ArrayList<Double>
Crear un catálogo de productosArrayList<Producto> o Map<String, Producto>
Evitar palabras repetidasSet<String>
Buscar un alumno por DNIMap<String, Alumno>
Validar un correo o códigoExpresión regular
Guardar información estructuradaXML

La idea importante es que cada estructura responde a una necesidad distinta. Un array no es “peor” que un ArrayList; simplemente está pensado para otro tipo de problema. Y un HashMap no sustituye siempre a una lista; tiene sentido cuando la búsqueda por clave es lo importante.

Cuando explico tipos avanzados de datos en Java en clase, suelo insistir en esta pregunta: “¿qué necesito hacer con los datos después de guardarlos?”. Esa pregunta cambia por completo la decisión técnica.

De una variable simple a una estructura de datos

Imagina que quieres guardar la nota de un alumno:

double nota = 8.5;

Hasta aquí no hay problema. Pero si tienes 30 alumnos, crear 30 variables deja de ser práctico:

double nota1 = 8.5;
double nota2 = 6.0;
double nota3 = 9.2;

Ese enfoque no escala. Es difícil de recorrer, buscar, ordenar, filtrar o modificar. Aquí es donde entran los tipos avanzados de datos en Java.

Con un array podríamos hacer esto:

double[] notas = {8.5, 6.0, 9.2, 7.0};

Y con una lista:

ArrayList<Double> notas = new ArrayList<>();
notas.add(8.5);
notas.add(6.0);
notas.add(9.2);

Ahora ya no pensamos en variables sueltas, sino en conjuntos de datos. Ese cambio mental es fundamental para avanzar en Java.

Cómo elegir la estructura adecuada según el problema

Antes de elegir una estructura, conviene hacerse algunas preguntas:

PreguntaPosible elección
¿Sé cuántos elementos habrá?array
¿La cantidad puede crecer o reducirse?ArrayList
¿Necesito evitar duplicados?HashSet o TreeSet
¿Quiero buscar por una clave?HashMap o TreeMap
¿Necesito procesar por orden de llegada?Queue
¿Tengo que validar textos?Regex
¿Necesito intercambiar datos estructurados?XML

Esta es una de las claves de los tipos avanzados de datos en Java: no usar siempre lo mismo por costumbre. En consultoría y en proyectos reales, muchas decisiones técnicas vienen de escuchar primero el problema. Antes de hablar de herramientas, hay que entender qué necesita la persona, el negocio o la aplicación. Esa forma de pensar también aplica al código.

Arrays en Java: la base para almacenar varios valores

Un array es una estructura que permite guardar varios valores del mismo tipo bajo un único nombre. Cada elemento ocupa una posición y se accede a él mediante un índice. En Java, los índices empiezan en 0. El documento lo presenta como una estructura de tamaño fijo y acceso por posición, útil cuando conocemos de antemano cuántos elementos vamos a manejar.

Ejemplo:

int[] notas = new int[5];

notas[0] = 7;
notas[1] = 8;
notas[2] = 6;
notas[3] = 9;
notas[4] = 5;

System.out.println(notas[0]);

Aquí notas[0] muestra el primer elemento, no el “elemento cero” en sentido humano. Este detalle parece pequeño, pero es una fuente clásica de errores cuando se empieza.

Los arrays forman parte esencial de los tipos avanzados de datos en Java porque permiten trabajar con bloques de información de manera ordenada y eficiente.

Declarar, crear e inicializar arrays

En Java, trabajar con arrays suele implicar tres pasos:

  1. Declarar la variable.
  2. Crear el array.
  3. Asignar valores.

Por ejemplo:

String[] nombres = new String[3];

nombres[0] = "Ana";
nombres[1] = "Luis";
nombres[2] = "Marta";

También podemos inicializarlo directamente:

double[] precios = {19.99, 5.50, 120.00, 8.75};

Y podemos crear arrays de objetos:

Producto[] productos = new Producto[10];

La limitación más importante del array es que su tamaño queda fijado cuando se crea. Si creas un array de 5 posiciones, sus índices válidos son 0, 1, 2, 3 y 4. Intentar acceder a la posición 5 provocará un error de tipo ArrayIndexOutOfBoundsException.

Este error es tan habitual que merece una regla sencilla: al recorrer arrays, usa siempre length.

Recorrer arrays con for y for-each

Recorrer un array significa visitar sus elementos uno a uno. La forma clásica es usar un bucle for con índice:

int[] notas = {7, 8, 6, 9, 5};

for (int i = 0; i < notas.length; i++) {
System.out.println("Nota " + i + ": " + notas[i]);
}

Este recorrido es útil cuando necesitas conocer la posición del elemento.

También puedes usar un for-each:

for (int nota : notas) {
System.out.println("Nota: " + nota);
}

El for-each es más limpio cuando solo quieres recorrer todos los valores y no necesitas el índice. En la práctica, ambos son necesarios. No se trata de elegir uno para siempre, sino de saber cuál encaja mejor.

Búsquedas, cálculos y errores frecuentes con arrays

Con arrays podemos hacer operaciones muy comunes: buscar un valor, calcular una media, encontrar el máximo, contar aprobados o copiar elementos a otra estructura.

Ejemplo de cálculo:

double[] notas = {7.5, 8.0, 4.5, 9.0, 6.0};

double suma = 0;
double max = notas[0];
int aprobados = 0;

for (double nota : notas) {
suma += nota;

if (nota > max) {
max = nota;
}

if (nota >= 5) {
aprobados++;
}
}

double media = suma / notas.length;

System.out.println("Media: " + media);
System.out.println("Máxima: " + max);
System.out.println("Aprobados: " + aprobados);

Este tipo de ejercicio ayuda mucho a entender los tipos avanzados de datos en Java porque combina almacenamiento, recorrido, condiciones y cálculo.

La limitación del array aparece cuando queremos añadir o eliminar elementos con frecuencia. Ahí empiezan a tener más sentido las colecciones.

Arrays multidimensionales: tablas, matrices y estructuras por filas y columnas

Un array multidimensional permite representar datos organizados en más de una dimensión. El caso más habitual es el array bidimensional, que podemos imaginar como una tabla con filas y columnas.

int[][] matriz = new int[3][4];

matriz[0][0] = 10;
matriz[0][1] = 20;
matriz[1][0] = 30;

Los arrays bidimensionales son útiles para representar tableros, horarios, asientos, mapas sencillos o matrices numéricas. El documento los presenta precisamente como una forma de organizar información en filas y columnas.

Para recorrer una matriz usamos dos bucles:

int[][] notas = {
{7, 8, 6},
{5, 9, 7},
{4, 6, 8}
};

for (int fila = 0; fila < notas.length; fila++) {
for (int col = 0; col < notas[fila].length; col++) {
System.out.print(notas[fila][col] + " ");
}
System.out.println();
}

Una idea práctica: matriz.length devuelve el número de filas, mientras que matriz[fila].length devuelve el número de columnas de una fila concreta.

Cadenas de texto en Java: String y StringBuilder

Aunque solemos ver String desde los primeros programas, también merece aparecer dentro de los tipos avanzados de datos en Java porque ofrece muchos métodos para analizar, buscar, comparar y transformar texto.

Algunos métodos útiles son:

MétodoUso
length()Devuelve la longitud de la cadena
charAt(pos)Obtiene el carácter de una posición
substring(inicio, fin)Extrae una parte del texto
contains(texto)Comprueba si contiene un texto
equals(texto)Compara contenido exacto
equalsIgnoreCase(texto)Compara ignorando mayúsculas y minúsculas
trim()Elimina espacios al inicio y al final
split(regex)Divide una cadena usando un patrón

Ejemplo:

String modulo = "Programación";

System.out.println(modulo.length());
System.out.println(modulo.toUpperCase());
System.out.println(modulo.contains("gram"));

Un error típico es comparar cadenas con ==. En Java, para comparar el contenido de dos cadenas debemos usar equals o equalsIgnoreCase.

String respuesta = "SI";

if (respuesta.equalsIgnoreCase("si")) {
System.out.println("Respuesta afirmativa");
}

Por qué String es inmutable

Los objetos String son inmutables. Eso significa que, cuando parece que modificamos una cadena, en realidad se crea una nueva.

String texto = "hola";
texto.toUpperCase();

System.out.println(texto); // sigue mostrando hola

Para guardar el cambio, hay que reasignar:

texto = texto.toUpperCase();
System.out.println(texto); // HOLA

Esto es importante cuando construimos textos largos dentro de bucles. Si concatenamos muchas cadenas con +, podemos acabar generando objetos innecesarios. Para esos casos existe StringBuilder.

StringBuilder informe = new StringBuilder();

informe.append("Listado de alumnos\n");
informe.append("------------------\n");
informe.append("Ana - 8.5\n");
informe.append("Luis - 6.0\n");

System.out.println(informe.toString());

En proyectos reales, este tipo de detalle importa porque el código no solo debe funcionar: también debe ser claro, mantenible y razonable.

Expresiones regulares en Java: validar y buscar patrones en textos

Las expresiones regulares, también llamadas regex, son patrones que permiten buscar, validar o extraer partes de una cadena. Son muy útiles para comprobar si un texto tiene una forma concreta: un código postal, un teléfono, un DNI simple, un correo orientativo o un código de producto.

Dentro de los tipos avanzados de datos en Java, las expresiones regulares tienen un papel especial: no almacenan datos como una lista o un mapa, pero ayudan a controlar la calidad de los datos que entran en el programa.

Algunos símbolos básicos son:

SímboloSignificado
.Cualquier carácter
\dUn dígito
\wLetra, dígito o guion bajo
\sEspacio en blanco
+Una o más repeticiones
*Cero o más repeticiones
{n}Exactamente n repeticiones
^Inicio de cadena
$Final de cadena

En Java hay que recordar una cosa importante: las barras invertidas se escriben escapadas. Por eso, para representar \d, en el código escribimos "\\d".

Validar datos con matches

El método matches permite comprobar si una cadena completa cumple un patrón.

String codigoPostal = "41001";

if (codigoPostal.matches("\\d{5}")) {
System.out.println("Código postal válido");
} else {
System.out.println("Código postal no válido");
}

El patrón "\\d{5}" significa: exactamente cinco dígitos.

Otro ejemplo:

public static boolean codigoValido(String codigo) {
return codigo.matches("PR-\\d{3}");
}

Este patrón acepta códigos como PR-001 o PR-120, pero rechaza valores como P-001, PR-1 o PR-ABCDE. El documento usa este tipo de validación dentro de un caso práctico de productos, combinando listas, regex y exportación a XML.

Cuando enseño expresiones regulares, suelo decir que al principio parecen más difíciles de lo que son. La clave es no intentar aprenderlas todas de golpe. Empieza con validaciones pequeñas y reales: códigos, teléfonos, identificadores y formatos que puedas probar.

Buscar coincidencias con Pattern y Matcher

Si queremos algo más potente que matches, podemos usar Pattern y Matcher.

Pattern representa el patrón compilado. Matcher aplica ese patrón sobre un texto y permite buscar coincidencias.

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class BuscarCorreos {
public static void main(String[] args) {
String texto = "Contacta con [email protected] o [email protected]";

Pattern patron = Pattern.compile("[\\w.-]+@[\\w.-]+\\.[a-zA-Z]{2,}");
Matcher matcher = patron.matcher(texto);

while (matcher.find()) {
System.out.println("Correo encontrado: " + matcher.group());
}
}
}

Este enfoque es útil cuando no solo quieres validar una cadena completa, sino encontrar fragmentos dentro de un texto.

Eso sí: cuidado con las regex imposibles de leer. Una buena práctica es guardarlas en constantes con nombres claros y añadir comentarios cuando el patrón sea complejo.

Colecciones en Java: List, Set, Map y Queue

Las colecciones son estructuras proporcionadas principalmente por el paquete java.util. A diferencia de los arrays, muchas colecciones pueden crecer y reducirse dinámicamente durante la ejecución. El documento compara List, Set, Map y Queue según si permiten duplicados, si mantienen orden y cuál es su forma principal de acceso.

Este es uno de los bloques más importantes cuando hablamos de tipos avanzados de datos en Java.

ColecciónIdea principalUso típico
ListLista ordenada que permite duplicadosListado de alumnos o productos
SetConjunto sin elementos repetidosCódigos únicos
MapAsociación clave-valorBuscar por DNI, código o id
QueueProcesamiento por ordenTurnos, tareas o mensajes

La diferencia no es solo sintáctica. Elegir mal puede hacer que el programa sea más incómodo de mantener o menos adecuado para el problema.

Diferencia entre array y ArrayList

Un ArrayList es una lista dinámica. Permite añadir, eliminar, buscar y acceder a elementos por posición de forma sencilla.

import java.util.ArrayList;

ArrayList<String> nombres = new ArrayList<>();

nombres.add("Ana");
nombres.add("Luis");
nombres.add("Marta");

System.out.println(nombres.get(0));
System.out.println("Tamaño: " + nombres.size());

nombres.remove("Luis");

Comparativa rápida:

AspectoArrayArrayList
TamañoFijoDinámico
Accesoarray[i]lista.get(i)
Tamaño actuallengthsize()
Tipos primitivosUsa envoltorios como Integer o Double
MétodosPocosMuchos: add, remove, contains, set

En mi experiencia explicando este tema, el error no es usar arrays o usar listas. El error es no saber por qué eliges una opción. Si tienes un conjunto de datos fijo y simple, un array puede ser perfecto. Si necesitas añadir y eliminar elementos durante la ejecución, ArrayList suele ser más cómodo.

Cuándo usar List, Set, Map o Queue

La elección puede resumirse así:

NecesidadEstructura recomendada
Mantener elementos en orden y permitir repetidosList
Evitar elementos duplicadosSet
Buscar rápidamente por claveMap
Procesar elementos por turnoQueue

Ejemplo con HashSet:

HashSet<String> codigos = new HashSet<>();

codigos.add("A001");
codigos.add("A002");
codigos.add("A001");

System.out.println(codigos.size()); // 2

Aunque añadimos "A001" dos veces, el conjunto solo lo guarda una vez.

Ejemplo con HashMap:

HashMap<String, String> alumnos = new HashMap<>();

alumnos.put("12345678A", "Ana");
alumnos.put("87654321B", "Luis");

String nombre = alumnos.get("12345678A");

System.out.println(nombre);

Aquí no buscamos por posición, sino por clave. Por eso HashMap es tan útil cuando trabajamos con identificadores, códigos, DNI o referencias únicas.

Listas de objetos propios: trabajar con datos reales

Lo más interesante de las colecciones no es guardar cadenas o números, sino objetos de nuestras propias clases. Ahí los tipos avanzados de datos en Java empiezan a parecerse mucho más a problemas reales.

Por ejemplo, una clase Alumno:

public class Alumno {
private String nombre;
private double nota;

public Alumno(String nombre, double nota) {
this.nombre = nombre;
this.nota = nota;
}

public String getNombre() {
return nombre;
}

public double getNota() {
return nota;
}
}

Y una lista de alumnos:

ArrayList<Alumno> alumnos = new ArrayList<>();

alumnos.add(new Alumno("Ana", 8.5));
alumnos.add(new Alumno("Luis", 6.0));
alumnos.add(new Alumno("Marta", 9.0));

Ahora podemos recorrer, calcular medias, filtrar destacados o buscar alumnos concretos. El documento trabaja este enfoque al procesar listas de objetos propios para calcular resultados, buscar información y filtrar datos.

Recorrer, buscar y filtrar información

Procesar una lista consiste en recorrer sus elementos para obtener un resultado.

Ejemplo: calcular media y contar aprobados.

double suma = 0;
int aprobados = 0;

for (Alumno alumno : alumnos) {
suma += alumno.getNota();

if (alumno.getNota() >= 5) {
aprobados++;
}
}

double media = suma / alumnos.size();

System.out.println("Media: " + media);
System.out.println("Aprobados: " + aprobados);

Filtrar consiste en crear un subconjunto con los elementos que cumplen una condición:

ArrayList<Alumno> destacados = new ArrayList<>();

for (Alumno alumno : alumnos) {
if (alumno.getNota() >= 8) {
destacados.add(alumno);
}
}

Esta forma de trabajar es muy habitual en aplicaciones reales: filtrar productos caros, usuarios activos, tareas pendientes, pedidos por estado o registros que cumplen una condición.

Iteradores en Java: recorrer y eliminar elementos de forma segura

Un Iterator es un objeto que permite recorrer una colección de forma controlada. Aunque el for-each es más sencillo, el iterador es especialmente útil cuando necesitamos eliminar elementos durante el recorrido.

Ejemplo:

Iterator<String> it = tareas.iterator();

while (it.hasNext()) {
String tarea = it.next();
System.out.println(tarea);
}

La ventaja aparece cuando queremos eliminar:

Iterator<Alumno> it = alumnos.iterator();

while (it.hasNext()) {
Alumno alumno = it.next();

if (alumno.getNota() < 5) {
it.remove();
}
}

Eliminar elementos de una lista dentro de un for-each usando lista.remove(elemento) puede provocar un ConcurrentModificationException. Por eso, el documento recomienda usar Iterator o recorrer desde el final cuando haya eliminaciones.

Este es un buen ejemplo de por qué los tipos avanzados de datos en Java no son solo “más clases”. También implican aprender buenas prácticas para no romper el programa en casos aparentemente sencillos.

Genéricos en Java: seguridad de tipos y código reutilizable

Los genéricos permiten trabajar con tipos parametrizados. Gracias a ellos, una lista puede ser ArrayList<String>, ArrayList<Integer> o ArrayList<Alumno> manteniendo seguridad de tipos.

ArrayList<String> nombres = new ArrayList<>();

nombres.add("Ana");
// nombres.add(25); // Error de compilación

Sin genéricos, podríamos mezclar tipos por error y descubrir el problema demasiado tarde. Con genéricos, el compilador nos ayuda antes de ejecutar el programa.

El documento resume muy bien sus ventajas: evitan insertar tipos incorrectos, reducen conversiones manuales, permiten detectar muchos errores en compilación y hacen el código más claro y mantenible.

Crear una clase genérica

Una clase genérica permite reutilizar la misma estructura con distintos tipos.

public class Caja<T> {
private T contenido;

public void guardar(T contenido) {
this.contenido = contenido;
}

public T obtener() {
return contenido;
}
}

Uso:

Caja<String> cajaTexto = new Caja<>();
cajaTexto.guardar("Hola");

Caja<Integer> cajaNumero = new Caja<>();
cajaNumero.guardar(42);

La letra T representa un tipo que se concretará al crear el objeto. Podría ser String, Integer, Alumno, Producto o cualquier otra clase.

Desde la docencia, este concepto suele costar al principio porque parece abstracto. Pero cuando lo conectas con listas reales, cajas reutilizables o métodos comunes, se entiende mucho mejor: no duplicas código, pero tampoco pierdes control sobre el tipo de dato.

Crear un método genérico con restricciones

También podemos crear métodos genéricos:

public class Utilidades {
public static <T> void mostrarArray(T[] elementos) {
for (T elemento : elementos) {
System.out.println(elemento);
}
}
}

Y usarlos con distintos tipos:

String[] nombres = {"Ana", "Luis"};
Integer[] numeros = {1, 2, 3};

Utilidades.mostrarArray(nombres);
Utilidades.mostrarArray(numeros);

A veces interesa limitar los tipos permitidos. Por ejemplo, si queremos sumar valores numéricos:

public static <T extends Number> double sumar(T[] valores) {
double suma = 0;

for (T valor : valores) {
suma += valor.doubleValue();
}

return suma;
}

Con T extends Number, indicamos que el método solo acepta tipos numéricos. Este tipo de detalle ayuda a crear código flexible, pero seguro.

XML en Java: representar, leer y generar información estructurada

XML significa eXtensible Markup Language. Es un lenguaje de marcas que permite representar información de forma estructurada y legible. Aunque hoy JSON se usa mucho en aplicaciones web, XML sigue apareciendo en configuraciones, intercambio de datos, servicios, documentos y herramientas empresariales.

Ejemplo:

<alumnos>
<alumno id="1">
<nombre>Ana</nombre>
<grupo>1DAW</grupo>
<nota>8.5</nota>
</alumno>
</alumnos>

En el contexto de los tipos avanzados de datos en Java, XML resulta interesante porque nos permite sacar la información del programa y representarla en un formato estructurado.

Un XML puede tener:

ElementoDescripción
EtiquetaMarca de apertura y cierre
ElementoUnidad formada por etiqueta y contenido
AtributoDato dentro de la etiqueta de apertura
Nodo raízElemento principal del documento
TextoContenido de un elemento

Qué significa que un XML esté bien formado

Un XML bien formado debe cumplir reglas básicas:

  • Tener un único elemento raíz.
  • Cerrar todas las etiquetas abiertas.
  • Anidar correctamente los elementos.
  • Usar comillas en los atributos.
  • Respetar mayúsculas y minúsculas.
  • Escapar caracteres especiales cuando corresponda.

Correcto:

<producto>
<nombre>Ratón</nombre>
<precio>12.50</precio>
</producto>

Incorrecto:

<producto>
<nombre>Ratón</producto>
</nombre>

El error está en el anidamiento. Se abre nombre, luego se cierra producto, y después se cierra nombre. Eso rompe la estructura.

Diferencia entre DTD y XSD

Un XML puede estar bien formado, pero eso no significa que tenga la estructura esperada por una aplicación. Para definir reglas se usan tecnologías como DTD y XSD.

TecnologíaFunción
DTDDefine la estructura básica permitida
XSDDefine estructura, tipos de datos y restricciones más precisas

DTD es más simple y antigua. XSD permite definir tipos como números, fechas, patrones, obligatoriedad y restricciones más detalladas.

En la práctica, esto significa que XML no solo sirve para guardar datos, sino también para comprobar que esos datos cumplen una estructura concreta.

Leer, crear y modificar XML con DOM

Java ofrece varias formas de trabajar con XML: DOM, SAX, StAX y JAXB. El documento se centra en DOM porque carga el documento completo en memoria y permite recorrerlo como un árbol de nodos, algo adecuado para documentos pequeños o medianos y fácil de entender al empezar.

Para leer XML con DOM se usan clases como:

  • DocumentBuilderFactory
  • DocumentBuilder
  • Document
  • NodeList
  • Element

Ejemplo simplificado:

DocumentBuilderFactory fabrica = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = fabrica.newDocumentBuilder();
Document documento = builder.parse(new File("alumnos.xml"));

NodeList lista = documento.getElementsByTagName("alumno");

for (int i = 0; i < lista.getLength(); i++) {
Element alumno = (Element) lista.item(i);

String id = alumno.getAttribute("id");
String nombre = alumno.getElementsByTagName("nombre")
.item(0)
.getTextContent();

System.out.println(id + " - " + nombre);
}

También podemos crear XML desde Java usando Document, Element, Transformer y DOMSource.

Una recomendación importante: al trabajar con XML, conviene comprobar que los nodos existen antes de acceder a item(0). Si el XML no tiene la estructura esperada, podemos provocar un NullPointerException.

Caso práctico: gestor de productos con ArrayList, regex y XML

Una buena forma de entender los tipos avanzados de datos en Java es combinarlos en un caso práctico.

Imagina un pequeño gestor de productos. Cada producto tiene código, nombre y precio:

public class Producto {
private String codigo;
private String nombre;
private double precio;

public Producto(String codigo, String nombre, double precio) {
this.codigo = codigo;
this.nombre = nombre;
this.precio = precio;
}

public String getCodigo() {
return codigo;
}

public String getNombre() {
return nombre;
}

public double getPrecio() {
return precio;
}
}

Creamos una lista:

ArrayList<Producto> productos = new ArrayList<>();

productos.add(new Producto("PR-001", "Teclado", 19.99));
productos.add(new Producto("PR-002", "Ratón", 12.50));
productos.add(new Producto("PR-003", "Monitor", 149.99));

Buscamos por código:

String codigoBuscado = "PR-002";
Producto encontrado = null;

for (Producto producto : productos) {
if (producto.getCodigo().equals(codigoBuscado)) {
encontrado = producto;
break;
}
}

Validamos códigos:

public static boolean codigoValido(String codigo) {
return codigo.matches("PR-\\d{3}");
}

Filtramos productos caros:

ArrayList<Producto> productosCaros = new ArrayList<>();

for (Producto producto : productos) {
if (producto.getPrecio() >= 50) {
productosCaros.add(producto);
}
}

Y podemos exportar a XML:

<productos>
<producto codigo="PR-001">
<nombre>Teclado</nombre>
<precio>19.99</precio>
</producto>
</productos>

Este ejemplo resume muy bien el objetivo del tema: usar listas para almacenar objetos, regex para validar códigos y XML para representar información estructurada. No es teoría aislada; es una pequeña aplicación con piezas que encajan entre sí.

Tabla rápida: qué estructura usar en cada caso

CasoRecomendación
Tengo un número fijo de elementosarray
Necesito una lista que crezcaArrayList
Quiero evitar duplicadosHashSet
Quiero datos ordenados sin repetirTreeSet
Necesito buscar por claveHashMap
Necesito claves ordenadasTreeMap
Quiero eliminar mientras recorroIterator
Quiero reutilizar código con distintos tiposGenéricos
Quiero validar formatos de textoRegex
Quiero guardar datos estructuradosXML

Esta tabla es una buena guía inicial para trabajar con tipos avanzados de datos en Java sin caer en el “uso siempre ArrayList para todo”.

Errores frecuentes al trabajar con tipos avanzados de datos en Java

El documento recoge varios errores habituales y buenas prácticas que merece la pena tener muy presentes.

Acceder a un índice fuera del array

Si un array tiene 5 elementos, el último índice es 4. Para evitar errores, usa length.

for (int i = 0; i < datos.length; i++) {
System.out.println(datos[i]);
}

Comparar cadenas con ==

No compares contenido de cadenas con ==.

if (nombre.equals("Ana")) {
System.out.println("Encontrado");
}

Elegir siempre ArrayList

ArrayList es muy útil, pero no siempre es la mejor opción. Si necesitas claves, piensa en Map. Si necesitas evitar duplicados, piensa en Set.

Eliminar dentro de un for-each

No hagas esto:

for (Alumno alumno : alumnos) {
if (alumno.getNota() < 5) {
alumnos.remove(alumno);
}
}

Usa Iterator.

Usar colecciones sin genéricos

Evita colecciones sin tipo:

ArrayList lista = new ArrayList();

Mejor:

ArrayList<Alumno> alumnos = new ArrayList<>();

Escribir regex imposibles de mantener

Si una expresión regular es compleja, ponle nombre y documenta su intención.

private static final String PATRON_PRODUCTO = "PR-\\d{3}";

Mezclarlo todo en el mismo sitio

Datos, lógica y exportación deberían estar separados. Esta idea viene mucho de la experiencia real: un programa pequeño puede sobrevivir con todo mezclado, pero un programa que crece necesita orden.

Conclusión: aprender Java es aprender a elegir bien

Los tipos avanzados de datos en Java son fundamentales porque nos permiten pasar de programas simples a soluciones más cercanas a la realidad. Un array nos ayuda cuando tenemos un número fijo de elementos. Un ArrayList nos da flexibilidad. Un Set evita duplicados. Un Map permite buscar por clave. Un Iterator nos ayuda a recorrer y eliminar de forma segura. Los genéricos mejoran la seguridad del código. Las expresiones regulares validan textos. Y XML permite representar información estructurada.

La idea más importante no es aprender una lista enorme de clases, sino desarrollar criterio. Un buen programador no usa siempre la misma estructura: analiza el problema, decide qué necesita y escribe código claro para manipular la información con seguridad.

En mi recorrido entre código, aula y proyectos digitales, esa es una de las lecciones que más se repite: la tecnología tiene que ser útil, clara y aplicable. Y los tipos avanzados de datos en Java son precisamente una herramienta para eso: organizar mejor la información y construir programas más sólidos.

Preguntas frecuentes sobre tipos avanzados de datos en Java

¿Qué son los tipos avanzados de datos en Java?

Son estructuras y mecanismos que permiten trabajar con información más compleja que una variable simple. Incluyen arrays, colecciones, cadenas, genéricos, expresiones regulares y XML.

¿Qué diferencia hay entre un array y un ArrayList?

Un array tiene tamaño fijo y se accede con índices usando array[i]. Un ArrayList tiene tamaño dinámico y ofrece métodos como add, remove, get, contains y size.

¿Cuándo conviene usar un array?

Conviene usar un array cuando sabes de antemano cuántos elementos habrá y necesitas una estructura simple con acceso rápido por posición.

¿Cuándo es mejor usar un ArrayList?

Un ArrayList es mejor cuando la cantidad de elementos puede cambiar durante la ejecución y necesitas añadir, eliminar o buscar elementos con comodidad.

¿Qué diferencia hay entre List, Set y Map?

List mantiene elementos ordenados y permite duplicados. Set evita duplicados. Map guarda pares clave-valor y permite buscar información usando una clave.

¿Para qué sirve un HashMap?

Un HashMap sirve para asociar claves con valores. Es útil, por ejemplo, para buscar un alumno por DNI, un producto por código o un usuario por identificador.

¿Qué problema resuelven los genéricos en Java?

Los genéricos permiten escribir clases y métodos reutilizables sin perder seguridad de tipos. Ayudan a evitar errores como insertar un Integer en una lista pensada para String.

¿Qué es un Iterator?

Un Iterator es un objeto que permite recorrer una colección de forma controlada. Es especialmente útil cuando necesitas eliminar elementos durante el recorrido.

¿Qué son las expresiones regulares en Java?

Son patrones que permiten validar, buscar o extraer texto. Se usan para comprobar formatos como códigos postales, teléfonos, DNI simples, correos orientativos o códigos de producto.

¿Qué diferencia hay entre matches, Pattern y Matcher?

matches comprueba si una cadena completa cumple un patrón. Pattern representa una expresión regular compilada. Matcher aplica ese patrón sobre un texto para buscar coincidencias.

¿Qué es XML en Java?

XML es un lenguaje de marcas para representar información estructurada. En Java puede leerse, crearse o modificarse usando tecnologías como DOM.

¿Qué diferencia hay entre DTD y XSD?

DTD define una estructura básica para un XML. XSD permite definir estructuras más precisas, tipos de datos y restricciones.

julian lopez jimenez

Hola, encantado de conocerte.

Regístrate para recibir las últimas entradas, cada domingo.

¡No hago spam!

Recibe nuevas entradas cada semana

Una seleccion de articulos, recursos y novedades sobre informatica, FP y tecnologia aplicada.

julian lopez jimenez

Hola, encantado de conocerte.

Regístrate para recibir las últimas entradas, cada domingo.

¡No hago spam!

Tambien te puede interesar