本文翻译自:https://www.baeldung.com/java-method-return-multiple-values#:~:text=%20How%20to%20Return%20Multiple%20Values%20From%20a,multiple%20values%20of%20a…%204%20Conclusion%20More%20


文章目录

  • 1.概览
  • 2.使用数组
  • 3.使用collections
  • 3.1 用List返回相似类型的值
  • 3.2 用Map返回命名值
  • 4.使用容器类
  • 5.使用元组
  • 6.第三方库
  • 6.1 Apache Commons Lang中的ImmutablePair
  • 6.2 Apache Commons Lang中的ImmutableTriple
  • 7.小结


1.概览

在本教程中,我们将学习从Java方法返回多个值的不同方法。

首先,我们将返回数组和集合。然后,我们将展示如何对复杂数据使用容器类,并学习如何创建泛型元组类。

最后,我们将看到如何使用第三方库返回多个值的示例。

2.使用数组

数组可用于返回基本数据类型和引用数据类型。
例如,下面的getCoordinates方法返回double数组:

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类型的coordinate数组,因为它是Integer和Double的共同父类。

3.使用collections

使用Java collections,我们可以返回一个类型的多个值。
collections框架具有丰富的类和接口。但是,在本节中,我们将仅讨论List和Map。

3.1 用List返回相似类型的值

首先,让我们使用List重写前面的数组示例:

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

与Number[]一样,List包含多个有公共父类的不同类型元素。

3.2 用Map返回命名值

如果要命名collections中的每个条目,可以使用Map:

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

getCoordinateMap方法的调用者可以使用longitude或latitude键以及Map的get方法来获取相应的值。

4.使用容器类

与数组和集合不同,容器类(pojo)可以包装不同数据类型的多个字段。
例如,以下Coordinates类有两种不同的数据类型: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这样的容器类,我们可以用有意义的名称来建模复杂的数据类型。
下一步是实例化并返回Coordinates类的实例:

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

我们应该注意到,建议我们将coordinate等数据类设置为不可变的。通过这样做,我们可以创建简单的、线程安全的、可共享的对象。

5.使用元组

与容器一样,元组存储不同类型的字段。但是,它们的不同之处在于它们不是特定于应用程序的。

当我们使用它们来描述我们希望它们处理的类型时,它们才会被专门化。它们是包含一些值的通用容器。这意味着我们不需要编写定制代码就可以拥有它们,我们可以使用一个库,或者创建一个通用的单一实现。

元组可以是任意数量的字段,通常称为Tuplen,其中n是字段数。例如,Tuple2是两个字段的tuple,Tuple3是三个字段的tuple,依此类推。

为了演示元组的重要性,让我们考虑下面的示例。假设我们想找到一个坐标点和列表中所有其他点之间的距离。然后,我们需要返回最远的坐标对象,以及距离。

首先创建一个通用的双字段元组:

public class Tuple2<K, V> {

    private K first;
    private V second;
  
    public Tuple2(K first, V second){
        this.first = first;
        this.second = second;
    }

    // getters and setters
}

接下来,让我们实现我们的逻辑,并使用Tuple2<Coordinates,Double>实例来包装结果:

Tuple2<Coordinates, Double> getMostDistantPoint(List<Coordinates> 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<Coordinates,Double>可以避免我们创建一个单独的容器类(如若单独创建,则只能在这个方法中用一次。)
像容器一样,元组应该是不可变的。此外,由于元组的通用性,我们应该在内部使用元组,而不是将其作为公共API的一部分。

6.第三方库

一些第三方库实现了不可变的Pair或Triple类型。apache commons Lang和java tuples是主要的例子。一旦我们在应用程序中将这些库作为依赖项,我们就可以直接使用库提供的Pair或Triple类型,而不用自己创建。
让我们看一个使用apache commons Lang返回Pair或Triple对象的示例。
在进一步讨论之前,让我们在pom.xml文件:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.11</version>
</dependency>

6.1 Apache Commons Lang中的ImmutablePair

apache commons Lang中的ImmutablePair类型正是我们想要的:一种用法简单的不可变类型。
它包含两个字段:left和right。让我们看看如何使getMostDistantPoint方法返回ImmutablePair类型的对象:

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

6.2 Apache Commons Lang中的ImmutableTriple

ImmutableTriple与ImmutablePair非常相似。唯一的区别是,正如其名称所示,ImmutableTriple包含三个字段:left、middle和right。
现在,让我们在坐标计算中添加一个新方法来演示如何使用ImmutableTriple类型。
我们将遍历列表中的所有点,找出到给定目标点的最小、平均和最大距离。
让我们看看如何使用ImmutableTriple类通过单个方法返回这三个值:

ImmutableTriple<Double, Double, Double> getMinAvgMaxTriple(
  List<Coordinates> coordinatesList, Coordinates target) {
    List<Double> 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.小结

在本文中,我们学习了如何使用数组、collections、容器类和元组从一个方法返回多个值。我们可以在简单的情况下使用数组和collections,因为它们包装了单个数据类型。
另一方面,容器类和元组在创建复杂类型时很有用,容器类提供了更好的可读性。
我们还了解到,一些第三方库已经实现了pair和triple类型,并看到了apache commons Lang库中的一些示例。
与往常一样,本文的源代码可以在GitHub上找到。