Cómo devolver varios valores desde un método Java

1. Información general

En este tutorial, aprenderemos diferentes formas de devolver múltiples valores desde un método Java.

Primero, devolveremos matrices y colecciones. Luego, mostraremos cómo usar clases de contenedor para datos complejos y aprenderemos a crear clases de tuplas genéricas.

Finalmente, veremos ejemplos de cómo usar bibliotecas de terceros para devolver múltiples valores.

2. Uso de matrices

Las matrices se pueden utilizar para devolver tipos de datos primitivos y de referencia .

Por ejemplo, el siguiente método getCoordinates devuelve una matriz de dos valores dobles :

double[] getCoordinatesDoubleArray() { double[] coordinates = new double[2]; coordinates[0] = 10; coordinates[1] = 12.5; return coordinates; }

Si queremos devolver una matriz de diferentes tipos de referencia, podemos usar un tipo principal común como tipo de matriz :

Number[] getCoordinatesNumberArray() { Number[] coordinates = new Number[2]; coordinates[0] = 10; // Integer coordinates[1] = 12.5; // Double return coordinates; }

Aquí hemos definido la matriz de coordenadas de tipo Número porque es la clase común entre los elementos Integer y Double .

3. Uso de colecciones

Con las colecciones genéricas de Java, podemos devolver varios valores de un tipo común .

El marco de colecciones tiene un amplio espectro de clases e interfaces. Sin embargo, en esta sección, limitaremos nuestra discusión a las interfaces List y Map .

3.1. Devolver valores de tipo similar en una lista

Para empezar, reescribamos el ejemplo de matriz anterior usando List :

List getCoordinatesList() { List coordinates = new ArrayList(); coordinates.add(10); // Integer coordinates.add(12.5); // Double return coordinates; }

Al igual que Number [] , la colección List contiene una secuencia de elementos de tipo mixto, todos del mismo tipo común.

3.2. Devolver valores nombrados en un mapa

Si quisiéramos nombrar cada entrada en nuestra colección, se puede usar un mapa en su lugar:

Map getCoordinatesMap() { Map coordinates = new HashMap(); coordinates.put("longitude", 10); coordinates.put("latitude", 12.5); return coordinates; }

Los usuarios del método getCoordinatesMap pueden usar las teclas de " longitud" o " latitud" con el método Map # get para recuperar el valor correspondiente.

4. Uso de clases de contenedor

A diferencia de las matrices y colecciones, las clases de contenedor (POJO) pueden envolver varios campos con diferentes tipos de datos .

Por ejemplo, la siguiente clase de Coordenadas tiene dos tipos de datos diferentes, doble y Cadena :

public class Coordinates { private double longitude; private double latitude; private String placeName; public Coordinates(double longitude, double latitude, String placeName) { this.longitude = longitude; this.latitude = latitude; this.placeName = placeName; } // getters and setters }

El uso de clases de contenedores como Coordenadas nos permite modelar tipos de datos complejos con nombres significativos .

El siguiente paso es crear una instancia y devolver una instancia de Coordenadas :

Coordinates getCoordinates() { double longitude = 10; double latitude = 12.5; String placeName = "home"; return new Coordinates(longitude, latitude, placeName); }

Debemos tener en cuenta que se recomienda que hagamos que las clases de datos como Coordenadas sean inmutables . Al hacerlo, creamos objetos simples, seguros para subprocesos y que se pueden compartir.

5. Usar tuplas

Al igual que los contenedores, las tuplas almacenan campos de diferentes tipos. Sin embargo, se diferencian en que no son específicas de una aplicación .

Están especializados cuando los usamos para describir qué tipos queremos que manejen, pero son contenedores de propósito general de un cierto número de valores. Esto significa que no necesitamos escribir código personalizado para tenerlos, y podemos usar una biblioteca o crear una implementación única común.

Una tupla puede tener cualquier número de campos y, a menudo, se denomina Tupla n, donde n es el número de campos. Por ejemplo, Tuple2 es una tupla de dos campos, Tuple3 es una tupla de tres campos y así sucesivamente.

Para demostrar la importancia de las tuplas, consideremos el siguiente ejemplo. Suponga que queremos encontrar la distancia entre un punto de Coordenadas y todos los demás puntos dentro de una Lista . Luego, necesitamos devolver el objeto Coordinado más distante, junto con la distancia.

Let's first create a generic two-fields tuple:

public class Tuple2 { private K first; private V second; public Tuple2(K first, V second){ this.first = first; this.second = second; } // getters and setters }

Next, let's implement our logic and use a Tuple2 instance to wrap the results:

Tuple2 getMostDistantPoint(List coordinatesList, Coordinates target) { return coordinatesList.stream() .map(coor -> new Tuple2(coor, coor.calculateDistance(target))) .max((d1, d2) -> Double.compare(d1.getSecond(), d2.getSecond())) // compare distances .get(); }

Using Tuple2 in the previous example has saved us from creating a separate container class for one-time use with this particular method.

Like containers, tuples should be immutable. Additionally, due to their general-purpose nature, we should use tuples internally rather than as part of our public API.

6. Third-Party Libraries

Some third-party libraries have implemented an immutable Pair or Triple type. Apache Commons Lang and javatuples are prime examples. Once we have those libraries as dependencies in our application, we can directly use the Pair or Triple types provided by the libraries instead of creating them by ourselves.

Let's look at an example using Apache Commons Lang to return a Pair or a Triple object.

Before we step further, let's add the commons-lang3 dependency in our pom.xml:

 org.apache.commons commons-lang3 3.9 

6.1. ImmutablePair from Apache Commons Lang

The ImmutablePair type from Apache Commons Lang is exactly what we want: an immutable type whose usage is straightforward.

It contains two fields: left and right. Let's see how to make our getMostDistantPoint method return an object of the ImmutablePair type:

ImmutablePair getMostDistantPoint( List coordinatesList, Coordinates target) { return coordinatesList.stream() .map(coordinates -> ImmutablePair.of(coordinates, coordinates.calculateDistance(target))) .max(Comparator.comparingDouble(Pair::getRight)) .get(); }

6.2. ImmutableTriple from Apache Commons Lang

The ImmutableTriple is pretty similar to the ImmutablePair. The only difference is, as its name tells, an ImmutableTriple contains three fields: left, middle, and right.

Now, let's add a new method to our coordinates calculation to show how to use the ImmutableTriple type.

We're going to go through all points in a List to find out the min, avg, and max distances to the given target point.

Let's see how can we return the three values with a single method using the ImmutableTriple class:

ImmutableTriple getMinAvgMaxTriple( List coordinatesList, Coordinates target) { List distanceList = coordinatesList.stream() .map(coordinates -> coordinates.calculateDistance(target)) .collect(Collectors.toList()); Double minDistance = distanceList.stream().mapToDouble(Double::doubleValue).min().getAsDouble(); Double avgDistance = distanceList.stream().mapToDouble(Double::doubleValue).average().orElse(0.0D); Double maxDistance = distanceList.stream().mapToDouble(Double::doubleValue).max().getAsDouble(); return ImmutableTriple.of(minDistance, avgDistance, maxDistance); }

7. Conclusion

In this article, we've learned how to use arrays, collections, containers, and tuples to return multiple values from a method. We can use arrays and collections in simple cases since they wrap a single data type.

On the other hand, containers and tuples are useful in creating complex types, with containers offering better readability.

También aprendimos que algunas bibliotecas de terceros han implementado tipos de pares y triples y vimos algunos ejemplos de la biblioteca Apache Commons Lang.

Como de costumbre, el código fuente de este artículo está disponible en GitHub.