我想从一个Java方法中返回两个对象,并且想知道有什么好的方法可以实现这一点?
我能想到的可能方法是:返回一个HashMap
(因为这两个对象是相关的),或者返回一个Object
对象的ArrayList
。
更准确地说,我要返回的两个对象是(a)对象列表和(b)相同对象的逗号分隔名称。
我希望从一个方法中返回这两个对象,因为我不想遍历对象列表来获取逗号分隔的名称(我可以在此方法的同一个循环中完成这些操作)。
以某种方式,返回一个HashMap
并不是一个非常优雅的方法。
我想从一个Java方法中返回两个对象,并且想知道有什么好的方法可以实现这一点?
我能想到的可能方法是:返回一个HashMap
(因为这两个对象是相关的),或者返回一个Object
对象的ArrayList
。
更准确地说,我要返回的两个对象是(a)对象列表和(b)相同对象的逗号分隔名称。
我希望从一个方法中返回这两个对象,因为我不想遍历对象列表来获取逗号分隔的名称(我可以在此方法的同一个循环中完成这些操作)。
以某种方式,返回一个HashMap
并不是一个非常优雅的方法。
public class NamedObject<T> {
public final String name;
public final T object;
public NamedObject(String name, T object) {
this.name = name;
this.object = object;
}
}
List<NamedObject<WhateverTypeYouWant>>
。List<String>
?或者更好的方法是,返回一个Map<String,TheObjectType>
,其中键是名称,值是对象(除非您的对象有指定的顺序,在这种情况下,可能需要使用NavigableMap
)。如果你知道你将返回两个对象,你也可以使用一个通用的pair:
public class Pair<A,B> {
public final A a;
public final B b;
public Pair(A a, B b) {
this.a = a;
this.b = b;
}
};
编辑 上述内容的更完整实现:
package util;
public class Pair<A,B> {
public static <P, Q> Pair<P, Q> makePair(P p, Q q) {
return new Pair<P, Q>(p, q);
}
public final A a;
public final B b;
public Pair(A a, B b) {
this.a = a;
this.b = b;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((a == null) ? 0 : a.hashCode());
result = prime * result + ((b == null) ? 0 : b.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
@SuppressWarnings("rawtypes")
Pair other = (Pair) obj;
if (a == null) {
if (other.a != null) {
return false;
}
} else if (!a.equals(other.a)) {
return false;
}
if (b == null) {
if (other.b != null) {
return false;
}
} else if (!b.equals(other.b)) {
return false;
}
return true;
}
public boolean isInstance(Class<?> classA, Class<?> classB) {
return classA.isInstance(a) && classB.isInstance(b);
}
@SuppressWarnings("unchecked")
public static <P, Q> Pair<P, Q> cast(Pair<?, ?> pair, Class<P> pClass, Class<Q> qClass) {
if (pair.isInstance(pClass, qClass)) {
return (Pair<P, Q>) pair;
}
throw new ClassCastException();
}
}
笔记,主要涉及Java和泛型的生疏:
a
和 b
都是不可变的。makePair
静态方法可以帮助你处理样板类型,而Java 7中的菱形运算符会使这一点更少烦人。在泛型方面有些工作需要做得更好(比如PECS),但现在应该还好。hashcode
和 equals
是由eclipse生成的。cast
方法中的编译时强制转换是可以的,但似乎不太对。isInstance
中的通配符是否必要。Pair
类的字段 a
和 b
更好的命名应该是 first
和 second
,因为这样可以清晰地表达这些字段的含义。另一方面,对于这样一个基本的类,有些人更喜欢使用较短的字段名 - 因此 a
和 b
也不是坏选择。 - ToolmakerSteve如果您调用的方法是私有的,或者只从一个位置调用,请尝试
return new Object[]{value1, value2};
调用者的样子如下:
Object[] temp=myMethod(parameters);
Type1 value1=(Type1)temp[0]; //For code clarity: temp[0] is not descriptive
Type2 value2=(Type2)temp[1];
David Hanak的Pair示例没有语法上的优势,且仅限于两个值。
return new Pair<Type1,Type2>(value1, value2);
调用者的样子如下:
Pair<Type1, Type2> temp=myMethod(parameters);
Type1 value1=temp.a; //For code clarity: temp.a is not descriptive
Type2 value2=temp.b;
Pair<T1, T2>
,Tuple<T1, T2, T3>
,Tuple<T1, T2, T3, T4>
等等。然后在具体使用时,展示参数的数量和类型,如Pair<int, String> temp = ...
或其他类似情况。 - ToolmakerSteve您可以使用以下任何一种方式:
private static final int RETURN_COUNT = 2;
private static final int VALUE_A = 0;
private static final int VALUE_B = 1;
private static final String A = "a";
private static final String B = "b";
1) 使用 Array
private static String[] methodWithArrayResult() {
//...
return new String[]{"valueA", "valueB"};
}
private static void usingArrayResultTest() {
String[] result = methodWithArrayResult();
System.out.println();
System.out.println("A = " + result[VALUE_A]);
System.out.println("B = " + result[VALUE_B]);
}
2) 使用 ArrayList
private static List<String> methodWithListResult() {
//...
return Arrays.asList("valueA", "valueB");
}
private static void usingListResultTest() {
List<String> result = methodWithListResult();
System.out.println();
System.out.println("A = " + result.get(VALUE_A));
System.out.println("B = " + result.get(VALUE_B));
}
3) 使用 HashMap
private static Map<String, String> methodWithMapResult() {
Map<String, String> result = new HashMap<>(RETURN_COUNT);
result.put(A, "valueA");
result.put(B, "valueB");
//...
return result;
}
private static void usingMapResultTest() {
Map<String, String> result = methodWithMapResult();
System.out.println();
System.out.println("A = " + result.get(A));
System.out.println("B = " + result.get(B));
}
4) 使用您的自定义容器类
private static class MyContainer<M,N> {
private final M first;
private final N second;
public MyContainer(M first, N second) {
this.first = first;
this.second = second;
}
public M getFirst() {
return first;
}
public N getSecond() {
return second;
}
// + hashcode, equals, toString if need
}
private static MyContainer<String, String> methodWithContainerResult() {
//...
return new MyContainer("valueA", "valueB");
}
private static void usingContainerResultTest() {
MyContainer<String, String> result = methodWithContainerResult();
System.out.println();
System.out.println("A = " + result.getFirst());
System.out.println("B = " + result.getSecond());
}
5) 使用AbstractMap.simpleEntry
private static AbstractMap.SimpleEntry<String, String> methodWithAbstractMapSimpleEntryResult() {
//...
return new AbstractMap.SimpleEntry<>("valueA", "valueB");
}
private static void usingAbstractMapSimpleResultTest() {
AbstractMap.SimpleEntry<String, String> result = methodWithAbstractMapSimpleEntryResult();
System.out.println();
System.out.println("A = " + result.getKey());
System.out.println("B = " + result.getValue());
}
6) 使用 Apache Commons 的 Pair
private static Pair<String, String> methodWithPairResult() {
//...
return new ImmutablePair<>("valueA", "valueB");
}
private static void usingPairResultTest() {
Pair<String, String> result = methodWithPairResult();
System.out.println();
System.out.println("A = " + result.getKey());
System.out.println("B = " + result.getValue());
}
在我编写Java代码时,我几乎总是会定义n元组类。例如:
public class Tuple2<T1,T2> {
private T1 f1;
private T2 f2;
public Tuple2(T1 f1, T2 f2) {
this.f1 = f1; this.f2 = f2;
}
public T1 getF1() {return f1;}
public T2 getF2() {return f2;}
}
我知道它有点丑陋,但它可以工作,并且您只需要定义一次元组类型。元组是Java缺乏的东西。
编辑:David Hanak的示例更加优雅,因为它避免了定义getter并仍然保持对象不可变。
Apache Commons提供了元组和三元组的实现:
ImmutablePair<L,R>
一个由两个对象元素组成的不可变的二元组。ImmutableTriple<L,M,R>
一个由三个对象元素组成的不可变的三元组。MutablePair<L,R>
一个由两个对象元素组成的可变的二元组。MutableTriple<L,M,R>
一个由三个对象元素组成的可变的三元组。Pair<L,R>
一个由两个元素组成的二元组。Triple<L,M,R>
一个由三个元素组成的三元组。在Java 5之前,我会有点同意Map解决方案并不理想。它不能在编译时进行类型检查,因此可能会在运行时出现问题。然而,在Java 5中,我们有了泛型。
所以你的方法可以这样写:
public Map<String, MyType> doStuff();
“MyType”指的是您返回的对象类型。
基本上,我认为在这种情况下返回Map是正确的解决方案,因为这正是您想要返回的 - 将字符串映射到对象。
另外,在需要从方法中返回多个内容时,有时我会使用回调机制而不是容器。在无法预先确定将生成多少对象的情况下,这种方法非常有效。
对于您的特定问题,它看起来像这样:
public class ResultsConsumer implements ResultsGenerator.ResultsCallback
{
public void handleResult( String name, Object value )
{
...
}
}
public class ResultsGenerator
{
public interface ResultsCallback
{
void handleResult( String aName, Object aValue );
}
public void generateResults( ResultsGenerator.ResultsCallback aCallback )
{
Object value = null;
String name = null;
...
aCallback.handleResult( name, value );
}
}
java
中的内存管理没有很好的理解,如果您有对象A
调用对象B.getResult()
并且B.getResult()
作为callback
调用A.finishResult()
,那么对象B
会被垃圾回收还是会一直存在直到A
完成?可能是一个愚蠢的问题,但这是我基本的困惑! - wired00关于一般情况下多返回值的问题,我通常使用一个小助手类来包装单个返回值,并将其作为参数传递给方法:
public class ReturnParameter<T> {
private T value;
public ReturnParameter() { this.value = null; }
public ReturnParameter(T initialValue) { this.value = initialValue; }
public void set(T value) { this.value = value; }
public T get() { return this.value; }
}
对于原始数据类型,我使用微小的变化直接存储值。
想要返回多个值的方法应该声明如下:
public void methodThatReturnsTwoValues(ReturnParameter<ClassA> nameForFirstValueToReturn, ReturnParameter<ClassB> nameForSecondValueToReturn) {
//...
nameForFirstValueToReturn.set("...");
nameForSecondValueToReturn.set("...");
//...
}
ReturnParameter<ClassA> nameForFirstValue = new ReturnParameter<ClassA>();
ReturnParameter<ClassB> nameForSecondValue = new ReturnParameter<ClassB>();
methodThatReturnsTwoValues(nameForFirstValue, nameForSecondValue);
优点(与其他解决方案相比):