Matrices en Java: una guía de referencia

1. Introducción

En este tutorial, profundizaremos en un concepto central en el lenguaje Java: matrices.

Primero veremos qué es una matriz, luego cómo usarlos; en general, cubriremos cómo:

  • Empiece con matrices
  • Leer y escribir elementos de matrices
  • Bucle sobre una matriz
  • Transforme matrices en otros objetos como List o Streams
  • Ordenar, buscar y combinar matrices

2. ¿Qué es una matriz?

Lo primero es lo primero, necesitamos definir qué es una matriz. Según la documentación de Java, una matriz es un objeto que contiene un número fijo de valores del mismo tipo . Los elementos de una matriz están indexados, lo que significa que podemos acceder a ellos con números (llamados índices ).

Podemos considerar una matriz como una lista numerada de celdas, cada celda es una variable que contiene un valor. En Java, la numeración comienza en 0.

Hay matrices de tipos primitivos y matrices de tipos de objetos. Esto significa que podemos usar matrices de int, float, boolean,… pero también matrices de String, Object y tipos personalizados.

3. Configuración de una matriz

Ahora que las matrices están bien definidas, profundicemos en sus usos.

Cubriremos muchos temas que nos enseñarán cómo usar matrices. Aprenderemos algunos conceptos básicos como cómo declarar e inicializar una matriz, pero también cubriremos temas más avanzados como ordenar y buscar matrices.

Primero vayamos con la declaración y la inicialización.

3.1. Declaración

Comenzaremos con la declaración. Hay dos formas de declarar una matriz en Java:

int[] anArray;

o:

int anOtherArray[];

El primero es más utilizado que el segundo .

3.2. Inicialización

Ahora es el momento de ver cómo inicializar matrices. Nuevamente, hay varias formas de inicializar una matriz. Veremos los principales aquí, pero este artículo cubre la inicialización de matrices en detalle.

Comencemos con una forma sencilla:

int[] anArray = new int[10];

Al usar este método, inicializamos una matriz de diez elementos int . Tenga en cuenta que necesitamos especificar el tamaño de la matriz.

Cuando usamos este método, inicializamos cada elemento a su valor predeterminado , aquí 0 . Al inicializar una matriz de Object , los elementos son nulos de forma predeterminada.

Ahora veremos otra forma que nos da la posibilidad de establecer valores en la matriz directamente al crearla:

int[] anArray = new int[] {1, 2, 3, 4, 5};

Aquí, inicializamos una matriz de cinco elementos que contiene números del 1 al 5. Cuando usamos este método no necesitamos especificar la longitud de la matriz, es el número de elementos que luego se declaran entre llaves.

4. Acceso a elementos

Veamos ahora cómo acceder a los elementos de una matriz. Podemos lograr esto requiriendo una posición de celda de matriz.

Por ejemplo, este pequeño fragmento de código imprimirá 10 en la consola:

anArray[0] = 10; System.out.println(anArray[0]);

Tenga en cuenta cómo estamos usando índices para acceder a las celdas de la matriz. El número entre paréntesis es la posición específica de la matriz a la que queremos acceder.

Al acceder a una celda, si el índice pasado es negativo o va más allá de la última celda, Java lanzará una ArrayIndexOutOfBoundException .

Debemos tener cuidado de no usar un índice negativo, o un índice mayor o igual al tamaño de la matriz .

5. Iterando sobre una matriz

Acceder a los elementos uno por uno puede ser útil, pero es posible que deseemos recorrer una matriz. Veamos cómo podemos lograrlo.

La primera forma es usar el bucle for :

int[] anArray = new int[] {1, 2, 3, 4, 5}; for (int i = 0; i < anArray.length; i++) { System.out.println(anArray[i]); }

Esto debería imprimir los números del 1 al 5 en la consola. Como podemos ver, hicimos uso de la propiedad length . Esta es una propiedad pública que nos da el tamaño de la matriz.

Por supuesto, es posible utilizar otros mecanismos de bucle como while o do while . Pero, en cuanto a las colecciones de Java, es posible recorrer las matrices usando el bucle foreach :

int[] anArray = new int[] {1, 2, 3, 4, 5}; for (int element : anArray) { System.out.println(element); }

This example is equivalent to the previous one, but we got rid of the indices boilerplate code. The foreach loop is an option when:

  • we don't need to modify the array (putting another value in an element won't modify the element in the array)
  • we don't need the indices to do something else

6. Varargs

We've already covered the basics when it comes to the creation and manipulation of arrays. Now, we'll dive into more advanced topics, beginning with varargs. As a reminder, varargs are used to pass an arbitrary number of arguments to a method:

void varargsMethod(String... varargs) {}

This method could take from 0 to an arbitrary number of String arguments. An article covering varargs can be found here.

What we have to know here is that inside the method body, a varargs parameter turns into an array. But, we can also pass an array directly as the argument. Let's see how by reusing the example method declared above:

String[] anArray = new String[] {"Milk", "Tomato", "Chips"}; varargsMethod(anArray);

Will behave the same as:

varargsMethod("Milk", "Tomato", "Chips");

7. Transforming an Array into a List

Arrays are great, but sometimes it can be handier to deal with List instead. We'll see here how to transform an array into a List.

We'll first do it the naïve way, by creating an empty list and iterating over the array to add its elements to the list:

