我想从Java方法中返回两个对象,并想知道这样做的好方法是什么?
我能想到的可能方法是:返回HashMap
(因为两个对象相关)或返回ArrayList
个Object
个对象。
更确切地说,我想要返回的两个对象是(a)List
个对象和(b)逗号分隔的名称。
我想从一个方法返回这两个对象,因为我不想遍历对象列表以获取逗号分隔的名称(我可以在此方法的同一循环中执行此操作)。
不知何故,返回HashMap
并不是一种非常优雅的方式。
答案 0 :(得分:121)
如果要返回两个对象,通常需要返回一个封装两个对象的对象。
您可以返回NamedObject
这样的对象列表:
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
可能是您想要的。
答案 1 :(得分:63)
如果您知道要返回两个对象,也可以使用通用对:
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中的钻石操作员可以减少烦恼。有一些工作可以使这个非常好的re:泛型,但它现在应该是好的。 (参见PECS)hashcode
和equals
由eclipse生成。cast
方法中的编译时间正常,但似乎不太正确。isInstance
中的通配符是否必要。答案 2 :(得分:23)
如果您正在呼叫的方法是私有的,或从一个位置呼叫,请尝试
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;
答案 3 :(得分:16)
您可以使用以下任何一种方式:
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)使用数组
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
的配对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());
}
答案 4 :(得分:15)
当我在Java中编码时,我几乎总是最终定义n-Tuple类。例如:
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并仍然保持对象不可变。
答案 5 :(得分:9)
在Java 5之前,我有点同意Map解决方案并不理想。它不会为您提供编译时类型检查,因此可能会在运行时导致问题。但是,使用Java 5,我们有通用类型。
所以你的方法看起来像这样:
public Map<String, MyType> doStuff();
MyType当然是您要返回的对象类型。
基本上我认为在这种情况下返回Map是正确的解决方案,因为这正是您想要返回的 - 字符串到对象的映射。
答案 6 :(得分:8)
我们应该忘记小的效率,大约97%的时间说:过早的优化是所有邪恶的根源。
d。 Knuth的
答案 7 :(得分:6)
或者,在我想从方法中返回许多内容的情况下,我有时会使用回调机制而不是容器。这在我无法提前指定将生成多少对象的情况下非常有效。
对于您的特定问题,它看起来像这样:
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 );
}
}
答案 8 :(得分:5)
Apache Commons为此提供了元组和三元组:
ImmutablePair<L,R>
由两个Object组成的不可变对
元件。ImmutableTriple<L,M,R>
由...组成的不可变三元组
三个对象元素。MutablePair<L,R>
由...组成的可变对
两个对象元素。MutableTriple<L,M,R>
可变三联
由三个对象元素组成。Pair<L,R>
一对由...组成
两个要素。Triple<L,M,R>
由三个元素组成的三元组。答案 9 :(得分:5)
关于一般多个返回值的问题,我通常使用一个包装单个返回值的小助手类,并作为参数传递给方法:
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);
优点(与提出的其他解决方案相比):
答案 10 :(得分:4)
虽然在您的情况下,the comment可能是个不错的选择,但在Android中,您可以使用Pair
。简单地
list * add_student(slist * students, char * name, int id) {
slist * temp, * adder;
if (!(adder = (slist * )
malloc(sizeof(slist)))) exit(1);
student * data;
if (!(data = (student * )
malloc(sizeof(student)))) exit(1);
data - > name = name;
data - > id = id;
data - > courses = NULL;
adder - > info = data;
adder - > next = NULL;
if (!students) {
return adder;
}
for (temp = students; temp - > next; temp = temp - > next);
temp - > next = adder;
return students;
}
答案 11 :(得分:4)
使用以下Entry对象 示例:
public Entry<A,B> methodname(arg)
{
.......
return new AbstractMap.simpleEntry<A,B>(instanceOfA,instanceOfB);
}
答案 12 :(得分:3)
所有可能的解决方案都是kludge(就像容器对象,你的HashMap想法,“通过数组实现的多个返回值”)。我建议从返回的List中重新生成以逗号分隔的列表。代码最终将变得更加清洁。
答案 13 :(得分:2)
可以做一些像动态语言(Python)中的元组
public class Tuple {
private Object[] multiReturns;
private Tuple(Object... multiReturns) {
this.multiReturns = multiReturns;
}
public static Tuple _t(Object... multiReturns){
return new Tuple(multiReturns);
}
public <T> T at(int index, Class<T> someClass) {
return someClass.cast(multiReturns[index]);
}
}
并像这样使用
public Tuple returnMultiValues(){
return Tuple._t(new ArrayList(),new HashMap())
}
Tuple t = returnMultiValues();
ArrayList list = t.at(0,ArrayList.class);
答案 14 :(得分:2)
保持简单并为多种结果情境创建一个类。此示例接受来自databasehelper getInfo的ArrayList和消息文本。
您调用返回多个值的例程的代码:
ArrayList<String> list= new ArrayList<String>();
add values to the list...
return new multResult("the message", list);
在例程getInfo中编码:
public class multResult {
public String message; // or create a getter if you don't like public
public ArrayList<String> list;
multResult(String m, ArrayList<String> l){
message = m;
list= l;
}
并定义一个类multResult:
{{1}}
}
答案 15 :(得分:2)
我遵循了类似于其他答案中描述的方法,根据我的要求进行了一些调整,基本上我创建了以下类(以防万一,一切都是Java):
public class Pair<L, R> {
final L left;
final R right;
public Pair(L left, R right) {
this.left = left;
this.right = right;
}
public <T> T get(Class<T> param) {
return (T) (param == this.left.getClass() ? this.left : this.right);
}
public static <L, R> Pair<L, R> of(L left, R right) {
return new Pair<L, R>(left, right);
}
}
然后,我的要求很简单,在到达数据库的存储库类中,对于Get Methods而不是从数据库中检索数据,我需要检查它是否失败或成功,然后,如果成功,我需要玩返回列表,如果失败,则停止执行并通知错误。
因此,例如,我的方法是这样的:
public Pair<ResultMessage, List<Customer>> getCustomers() {
List<Customer> list = new ArrayList<Customer>();
try {
/*
* Do some work to get the list of Customers from the DB
* */
} catch (SQLException e) {
return Pair.of(
new ResultMessage(e.getErrorCode(), e.getMessage()), // Left
null); // Right
}
return Pair.of(
new ResultMessage(0, "SUCCESS"), // Left
list); // Right
}
其中ResultMessage只是一个包含两个字段(代码/消息)的类,而Customer是包含来自数据库的一堆字段的任何类。
然后,检查结果我就这样做了:
void doSomething(){
Pair<ResultMessage, List<Customer>> customerResult = _repository.getCustomers();
if (customerResult.get(ResultMessage.class).getCode() == 0) {
List<Customer> listOfCustomers = customerResult.get(List.class);
System.out.println("do SOMETHING with the list ;) ");
}else {
System.out.println("Raised Error... do nothing!");
}
}
答案 16 :(得分:2)
正如我所看到的,这里有三个选择,解决方案取决于上下文。您可以选择在生成列表的方法中实现名称的构造。这是你选择的选择,但我不认为这是最好的选择。您正在生成器方法中创建一个不需要存在的消费方法的耦合。其他呼叫者可能不需要额外的信息,您将为这些呼叫者计算额外信息。
或者,您可以让调用方法计算名称。如果只有一个呼叫者需要此信息,您可以在那里停止。您没有额外的依赖关系,虽然涉及一些额外的计算,但您已避免使您的构造方法过于具体。这是一个很好的权衡。
最后,您可以让列表本身负责创建名称。如果计算需要由多个调用者完成,这就是我要走的路线。我认为这使得创建名称的责任在于与对象本身最密切相关的类。
在后一种情况下,我的解决方案是创建一个专门的List类,它返回一个逗号分隔的字符串,包含它包含的对象的名称。使类足够智能,以便在添加和删除对象时动态构造名称字符串。然后返回此列表的实例并根据需要调用名称生成方法。虽然在第一次调用方法之前简单地延迟计算名称可能几乎同样有效(并且更简单),然后将其存储(延迟加载)。如果添加/删除对象,则只需删除计算出的值,并在下次调用时重新计算。
答案 17 :(得分:1)
public class MultipleReturnValues {
public MultipleReturnValues() {
}
public static void functionWithSeveralReturnValues(final String[] returnValues) {
returnValues[0] = "return value 1";
returnValues[1] = "return value 2";
}
public static void main(String[] args) {
String[] returnValues = new String[2];
functionWithSeveralReturnValues(returnValues);
System.out.println("returnValues[0] = " + returnValues[0]);
System.out.println("returnValues[1] = " + returnValues[1]);
}
}
答案 18 :(得分:1)
为什么不创建一个包含结果的WhateverFunctionResult
对象,和解析这些结果所需的逻辑,迭代等等。在我看来要么:
我看到这种问题一再出现。不要害怕创建自己的容器/结果类,其中包含数据和相关的功能来处理这个问题。如果您只是以HashMap
或类似方式传递内容,那么您的客户必须将此地图分开并在每次想要使用结果时浏览内容。
答案 19 :(得分:1)
在C ++(STL)中,有一个用于捆绑两个对象的对类。在Java Generics中,虽然有一些demand,但它不可用。你可以自己轻松实现它。
我同意其他一些答案,如果你需要从方法中返回两个或多个对象,最好将它们封装在一个类中。
答案 20 :(得分:1)
这并不能完全回答这个问题,但由于这里给出的每个解决方案都有一些缺点,我建议尝试重构一下你的代码,这样你只需要返回一个值。
案例一。
您需要在方法内外使用某些内容。为什么不在外面计算它并将其传递给方法?
而不是:
[thingA, thingB] = createThings(...); // just a conceptual syntax of method returning two values, not valid in Java
尝试:
thingA = createThingA(...);
thingB = createThingB(thingA, ...);
这应该涵盖您的大多数需求,因为在大多数情况下,一个值在另一个之前创建,您可以使用两种方法拆分创建它们。缺点是方法createThingsB
具有与createThings
相比的额外参数,并且可能您将两个参数列表完全相同地传递给不同的方法。
案例二。
最明显的解决方案和案例一的简化版本。它并不总是可能的,但也许两个值可以彼此独立地创建?
而不是:
[thingA, thingB] = createThings(...); // see above
尝试:
thingA = createThingA(...);
thingB = createThingB(...);
为了使它更有用,这两种方法可以共享一些共同的逻辑:
public ThingA createThingA(...) {
doCommonThings(); // common logic
// create thing A
}
public ThingB createThingB(...) {
doCommonThings(); // common logic
// create thing B
}
答案 21 :(得分:0)
您可以如下使用HashMap<String, Object>
public HashMap<String, Object> yourMethod()
{
.... different logic here
HashMap<String, Object> returnHashMap = new HashMap<String, Object>();
returnHashMap.put("objectA", objectAValue);
returnHashMap.put("myString", myStringValue);
returnHashMap.put("myBoolean", myBooleanValue);
return returnHashMap;
}
然后在不同范围内调用该方法时,可以将每个对象强制转换回其初始类型:
// call the method
HashMap<String, Object> resultMap = yourMethod();
// fetch the results and cast them
ObjectA objectA = (ObjectA) resultMap.get("objectA");
String myString = (String) resultMap.get("myString");
Boolean myBoolean = (Boolean) resultMap.get("myBoolean");
答案 22 :(得分:0)
我注意到还没有返回多个值的非自定义类、n 长度、非强制转换、类型安全的答案。
这是我的目标:
import java.util.Objects;
public final class NTuple<V, T extends NTuple<?, ?>> {
private final V value;
private final T next;
private NTuple(V value, T next) {
this.value = value;
this.next = next;
}
public static <V> NTuple<V, ?> of(V value) {
return new NTuple<>(value, null);
}
public static <V, T extends NTuple<?, ?>> NTuple<V, T> of(V value, T next) {
return new NTuple<>(value, next);
}
public V value() {
return value;
}
public T next() {
return next;
}
public static <V> V unpack0(NTuple<V, ?> tuple) {
return Objects.requireNonNull(tuple, "0").value();
}
public static <V, T extends NTuple<V, ?>> V unpack1(NTuple<?, T> tuple) {
NTuple<?, T> tuple0 = Objects.requireNonNull(tuple, "0");
NTuple<V, ?> tuple1 = Objects.requireNonNull(tuple0.next(), "1");
return tuple1.value();
}
public static <V, T extends NTuple<?, NTuple<V, ?>>> V unpack2(NTuple<?, T> tuple) {
NTuple<?, T> tuple0 = Objects.requireNonNull(tuple, "0");
NTuple<?, NTuple<V, ?>> tuple1 = Objects.requireNonNull(tuple0.next(), "1");
NTuple<V, ?> tuple2 = Objects.requireNonNull(tuple1.next(), "2");
return tuple2.value();
}
}
示例使用:
public static void main(String[] args) {
// pre-java 10 without lombok - use lombok's var or java 10's var if you can
NTuple<String, NTuple<Integer, NTuple<Integer, ?>>> multiple = wordCount("hello world");
String original = NTuple.unpack0(multiple);
Integer wordCount = NTuple.unpack1(multiple);
Integer characterCount = NTuple.unpack2(multiple);
System.out.println(original + ": " + wordCount + " words " + characterCount + " chars");
}
private static NTuple<String, NTuple<Integer, NTuple<Integer, ?>>> wordCount(String s) {
int nWords = s.split(" ").length;
int nChars = s.length();
return NTuple.of(s, NTuple.of(nWords, NTuple.of(nChars)));
}
优点:
缺点:
var
答案 23 :(得分:0)
将一个列表传递给您的方法并填充它,然后返回带有名称的String,如下所示:
public String buildList(List<?> list) {
list.add(1);
list.add(2);
list.add(3);
return "something,something,something,dark side";
}
然后这样称呼:
List<?> values = new ArrayList<?>();
String names = buildList(values);
答案 24 :(得分:-2)
我一直在使用一种非常基本的方法来处理多重回报的问题。它有助于实现目的,并避免复杂性。
我称之为字符串分隔符方法
它有效,因为它甚至可以返回多种类型的值,例如int,double,char,string等
在这种方法中,我们使用一般不太可能发生的字符串。 我们将其称为分隔符。 当在函数
中使用时,此分隔符将用于分隔各种值例如,我们将最终返回为(例如)intValue separator doubleValue separator ... 然后使用这个字符串,我们将检索所需的所有信息,也可以是不同的类型
以下代码将显示此概念的工作原理
使用的分隔符是!@#和 返回3个值intVal,doubleVal和stringVal
public class TestMultipleReturns {
public static String multipleVals() {
String result = "";
String separator = "!@#";
int intVal = 5;
// Code to process intVal
double doubleVal = 3.14;
// Code to process doubleVal
String stringVal = "hello";
// Code to process Int intVal
result = intVal + separator + doubleVal + separator + stringVal + separator;
return (result);
}
public static void main(String[] args) {
String res = multipleVals();
int intVal = Integer.parseInt(res.split("!@#")[0]);
// Code to process intVal
double doubleVal = Double.parseDouble(res.split("!@#")[1]);
// Code to process doubleVal
String stringVal = res.split("!@#")[2];
System.out.println(intVal+"\n"+doubleVal+"\n"+stringVal);
}
}
输出
5
3.14
hello
BUILD SUCCESSFUL (total time: 2 seconds)
答案 25 :(得分:-4)
在C中,您可以通过将指针作为参数传递给占位符来实现:
void getShoeAndWaistSizes(int *shoeSize, int *waistSize) {
*shoeSize = 36;
*waistSize = 45;
}
...
int shoeSize, waistSize;
getShoeAndWaistSize(&shoeSize, &waistSize);
int i = shoeSize + waistSize;
让我们尝试类似的东西,用Java。
void getShoeAndWaistSizes(List<Integer> shoeSize, List<Integer> waistSize) {
shoeSize.add(36);
waistSize.add(45);
}
...
List<Integer> shoeSize = new List<>();
List<Integer> waistSize = new List<>();
getShoeAndWaistSizes(shoeSize, waistSize);
int i = shoeSize.get(0) + waistSize.get(0);
答案 26 :(得分:-5)
通过这种方法并将其推广......
public void buildResponse(String data,Map response);