Как да върнете множество стойности от Java метод

1. Общ преглед

В този урок ще научим различни начини за връщане на множество стойности от метод на Java.

Първо ще върнем масиви и колекции. След това ще покажем как да използваме класове контейнери за сложни данни и ще се научим как да създаваме общи класове кортежи.

Накрая ще видим примери за това как да използваме библиотеки на трети страни за връщане на множество стойности.

2. Използване на масиви

Масивите могат да се използват за връщане на примитивни и референтни типове данни .

Например, следният метод getCoordinates връща масив от две двойни стойности:

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

Ако искаме да върнем масив от различни референтни типове, можем да използваме общ родителски тип като тип на масива :

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

Тук сме дефинирали координатния масив от тип Number, защото това е често срещаният клас между Integer и Double елементи.

3. Използване на колекции

С общите Java колекции можем да върнем множество стойности от общ тип .

Рамката на колекциите има широк спектър от класове и интерфейси. В този раздел обаче ще ограничим дискусията си до интерфейсите List и Map .

3.1. Връщащи стойности от подобен тип в списък

Като начало, нека пренапишем предишния пример за масив, като използваме Списък :

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

Подобно на Number [] , колекцията List съдържа поредица от елементи от смесен тип, всички от един и същ общ тип.

3.2. Връщане на именувани стойности в карта

Ако искаме да дадем име на всеки запис в нашата колекция, вместо него може да се използва Карта :

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

Потребителите на метода getCoordinatesMap могат да използват клавишите “ longitude” или “ latitude” с метода Map # get , за да извлекат съответната стойност.

4. Използване на класове контейнери

За разлика от масивите и колекциите, класовете контейнери (POJO) могат да обгръщат множество полета с различни типове данни .

Например, следният клас Координати има два различни типа данни, double и String :

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 }

Използването на класове контейнери като Координати ни позволява да моделираме сложни типове данни със значими имена .

Следващата стъпка е да създадете екземпляр и да върнете екземпляр от координати :

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

Трябва да отбележим, че се препоръчва да правим класове данни като Координати неизменни . По този начин ние създаваме прости, безопасни за нишки, обекти за споделяне.

5. Използване на Tuples

Подобно на контейнерите, кортежите съхраняват полета от различен тип. Те обаче се различават по това, че не са специфични за приложението .

Те са специализирани, когато ги използваме, за да опишем кои типове искаме да обработват, но са контейнер с общо предназначение с определен брой стойности. Това означава, че не е нужно да пишем персонализиран код, за да ги имаме, и можем да използваме библиотека или да създадем обща единична реализация.

Кортежът може да бъде от произволен брой полета и често се нарича Tuple n, където n е броят на полетата. Например Tuple2 е кортеж от две полета, Tuple3 е кортеж от три полета и т.н.

За да демонстрираме важността на кортежите, нека разгледаме следния пример. Да предположим, че искаме да намерим разстоянието между точка на координатите и всички останали точки в списъка . След това трябва да върнем този най-отдалечен координатен обект, заедно с разстоянието.

Нека първо създадем общ набор от две полета:

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

След това нека приложим нашата логика и използваме екземпляр Tuple2, за да обгърнем резултатите:

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(); }

Използването на Tuple2 в предишния пример ни спести от създаването на отделен клас контейнер за еднократна употреба с този конкретен метод .

Подобно на контейнерите, кортежите трябва да бъдат неизменни . Освен това, поради техния характер с общо предназначение, трябва да използваме кортежи вътрешно, а не като част от публичния ни API .

6. Библиотеки на трети страни

Някои библиотеки на трети страни са внедрили неизменяем тип двойка или тройка . Apache Commons Lang и javatuples са отлични примери. След като имаме тези библиотеки като зависимости в нашето приложение, можем директно да използваме типовете Pair или Triple , предоставени от библиотеките, вместо да ги създаваме сами.

Нека разгледаме пример, използващ Apache Commons Lang за връщане на обект чифт или тройка .

Преди да пристъпим по-нататък, нека добавим зависимостта commons-lang3 в нашия pom.xml:

 org.apache.commons commons-lang3 3.9 

6.1. ImmutablePair от 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.

Също така научихме, че някои библиотеки на трети страни са внедрили двойни и тройни типове и видяхме някои примери от библиотеката Apache Commons Lang.

Както обикновено, изходният код за тази статия е достъпен в GitHub.