int[] anArray = new int[] {1, 2, 3, 4, 5}; List aList = new ArrayList(); for (int element : anArray) { aList.add(element); }

But there is another way, a little bit more succinct:

Integer[] anArray = new Integer[] {1, 2, 3, 4, 5}; List aList = Arrays.asList(anArray);

The static method Arrays.asList takes a varargs argument and creates a list with the passed values. Unfortunately, this method comes with some drawbacks:

  • It's not possible to use an array of primitive types
  • We can't add or remove elements from the created list, as it'll throw an UnsupportedOperationException

8. From an Array to a Stream

We can now transform arrays into lists, but since Java 8 we have access to the Stream API and we might want to turn our arrays into Stream. Java provides us with the Arrays.stream method for that:

String[] anArray = new String[] {"Milk", "Tomato", "Chips"}; Stream aStream = Arrays.stream(anArray);

When passing an Object array to the method it will return a Stream of the matching type (e.g. Stream for an array of Integer). When passing a primitive one it will return the corresponding primitive Stream.

It's also possible to create the stream only on a subset of the array:

Stream anotherStream = Arrays.stream(anArray, 1, 3);

This will create a Stream with only “Tomato” and “Chips” Strings (the first index being inclusive while the second one is exclusive).

9. Sorting Arrays

Let's now see how to sort an array, that is rearranging its elements in a certain order. The Arrays class provides us with the sort method. A bit like the stream method, sort has a lot of overloadings.

There are overloadings to sort:

  • Primitive type arrays: which are sorted in ascending order
  • Object arrays (those Object must implement the Comparable interface): which are sorted according to the natural order (relying on the compareTo method from Comparable)
  • Generic arrays: which are sorted according to a given Comparator

In addition, it's possible to sort only a specific portion of an array (passing start and end indices to the method).

The algorithms behind the sort method are quick sort and merge sort for primitive and other arrays, respectively.

Let's see how this all work through some examples:

int[] anArray = new int[] {5, 2, 1, 4, 8}; Arrays.sort(anArray); // anArray is now {1, 2, 4, 5, 8} Integer[] anotherArray = new Integer[] {5, 2, 1, 4, 8}; Arrays.sort(anotherArray); // anotherArray is now {1, 2, 4, 5, 8} String[] yetAnotherArray = new String[] {"A", "E", "Z", "B", "C"}; Arrays.sort(yetAnotherArray, 1, 3, Comparator.comparing(String::toString).reversed()); // yetAnotherArray is now {"A", "Z", "E", "B", "C"}

10. Searching in an Array

Searching an array is pretty simple, we can loop over the array and search our element among the array elements:

int[] anArray = new int[] {5, 2, 1, 4, 8}; for (int i = 0; i < anArray.length; i++) { if (anArray[i] == 4) { System.out.println("Found at index " + i); break; } }

Here we searched for number 4 and found it at index 3.

If we have a sorted array though, we can use another solution: the binary search. The principle of binary search is explained in this article.

Fortunately, Java provides us with the Arrays.binarySearch method. We have to give it an array and an element to search.

In case of a generic array, we also have to give it the Comparator that was used to sort the array in the first place. There is again the possibility to call the method on a subset of the array.

Let's see an example of the binary search method usage:

int[] anArray = new int[] {1, 2, 3, 4, 5}; int index = Arrays.binarySearch(anArray, 4); System.out.println("Found at index " + index);

As we stored number 4 in the fourth cell, this will return index 3 as the result. Note that we used an already sorted array.

11. Concatenating Arrays

Finally, let's see how to concatenate two arrays. The idea is to create an array which length is the sum of the two arrays to concatenate. After that we have to add the elements of the first one and then the elements of the second one:

int[] anArray = new int[] {5, 2, 1, 4, 8}; int[] anotherArray = new int[] {10, 4, 9, 11, 2}; int[] resultArray = new int[anArray.length + anotherArray.length]; for (int i = 0; i < resultArray.length; i++) { resultArray[i] = (i < anArray.length ? anArray[i] : anotherArray[i - anArray.length]); }

As we can see, when the index is still lesser than the first array length we add elements from that array. Then we add elements from the second one. We can make use of the Arrays.setAll method to avoid writing a loop:

int[] anArray = new int[] {5, 2, 1, 4, 8}; int[] anotherArray = new int[] {10, 4, 9, 11, 2}; int[] resultArray = new int[anArray.length + anotherArray.length]; Arrays.setAll(resultArray, i -> (i < anArray.length ? anArray[i] : anotherArray[i - anArray.length]));

This method will set all array element according to the given function. This function associates an index with a result.

Aquí hay una tercera opción para combinar en matrices: System.arraycopy . Este método toma una matriz de origen , una posición de origen, una matriz de destino , una posición de destino y un int que define el número de elementos a copiar:

System.arraycopy(anArray, 0, resultArray, 0, anArray.length); System.arraycopy(anotherArray, 0, resultArray, anArray.length, anotherArray.length);

Como podemos ver, copiamos el primer arreglo, luego el segundo (después del último elemento del primero).

12. Conclusión

En este artículo detallado, hemos cubierto los usos básicos y algunos avanzados de las matrices en Java.

Vimos que Java ofrece muchos métodos para lidiar con matrices a través de la clase de utilidad Arrays . También hay clases de utilidad para manipular matrices en bibliotecas como Apache Commons o Guava.

El código completo de este artículo se puede encontrar en nuestro GitHub.