En Java, los arreglos (arrays) son contenedores de tamaño fijo que almacenan elementos del mismo tipo. Si venís de C, la sintaxis te resultará familiar, aunque hay diferencias importantes en cómo Java gestiona la memoria y verifica los límites.
¿Qué es un Arreglo?¶
Un arreglo es una estructura de datos que permite almacenar múltiples valores del mismo tipo bajo un único nombre, organizados en posiciones consecutivas de memoria. Cada posición se identifica mediante un índice numérico que comienza en cero.
Pensalo como una fila de casilleros numerados: cada casillero puede guardar exactamente un elemento del mismo tipo, y para acceder a un casillero específico usás su número (índice).
Características Fundamentales¶
Antes de ver la sintaxis, es importante entender qué caracteriza a los arreglos en Java:
| Característica | Descripción |
|---|---|
| Tamaño fijo | Una vez creado, el tamaño no puede cambiar. Si necesitás más espacio, debés crear un arreglo nuevo. |
| Homogéneo | Todos los elementos deben ser del mismo tipo (todos int, todos double, etc.). |
| Indexado desde cero | El primer elemento está en la posición 0, el segundo en la posición 1, y así sucesivamente. |
| Acceso directo | Podés acceder a cualquier elemento directamente por su índice en tiempo constante O(1). |
| Almacenamiento contiguo | Los elementos se almacenan en posiciones consecutivas de memoria (en el heap). |
Declaración de Arreglos¶
Declarar un arreglo significa indicarle al compilador que vamos a usar una variable que contendrá una referencia a un arreglo de un tipo específico.
En Java hay dos sintaxis válidas para declarar arreglos (la primera es preferida):
tipo[] nombreArreglo; // ✅ Sintaxis preferida en Java
tipo nombreArreglo[]; // Sintaxis estilo C (válida pero no recomendada)Sintaxis de declaración
La sintaxis tipo[] es preferida porque mantiene toda la información del tipo junta: int[] se lee claramente como “arreglo de enteros”.
int[] numeros; // Arreglo de enteros
double[] temperaturas; // Arreglo de doubles
char[] letras; // Arreglo de caracteres
boolean[] banderas; // Arreglo de booleanos
String[] nombres; // Arreglo de Strings (referencias a objetos)Ejemplos de declaración
Creación de Arreglos¶
Crear un arreglo significa reservar memoria en el heap para almacenar los elementos. Para esto se usa la palabra clave new, seguida del tipo y el tamaño entre corchetes:
int[] numeros = new int[5]; // Arreglo de 5 enteros
double[] precios = new double[10]; // Arreglo de 10 doubles
char[] vocales = new char[5]; // Arreglo de 5 caracteresCreación con new
La expresión new int[5] hace lo siguiente:
Reserva espacio en el heap para 5 valores de tipo
int(20 bytes, ya que cadaintocupa 4 bytes).Inicializa todos los elementos a su valor por defecto (cero para
int).Retorna una referencia (dirección de memoria) al arreglo creado.
Esta referencia se almacena en la variable numeros, que vive en el stack.
Valores por Defecto¶
Al crear un arreglo con new, Java garantiza que todos los elementos se inicializan automáticamente a un valor por defecto. Esto es una diferencia importante con C, donde la memoria dinámica contiene “basura” hasta que la inicializás explícitamente.
| Tipo de Elemento | Valor por Defecto | Explicación |
|---|---|---|
int, short, byte, long | 0 | Cero numérico entero |
float, double | 0.0 | Cero numérico decimal |
char | '\u0000' | Carácter nulo (código Unicode 0) |
boolean | false | El valor booleano falso |
| Referencias (String, objetos, etc.) | null | No apunta a ningún objeto |
int[] numeros = new int[3];
// numeros[0] = 0, numeros[1] = 0, numeros[2] = 0
boolean[] flags = new boolean[2];
// flags[0] = false, flags[1] = false
String[] palabras = new String[3];
// palabras[0] = null, palabras[1] = null, palabras[2] = nullValores por defecto
Creación con Inicialización¶
Cuando conocés los valores iniciales al momento de crear el arreglo, podés usar una sintaxis abreviada con llaves. Java determina automáticamente el tamaño según la cantidad de elementos:
int[] primos = {2, 3, 5, 7, 11}; // 5 elementos
double[] notas = {8.5, 9.0, 7.5, 10.0}; // 4 elementos
char[] vocales = {'a', 'e', 'i', 'o', 'u'}; // 5 elementos
String[] dias = {"Lunes", "Martes", "Miércoles"};Inicialización directa
Esta sintaxis es equivalente a:
int[] primos = new int[5];
primos[0] = 2;
primos[1] = 3;
primos[2] = 5;
primos[3] = 7;
primos[4] = 11;Equivalencia de la inicialización directa
Separación de Declaración e Inicialización¶
A veces necesitás declarar la variable en un lugar y crear el arreglo en otro (por ejemplo, dentro de un condicional). En ese caso, la sintaxis con llaves requiere incluir new tipo[] explícitamente:
int[] numeros;
numeros = new int[5]; // OK: creación con tamaño
int[] datos;
datos = new int[]{1, 2, 3, 4, 5}; // OK: creación con valores
// Nota: new int[] es NECESARIO aquí, no podés usar solo {1, 2, 3, 4, 5}Declaración e inicialización separadas
¿Por qué se necesita new int[] en el segundo caso? Porque las llaves solas ({1, 2, 3}) solo son válidas como parte de una declaración con inicialización. Es una restricción sintáctica del lenguaje.
// Útil cuando el arreglo depende de una condición:
int[] resultado;
if (modo == 1) {
resultado = new int[]{1, 2, 3};
} else {
resultado = new int[]{10, 20};
}
// Útil cuando el tamaño se conoce en tiempo de ejecución:
int[] dinamico;
int tamanio = obtenerTamanioDeUsuario();
dinamico = new int[tamanio];Casos de uso de la separación
Acceso a Elementos¶
Una vez creado el arreglo, podés leer y escribir elementos individuales usando el operador de indexación []. Los elementos se acceden usando índices entre corchetes, y los índices comienzan en 0 (igual que en C).
int[] numeros = {10, 20, 30, 40, 50};
// [0] [1] [2] [3] [4] ← índices
// Lectura de elementos
int primero = numeros[0]; // 10
int tercero = numeros[2]; // 30
int ultimo = numeros[4]; // 50
// Escritura de elementos
numeros[1] = 25; // Modifica: ahora es {10, 25, 30, 40, 50}Acceso por índice
El acceso por índice es una operación de tiempo constante O(1): acceder al elemento 0 toma el mismo tiempo que acceder al elemento 1000, porque Java calcula directamente la dirección de memoria usando aritmética de punteros internamente.
Índices Válidos¶
Para un arreglo de tamaño n, los índices válidos van de 0 a n-1:
| Arreglo | Tamaño | Índices válidos |
|---|---|---|
new int[5] | 5 | 0, 1, 2, 3, 4 |
new int[1] | 1 | 0 |
new int[100] | 100 | 0 a 99 |
El Atributo length¶
Todo arreglo en Java tiene un atributo llamado length que indica cuántos elementos puede contener. Este atributo:
Es de solo lectura: no podés modificarlo.
Está siempre disponible: no necesitás importar nada.
Tiene un valor constante: se establece al crear el arreglo y nunca cambia.
int[] datos = {1, 2, 3, 4, 5};
int tamanio = datos.length; // 5
int ultimoIndice = datos.length - 1; // 4 (índice del último elemento)
int ultimoElemento = datos[datos.length - 1]; // 5 (valor del último elemento)Uso de length
Verificación de Límites¶
Java verifica automáticamente en tiempo de ejecución que el índice esté dentro del rango válido [0, length-1]. Si intentás acceder con un índice inválido (negativo o mayor o igual a length), Java lanza una excepción ArrayIndexOutOfBoundsException y el programa se detiene.
int[] numeros = new int[5]; // Índices válidos: 0, 1, 2, 3, 4
numeros[5] = 10; // ❌ ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5
numeros[-1] = 10; // ❌ ArrayIndexOutOfBoundsException: Index -1 out of bounds for length 5Error de índice fuera de rango
int[] datos = {10, 20, 30};
int indice = 5;
// Forma segura: verificar antes de acceder
if (indice >= 0 && indice < datos.length) {
System.out.println(datos[indice]);
} else {
System.out.println("Índice fuera de rango: " + indice);
}Ejemplo de manejo del error
Recorrido de Arreglos¶
Recorrer un arreglo significa visitar cada uno de sus elementos, generalmente para leerlos, procesarlos, o modificarlos. Es una de las operaciones más comunes.
Recorrido con for Clásico¶
El lazo for clásico es la forma más flexible de recorrer un arreglo. Usás una variable índice que va desde 0 hasta length - 1:
for (int i = 0; i < arreglo.length; i = i + 1) {
// Usar arreglo[i]
}Recorrido con for - estructura básica
La condición i < arreglo.length (no <=) es fundamental: si usaras <= accederías a arreglo[length] que está fuera de rango.
int[] numeros = {10, 20, 30, 40, 50};
// Recorrido para mostrar elementos
for (int i = 0; i < numeros.length; i = i + 1) {
System.out.println("Elemento " + i + ": " + numeros[i]);
}
// Salida:
// Elemento 0: 10
// Elemento 1: 20
// Elemento 2: 30
// Elemento 3: 40
// Elemento 4: 50Recorrido de lectura
int[] numeros = {10, 20, 30, 40, 50};
// Recorrido para modificar elementos
for (int i = 0; i < numeros.length; i = i + 1) {
numeros[i] = numeros[i] * 2; // Duplica cada elemento
}
// numeros ahora es {20, 40, 60, 80, 100}Recorrido de modificación
Recorrido con for-each¶
El lazo for-each (también llamado “enhanced for” o “for mejorado”) proporciona una sintaxis más limpia cuando solo necesitás leer los elementos sin importar el índice:
int[] numeros = {10, 20, 30, 40, 50};
for (int numero : numeros) {
System.out.println(numero);
}
// Salida: 10, 20, 30, 40, 50 (cada uno en su línea)Recorrido con for-each
La sintaxis es: for (tipo elemento : arreglo). Se lee como “para cada numero en numeros”.
¿Cómo funciona internamente? El for-each es azúcar sintáctica. El compilador lo transforma en un for clásico:
// Esto:
for (int numero : numeros) {
System.out.println(numero);
}
// Es equivalente a esto:
for (int i = 0; i < numeros.length; i = i + 1) {
int numero = numeros[i]; // Copia el valor en variable local
System.out.println(numero);
}Equivalencia del for-each
Recorrido Parcial¶
A veces necesitás recorrer solo una porción del arreglo. El for clásico permite controlar exactamente dónde empezar y terminar:
int[] datos = {10, 20, 30, 40, 50, 60, 70};
// Recorrer solo del índice 2 al 4 (inclusive)
for (int i = 2; i <= 4; i = i + 1) {
System.out.print(datos[i] + " "); // Imprime: 30 40 50
}Recorrido de una porción del arreglo
Recorrido Inverso¶
Para recorrer de atrás hacia adelante, empezá en length - 1 y decrementá:
int[] numeros = {10, 20, 30, 40, 50};
for (int i = numeros.length - 1; i >= 0; i = i - 1) {
System.out.print(numeros[i] + " "); // Imprime: 50 40 30 20 10
}Recorrido en orden inverso
Recorrido con while¶
Aunque el for es más común para recorrer arreglos, también podés usar while. Es especialmente útil cuando la condición de terminación es más compleja que simplemente llegar al final:
int[] numeros = {10, 20, 30, 40, 50};
int i = 0;
while (i < numeros.length) {
System.out.println(numeros[i]);
i = i + 1;
}Recorrido con while
Recorrido con Bandera (Patrón de Búsqueda)¶
Uno de los patrones más importantes es la búsqueda con bandera: recorrer el arreglo hasta encontrar un elemento específico o llegar al final. Usamos una variable booleana (la “bandera”) para controlar cuándo terminar.
Este patrón evita usar break, lo cual produce código más claro y predecible:
int[] numeros = {10, 25, 30, 45, 50};
int buscado = 30;
boolean encontrado = false;
int posicion = -1; // -1 indica "no encontrado"
int i = 0;
// Recorre mientras haya elementos Y no hayamos encontrado
while (i < numeros.length && !encontrado) {
if (numeros[i] == buscado) {
encontrado = true;
posicion = i;
}
i = i + 1;
}
if (encontrado) {
System.out.println("Encontrado en posición: " + posicion);
} else {
System.out.println("No encontrado");
}Búsqueda con bandera
Análisis del patrón:
Inicialización:
encontrado = falseyposicion = -1(valor centinela que indica “no encontrado”).Condición del while:
i < numeros.length && !encontradoContinúa si hay elementos por revisar Y todavía no encontramos el buscado.
Cuando encontramos el elemento,
encontradose vuelvetrue,!encontradoesfalse, y el lazo termina.
Resultado: Después del lazo,
encontradonos dice si tuvimos éxito, yposicionnos da la ubicación.
Arreglos como Referencias¶
En Java, las variables de arreglo son referencias (conceptualmente similares a punteros en C). Entender esto es fundamental para evitar errores comunes.
¿Qué es una Referencia?¶
Una referencia es un valor que indica la ubicación de un objeto en memoria. Cuando declarás int[] numeros, la variable numeros no contiene el arreglo en sí, sino una referencia (dirección de memoria) hacia donde está el arreglo en el heap.
Figure 1:Representación de un arreglo en memoria: la variable (referencia) vive en el Stack, mientras que el arreglo con sus datos vive en el Heap.
Analogía: pensá en la referencia como la dirección de una casa. La variable numeros contiene la dirección “Calle 123”, pero la casa (el arreglo) está en esa dirección, no dentro de la variable.
Asignación de Referencias (Alias)¶
Cuando asignás un arreglo a otra variable, no se copian los datos. Solo se copia la referencia, por lo que ambas variables terminan apuntando al mismo arreglo en memoria. Esto se llama crear un alias.
Figure 2:Diferencia entre asignación de referencias (alias) y copia real de arreglos.
int[] original = {1, 2, 3};
int[] alias = original; // alias apunta al MISMO arreglo, NO es una copia
alias[0] = 100; // Modifica a través del alias
System.out.println(original[0]); // Imprime: 100
System.out.println(alias[0]); // Imprime: 100
// Ambos "ven" el mismo cambio porque apuntan al mismo arregloAsignación de referencias - creando un alias
¿Por qué pasa esto? La línea int[] alias = original; copia el valor de la referencia (la dirección de memoria), no el contenido del arreglo. Después de esta línea, tanto original como alias contienen la misma dirección, por lo que ambos apuntan al mismo arreglo.
Comparación de Arreglos¶
El operador == compara referencias (direcciones de memoria), no el contenido de los arreglos:
int[] a = {1, 2, 3};
int[] b = {1, 2, 3}; // Mismo contenido, pero otro arreglo en memoria
int[] c = a; // Alias: misma referencia que 'a'
System.out.println(a == b); // false: diferentes referencias (diferentes arreglos)
System.out.println(a == c); // true: misma referencia (mismo arreglo)Comparación de referencias vs contenido
Aunque a y b tienen exactamente el mismo contenido {1, 2, 3}, son dos arreglos diferentes en memoria, por lo que a == b es false.
Para comparar el contenido de dos arreglos (verificar si tienen los mismos elementos), usá Arrays.equals():
import java.util.Arrays;
int[] a = {1, 2, 3};
int[] b = {1, 2, 3};
int[] d = {1, 2, 4};
System.out.println(Arrays.equals(a, b)); // true: mismo contenido
System.out.println(Arrays.equals(a, d)); // false: contenido diferenteComparación de contenido con Arrays.equals
Arrays.equals() compara elemento por elemento y retorna true solo si:
Ambos arreglos tienen la misma longitud.
Cada elemento en la posición
ide un arreglo es igual al elemento en la posiciónidel otro.
Copia de Arreglos¶
Para crear una copia independiente de un arreglo (donde modificar uno no afecte al otro), tenés varias opciones:
import java.util.Arrays;
int[] original = {1, 2, 3, 4, 5};
// Opción 1: Arrays.copyOf (recomendada)
// Copia todos los elementos a un nuevo arreglo
int[] copia1 = Arrays.copyOf(original, original.length);
// Opción 2: clone() (método heredado de Object)
int[] copia2 = original.clone();
// Opción 3: Copia manual con lazo
int[] copia3 = new int[original.length];
for (int i = 0; i < original.length; i = i + 1) {
copia3[i] = original[i];
}
// Opción 4: System.arraycopy (más bajo nivel, más rápida para arreglos grandes)
int[] copia4 = new int[original.length];
System.arraycopy(original, 0, copia4, 0, original.length);
// Ahora las copias son independientes
copia1[0] = 100;
System.out.println(original[0]); // 1 (no afectado)
System.out.println(copia1[0]); // 100Diferentes formas de copiar un arreglo
Arrays.copyOf(arreglo, nuevoTamaño) también permite cambiar el tamaño:
int[] original = {1, 2, 3};
// Copia más grande: los nuevos espacios se llenan con 0
int[] masGrande = Arrays.copyOf(original, 5); // {1, 2, 3, 0, 0}
// Copia más pequeña: se trunca
int[] masPequeno = Arrays.copyOf(original, 2); // {1, 2}Copia con cambio de tamaño
Arrays.copyOfRange(arreglo, desde, hasta) copia una porción:
int[] original = {10, 20, 30, 40, 50};
// Copia desde índice 1 hasta índice 4 (sin incluir el 4)
int[] porcion = Arrays.copyOfRange(original, 1, 4); // {20, 30, 40}Copia de un rango
Arreglos y Métodos¶
Los arreglos interactúan con los métodos de formas específicas que es importante entender para escribir código correcto.
Pasaje de Arreglos a Métodos¶
Cuando pasás un arreglo a un método, se pasa la referencia (la dirección de memoria), no una copia del arreglo. Esto tiene dos consecuencias importantes:
Es eficiente: no se copia todo el contenido del arreglo, solo la referencia (unos pocos bytes).
El método puede modificar el original: cualquier cambio que haga el método a los elementos será visible después de que el método termine.
public static void duplicarElementos(int[] arr) {
for (int i = 0; i < arr.length; i = i + 1) {
arr[i] = arr[i] * 2;
}
}
public static void main(String[] args) {
int[] numeros = {1, 2, 3, 4, 5};
System.out.println("Antes: " + Arrays.toString(numeros)); // [1, 2, 3, 4, 5]
duplicarElementos(numeros);
System.out.println("Después: " + Arrays.toString(numeros)); // [2, 4, 6, 8, 10]
// ¡El arreglo original fue modificado!
}Pasaje de arreglo a método
Retorno de Arreglos¶
Un método puede crear un arreglo internamente y retornarlo. El método retorna la referencia al arreglo creado, que vive en el heap y sobrevive después de que el método termine.
public static int[] crearSecuencia(int inicio, int cantidad) {
int[] resultado = new int[cantidad]; // Se crea en el heap
for (int i = 0; i < cantidad; i = i + 1) {
resultado[i] = inicio + i;
}
return resultado; // Retorna la referencia al arreglo
}
public static void main(String[] args) {
int[] secuencia = crearSecuencia(10, 5);
// secuencia es {10, 11, 12, 13, 14}
System.out.println(Arrays.toString(secuencia));
}Método que retorna un arreglo
Patrón: Método que Calcula sin Modificar¶
Para evitar efectos secundarios y hacer el código más predecible, podés crear métodos que retornen un nuevo arreglo en lugar de modificar el original. Este estilo se llama programación sin efectos secundarios o estilo funcional:
public static int[] duplicar(int[] original) {
// Crear un NUEVO arreglo para el resultado
int[] resultado = new int[original.length];
// Copiar los valores duplicados al nuevo arreglo
for (int i = 0; i < original.length; i = i + 1) {
resultado[i] = original[i] * 2;
}
return resultado; // Retorna el nuevo arreglo
}
public static void main(String[] args) {
int[] numeros = {1, 2, 3};
int[] duplicados = duplicar(numeros);
System.out.println(Arrays.toString(numeros)); // [1, 2, 3] - sin cambios
System.out.println(Arrays.toString(duplicados)); // [2, 4, 6] - nuevo arreglo
}Método sin efecto secundario
Ventajas de este patrón:
El arreglo original no se modifica, evitando sorpresas.
El código es más fácil de entender y testear.
Es seguro llamar al método desde cualquier parte del código.
Desventaja:
Usa más memoria porque crea un nuevo arreglo.
Reasignación de la Referencia dentro del Método¶
Un detalle importante: un método no puede cambiar a qué apunta una referencia fuera del método. Reasignar el parámetro solo afecta la copia local de la referencia:
public static void intentarReemplazar(int[] arr) {
arr = new int[]{100, 200, 300}; // Solo cambia la copia local
// El arreglo original sigue intacto
}
public static void main(String[] args) {
int[] datos = {1, 2, 3};
intentarReemplazar(datos);
System.out.println(Arrays.toString(datos)); // [1, 2, 3] - sin cambios
}Reasignar la referencia no afecta afuera
El parámetro arr es una copia de la referencia. Modificar el contenido del arreglo (arr[0] = 999) sí afecta al original, pero reasignar arr a otro arreglo solo cambia la copia local.
Arreglos Multidimensionales¶
Java permite crear arreglos de arreglos, que se usan comúnmente para representar estructuras bidimensionales como matrices, tableros de juego, imágenes, etc.
Concepto: Arreglo de Arreglos¶
Técnicamente, Java no tiene “arreglos multidimensionales” verdaderos como otros lenguajes. Lo que tiene son arreglos cuyos elementos son otros arreglos. Una matriz int[][] es un arreglo de int[].
Figure 3:Representación de una matriz en memoria: un arreglo principal contiene referencias a arreglos individuales (las filas).
Esta arquitectura tiene implicaciones importantes:
Cada fila es un arreglo independiente en memoria.
Las filas pueden tener diferentes tamaños (arreglos irregulares).
Acceder a un elemento requiere dos accesos a memoria (primero la fila, luego el elemento).
Declaración y Creación de Matrices¶
int[][] matriz = new int[3][4]; // 3 filas, 4 columnas
// Acceso: matriz[fila][columna]
matriz[0][0] = 1; // Primera fila, primera columna
matriz[2][3] = 12; // Tercera fila (índice 2), cuarta columna (índice 3)
// Dimensiones
int filas = matriz.length; // 3 (cantidad de filas)
int columnas = matriz[0].length; // 4 (cantidad de columnas en la fila 0)Matriz bidimensional rectangular
La expresión new int[3][4] crea:
Un arreglo de 3 referencias (las filas).
Tres arreglos de 4 enteros cada uno (el contenido de cada fila).
Todos los elementos se inicializan a 0.
Inicialización de Matrices¶
Al igual que con arreglos unidimensionales, podés inicializar una matriz directamente con valores:
int[][] matriz = {
{1, 2, 3, 4}, // Fila 0: 4 columnas
{5, 6, 7, 8}, // Fila 1: 4 columnas
{9, 10, 11, 12} // Fila 2: 4 columnas
};
// Acceso a elementos específicos
int valor = matriz[1][2]; // Fila 1, columna 2 → 7Inicialización directa de matriz
El compilador deduce las dimensiones automáticamente (3 filas × 4 columnas en este caso).
Recorrido de Matrices¶
Para procesar todos los elementos de una matriz, usás lazos anidados: un lazo externo para las filas y uno interno para las columnas.
int[][] matriz = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
int filas = matriz.length; // 3 filas
int columnas = matriz[0].length; // 3 columnas (asumiendo matriz rectangular)
// Recorrido fila por fila
for (int fila = 0; fila < filas; fila = fila + 1) {
for (int col = 0; col < columnas; col = col + 1) {
System.out.print(matriz[fila][col] + "\t");
}
System.out.println(); // Nueva línea después de cada fila
}
// Salida:
// 1 2 3
// 4 5 6
// 7 8 9Recorrido de matriz con for anidado
Recorrido con for-each¶
También podés usar for-each anidados para recorrer matrices:
int[][] matriz = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
for (int[] fila : matriz) { // Cada fila es un int[]
for (int elemento : fila) { // Cada elemento de la fila
System.out.print(elemento + " ");
}
System.out.println();
}Recorrido de matriz con for-each
Matrices Irregulares (Jagged Arrays)¶
Como cada fila es un arreglo independiente, pueden tener diferentes tamaños. Esto se llama arreglo irregular o jagged array:
// Crear solo el arreglo de filas (sin especificar columnas)
int[][] triangulo = new int[3][];
// Crear cada fila con tamaño diferente
triangulo[0] = new int[1]; // Primera fila: 1 elemento
triangulo[1] = new int[2]; // Segunda fila: 2 elementos
triangulo[2] = new int[3]; // Tercera fila: 3 elementos
// O con inicialización directa
int[][] pascal = {
{1},
{1, 1},
{1, 2, 1},
{1, 3, 3, 1}
};Matriz irregular
Casos de uso de matrices irregulares:
Triángulo de Pascal (como arriba)
Representar estructuras donde las filas tienen naturalmente tamaños diferentes
Ahorrar memoria cuando muchas filas serían pequeñas
Operaciones Comunes con Arreglos¶
Esta sección presenta algoritmos fundamentales que todo programador debe conocer. Estas operaciones son la base para resolver problemas más complejos.
Encontrar Máximo y Mínimo¶
El patrón para encontrar el máximo es: asumir que el primer elemento es el máximo, y luego recorrer el resto comparando:
public static int maximo(int[] arr) {
// Precondición: el arreglo no debe estar vacío
int max = arr[0]; // Asumir que el primero es el máximo
for (int i = 1; i < arr.length; i = i + 1) { // Empezar desde 1
if (arr[i] > max) {
max = arr[i]; // Encontramos uno mayor
}
}
return max;
}
// Uso:
int[] datos = {3, 7, 2, 9, 1};
int mayor = maximo(datos); // 9Buscar máximo en un arreglo
El algoritmo para el mínimo es idéntico, solo cambiás > por <:
public static int minimo(int[] arr) {
int min = arr[0];
for (int i = 1; i < arr.length; i = i + 1) {
if (arr[i] < min) {
min = arr[i];
}
}
return min;
}Buscar mínimo en un arreglo
Sumar Elementos¶
El patrón acumulador: inicializar una variable a 0 y sumar cada elemento:
public static int sumar(int[] arr) {
int suma = 0; // Acumulador inicializado en el elemento neutro de la suma
for (int i = 0; i < arr.length; i = i + 1) {
suma = suma + arr[i];
}
return suma;
}
// También se puede usar for-each (más legible para este caso):
public static int sumarConForEach(int[] arr) {
int suma = 0;
for (int elemento : arr) {
suma = suma + elemento;
}
return suma;
}Sumar todos los elementos
Calcular Promedio¶
El promedio es la suma dividida por la cantidad de elementos. Importante: hacer la división con punto flotante:
public static double promedio(int[] arr) {
int suma = sumar(arr);
// El cast a double evita división entera
return (double) suma / arr.length;
}
// Uso:
int[] notas = {7, 8, 6, 9, 10};
double prom = promedio(notas); // 8.0Calcular promedio
Contar Elementos que Cumplen una Condición¶
Otro patrón fundamental: recorrer contando cuántos elementos satisfacen cierta condición:
public static int contarPares(int[] arr) {
int contador = 0;
for (int i = 0; i < arr.length; i = i + 1) {
if (arr[i] % 2 == 0) { // Condición: ser par
contador = contador + 1;
}
}
return contador;
}
// Más general: contar mayores a un umbral
public static int contarMayoresQue(int[] arr, int umbral) {
int contador = 0;
for (int i = 0; i < arr.length; i = i + 1) {
if (arr[i] > umbral) {
contador = contador + 1;
}
}
return contador;
}Contar elementos que cumplen condición
Verificar si Todos/Alguno Cumple una Condición¶
Dos patrones relacionados con el conteo:
// ¿TODOS los elementos son positivos?
public static boolean todosSonPositivos(int[] arr) {
boolean todosCumplen = true;
int i = 0;
while (i < arr.length && todosCumplen) {
if (arr[i] <= 0) {
todosCumplen = false; // Encontramos uno que no cumple
}
i = i + 1;
}
return todosCumplen;
}
// ¿ALGÚN elemento es negativo?
public static boolean existeNegativo(int[] arr) {
boolean encontrado = false;
int i = 0;
while (i < arr.length && !encontrado) {
if (arr[i] < 0) {
encontrado = true;
}
i = i + 1;
}
return encontrado;
}Verificar condiciones universales y existenciales
Invertir Arreglo¶
Intercambiar elementos desde los extremos hacia el centro:
public static void invertir(int[] arr) {
int izq = 0;
int der = arr.length - 1;
while (izq < der) {
// Intercambiar arr[izq] y arr[der]
int temp = arr[izq];
arr[izq] = arr[der];
arr[der] = temp;
izq = izq + 1;
der = der - 1;
}
}
// Ejemplo:
// Antes: {1, 2, 3, 4, 5}
// Después: {5, 4, 3, 2, 1}Invertir arreglo en su lugar
Este algoritmo es in-place: modifica el arreglo original sin crear uno nuevo, usando solo O(1) memoria adicional (la variable temp).
Clase Arrays (Utilidades)¶
Java provee la clase java.util.Arrays con métodos de utilidad para trabajar con arreglos. Estos métodos están optimizados y son más seguros que implementaciones manuales.
Para usar esta clase, debés importarla al inicio del archivo:
import java.util.Arrays;Métodos Principales¶
import java.util.Arrays;
int[] numeros = {5, 2, 8, 1, 9};
// ========== ORDENAMIENTO ==========
Arrays.sort(numeros);
// numeros ahora es {1, 2, 5, 8, 9}
// Usa un algoritmo eficiente (Dual-Pivot Quicksort)
// ========== CONVERSIÓN A STRING ==========
String texto = Arrays.toString(numeros);
System.out.println(texto); // Imprime: [1, 2, 5, 8, 9]
// Muy útil para debugging
// ========== LLENAR CON UN VALOR ==========
int[] ceros = new int[5];
Arrays.fill(ceros, 0); // {0, 0, 0, 0, 0}
int[] unos = new int[3];
Arrays.fill(unos, 1); // {1, 1, 1}
// ========== BÚSQUEDA BINARIA ==========
// IMPORTANTE: el arreglo DEBE estar ordenado
int[] ordenado = {1, 2, 5, 8, 9};
int pos = Arrays.binarySearch(ordenado, 5); // Retorna 2 (índice de 5)
int noEncontrado = Arrays.binarySearch(ordenado, 7); // Retorna negativo
// ========== COMPARACIÓN DE CONTENIDO ==========
int[] a = {1, 2, 3};
int[] b = {1, 2, 3};
int[] c = {1, 2, 4};
boolean iguales1 = Arrays.equals(a, b); // true
boolean iguales2 = Arrays.equals(a, c); // false
// ========== COPIA ==========
int[] original = {1, 2, 3, 4, 5};
int[] copiaCompleta = Arrays.copyOf(original, original.length); // {1, 2, 3, 4, 5}
int[] copiaParcial = Arrays.copyOf(original, 3); // {1, 2, 3}
int[] copiaExtendida = Arrays.copyOf(original, 7); // {1, 2, 3, 4, 5, 0, 0}
// Copiar un rango específico
int[] rango = Arrays.copyOfRange(original, 1, 4); // {2, 3, 4} (índices 1, 2, 3)Métodos de la clase Arrays
Resumen de Métodos de Arrays¶
| Método | Descripción | Precondición |
|---|---|---|
sort(arr) | Ordena el arreglo de menor a mayor | - |
toString(arr) | Convierte a String legible | - |
fill(arr, valor) | Llena todo el arreglo con el valor | - |
equals(arr1, arr2) | Compara contenido elemento por elemento | - |
copyOf(arr, len) | Copia a un nuevo arreglo de tamaño len | - |
copyOfRange(arr, from, to) | Copia el rango [from, to) | from <= to |
binarySearch(arr, val) | Busca val y retorna su índice | Arreglo ordenado |
Ejercicios de Aplicación¶
Los siguientes ejercicios te ayudarán a consolidar los conceptos de arreglos. Intentá resolverlos antes de ver las soluciones.
Ejercicios de Comprensión¶
Solution to Exercise 1
Imprime [999, 2, 3].
Explicación paso a paso:
Se crea
datosapuntando a un arreglo{1, 2, 3}en el heap.Se llama a
modificar(datos), pasando una copia de la referencia.Dentro del método,
arrapunta al mismo arreglo quedatos.arr[0] = 999modifica el arreglo original (ahora es{999, 2, 3}).arr = new int[]{100, 200, 300}crea un NUEVO arreglo y hace quearrapunte a él. Pero esto solo cambia la variable localarr, no afecta adatos.El método termina. El nuevo arreglo
{100, 200, 300}se pierde (no hay referencias a él).datossigue apuntando al arreglo original, que ahora es{999, 2, 3}.
Moraleja: Podés modificar el contenido del arreglo desde un método, pero no podés cambiar a qué arreglo apunta la referencia original.
Ejercicios de Programación¶
Solution to Exercise 2
public static int[] sumarFilas(int[][] matriz) {
// Un resultado por cada fila
int[] sumas = new int[matriz.length];
for (int fila = 0; fila < matriz.length; fila = fila + 1) {
int suma = 0;
for (int col = 0; col < matriz[fila].length; col = col + 1) {
suma = suma + matriz[fila][col];
}
sumas[fila] = suma;
}
return sumas;
}
// Ejemplo de uso:
int[][] m = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
int[] resultado = sumarFilas(m); // {6, 15, 24}Solution to Exercise 3
public static boolean esRectangular(int[][] m) {
// Caso especial: matriz vacía o nula
if (m == null || m.length == 0) {
return true; // Consideramos que una matriz vacía es rectangular
}
// Todas las filas deben tener esta cantidad de columnas
int columnasEsperadas = m[0].length;
// Usar patrón de bandera
boolean esRect = true;
int i = 1; // Empezamos desde la fila 1 (ya vimos la 0)
while (i < m.length && esRect) {
// Verificar que la fila no sea null y tenga el tamaño correcto
if (m[i] == null || m[i].length != columnasEsperadas) {
esRect = false;
}
i = i + 1;
}
return esRect;
}
// Ejemplos:
// esRectangular({{1, 2}, {3, 4}, {5, 6}}) → true
// esRectangular({{1}, {2, 3}, {4, 5, 6}}) → falseSolution to Exercise 4
public static void rotarDerecha(int[] arr) {
// Caso especial: arreglo vacío o de un elemento
if (arr.length <= 1) {
return; // Nada que rotar
}
// Guardar el último elemento (se "caería" del arreglo)
int ultimo = arr[arr.length - 1];
// Desplazar todos los elementos una posición a la derecha
// Empezamos desde el final para no sobrescribir datos
for (int i = arr.length - 1; i > 0; i = i - 1) {
arr[i] = arr[i - 1];
}
// Colocar el último elemento guardado al principio
arr[0] = ultimo;
}
// Traza para {1, 2, 3, 4, 5}:
// 1. Guardamos: ultimo = 5
// 2. Desplazamos: {1, 2, 3, 4, 4} → {1, 2, 3, 3, 4} → {1, 2, 2, 3, 4} → {1, 1, 2, 3, 4}
// 3. Ponemos ultimo: {5, 1, 2, 3, 4}Solution to Exercise 5
public static int buscarPosicion(int[] arr, int buscado) {
boolean encontrado = false;
int posicion = -1; // Valor que indica "no encontrado"
int i = 0;
while (i < arr.length && !encontrado) {
if (arr[i] == buscado) {
encontrado = true;
posicion = i;
}
i = i + 1;
}
return posicion;
}
// Ejemplos:
// buscarPosicion({10, 20, 30, 40}, 30) → 2
// buscarPosicion({10, 20, 30, 40}, 25) → -1
// buscarPosicion({5, 5, 5}, 5) → 0 (primera ocurrencia)Solution to Exercise 6
public static int[] eliminarDuplicados(int[] ordenado) {
if (ordenado.length == 0) {
return new int[0];
}
// Primero contamos cuántos elementos únicos hay
int unicos = 1; // El primero siempre es único
for (int i = 1; i < ordenado.length; i = i + 1) {
if (ordenado[i] != ordenado[i - 1]) {
unicos = unicos + 1;
}
}
// Creamos el arreglo resultado con el tamaño exacto
int[] resultado = new int[unicos];
resultado[0] = ordenado[0];
int indiceResultado = 1;
// Copiamos solo los elementos diferentes al anterior
for (int i = 1; i < ordenado.length; i = i + 1) {
if (ordenado[i] != ordenado[i - 1]) {
resultado[indiceResultado] = ordenado[i];
indiceResultado = indiceResultado + 1;
}
}
return resultado;
}
// El algoritmo funciona porque el arreglo está ordenado:
// los duplicados siempre están consecutivos.Resumen¶
Los arreglos en Java son estructuras fundamentales que todo programador debe dominar. Los puntos clave a recordar:
| Concepto | Descripción |
|---|---|
| Declaración | tipo[] nombre; crea una referencia, no el arreglo |
| Creación | new tipo[tamaño] reserva memoria e inicializa valores |
| Tamaño fijo | Una vez creado, el tamaño no puede cambiar |
| Índices | Comienzan en 0, hasta length - 1 |
| Verificación de límites | Java lanza excepción si el índice es inválido |
| Referencias | Las variables de arreglo son referencias, no copias |
| Pasaje a métodos | Se pasa la referencia; el método puede modificar el contenido |
Comparación Java vs C¶
| Aspecto | C | Java |
|---|---|---|
| Tamaño del arreglo | Debe pasarse como parámetro | Disponible con .length |
| Verificación de límites | No hay (comportamiento indefinido) | Automática (excepción) |
| Gestión de memoria | Manual (malloc/free) | Automática (garbage collector) |
| Valores iniciales | Basura | Valores por defecto (0, false, null) |
| Retornar arreglos | Peligroso si es local | Seguro (siempre en heap) |
Referencias Bibliográficas¶
Schildt, H. (2022). Java: A Beginner’s Guide (9na ed.). McGraw Hill. Capítulo 5: Arrays.
Liang, Y. D. (2017). Introduction to Java Programming and Data Structures (11va ed.). Pearson. Capítulos 7 y 8.
Horstmann, C. (2019). Core Java Volume I: Fundamentals (11va ed.). Pearson. Sección 3.10.
Oracle Corporation. (2023). The Java Language Specification. Chapter 10: Arrays.
Oracle Corporation. (2023). Java API Documentation. java.util.Arrays.