Java中C++对<L, R>的等价物是什么?

Java中没有Pair<L,R>有什么好的理由吗?这个C++结构的等价物是什么?我宁愿避免重新实现我自己的。

似乎1.6提供了类似的东西(AbstractMap.SimpleEntry<K,V>),但这看起来相当复杂。

497416 次浏览

这取决于你想用它做什么。这样做的典型原因是迭代映射,为此你只需这样做(Java5+):

Map<String, Object> map = ... ; // just an example
for (Map.Entry<String, Object> entry : map.entrySet()) {
System.out.printf("%s -> %s\n", entry.getKey(), entry.getValue());
}

comp.lang.java.help上的一个线程中,Hunter Gratzner给出了一些反对Java中存在Pair构造的论点。主要论点是类Pair没有传达关于两个值之间关系的任何语义学(你怎么知道“first”和“第二”是什么意思?)。

一个更好的做法是为您将使用Pair类制作的每个应用程序编写一个非常简单的类,就像Mike提议的那样。Map.Entry是名称中带有其含义的一对的示例。

总之,在我看来,最好有一个类Position(x,y),一个类Range(begin,end)和一个类Entry(key,value),而不是一个通用的Pair(first,second),它没有告诉我它应该做什么。

HashMap兼容的Pair类:

public class Pair<A, B> {
private A first;
private B second;


public Pair(A first, B second) {
super();
this.first = first;
this.second = second;
}


public int hashCode() {
int hashFirst = first != null ? first.hashCode() : 0;
int hashSecond = second != null ? second.hashCode() : 0;


return (hashFirst + hashSecond) * hashSecond + hashFirst;
}


public boolean equals(Object other) {
if (other instanceof Pair) {
Pair otherPair = (Pair) other;
return
((  this.first == otherPair.first ||
( this.first != null && otherPair.first != null &&
this.first.equals(otherPair.first))) &&
(  this.second == otherPair.second ||
( this.second != null && otherPair.second != null &&
this.second.equals(otherPair.second))) );
}


return false;
}


public String toString()
{
return "(" + first + ", " + second + ")";
}


public A getFirst() {
return first;
}


public void setFirst(A first) {
this.first = first;
}


public B getSecond() {
return second;
}


public void setSecond(B second) {
this.second = second;
}
}

Pair将是一个很好的东西,作为复杂泛型的基本构造单元,例如,这来自我的代码:

WeakHashMap<Pair<String, String>, String> map = ...

它与Haskell的元组相同

这是Java。你必须使用描述性的类和字段名称来制作自己定制的Pair类,不要介意你会通过编写hashCode()/equals()或一次又一次地实现比较来重新发明轮子。

另一种实现Pair with的方法。

  • 公共不可变字段,即简单数据结构。
  • 可比。
  • 简单的哈希和等于。
  • 简单的工厂,所以你不必提供类型。例如Pair.of(“你好”,1);

    public class Pair<FIRST, SECOND> implements Comparable<Pair<FIRST, SECOND>> {
    
    
    public final FIRST first;
    public final SECOND second;
    
    
    private Pair(FIRST first, SECOND second) {
    this.first = first;
    this.second = second;
    }
    
    
    public static <FIRST, SECOND> Pair<FIRST, SECOND> of(FIRST first,
    SECOND second) {
    return new Pair<FIRST, SECOND>(first, second);
    }
    
    
    @Override
    public int compareTo(Pair<FIRST, SECOND> o) {
    int cmp = compare(first, o.first);
    return cmp == 0 ? compare(second, o.second) : cmp;
    }
    
    
    // todo move this to a helper class.
    private static int compare(Object o1, Object o2) {
    return o1 == null ? o2 == null ? 0 : -1 : o2 == null ? +1
    : ((Comparable) o1).compareTo(o2);
    }
    
    
    @Override
    public int hashCode() {
    return 31 * hashcode(first) + hashcode(second);
    }
    
    
    // todo move this to a helper class.
    private static int hashcode(Object o) {
    return o == null ? 0 : o.hashCode();
    }
    
    
    @Override
    public boolean equals(Object obj) {
    if (!(obj instanceof Pair))
    return false;
    if (this == obj)
    return true;
    return equal(first, ((Pair) obj).first)
    && equal(second, ((Pair) obj).second);
    }
    
    
    // todo move this to a helper class.
    private boolean equal(Object o1, Object o2) {
    return o1 == null ? o2 == null : (o1 == o2 || o1.equals(o2));
    }
    
    
    @Override
    public String toString() {
    return "(" + first + ", " + second + ')';
    }
    }
    

简单的方法Object[]-可以用作一个元组

在我看来,Java中没有Pair,因为如果你想直接在对上添加额外的功能(例如比较),你必须绑定类型。C++,我们不在乎,如果组成一对的类型没有operator <pair::operator <也不会编译。

一个没有边界的可比较的例子:

public class Pair<F, S> implements Comparable<Pair<? extends F, ? extends S>> {
public final F first;
public final S second;
/* ... */
public int compareTo(Pair<? extends F, ? extends S> that) {
int cf = compare(first, that.first);
return cf == 0 ? compare(second, that.second) : cf;
}
//Why null is decided to be less than everything?
private static int compare(Object l, Object r) {
if (l == null) {
return r == null ? 0 : -1;
} else {
return r == null ? 1 : ((Comparable) (l)).compareTo(r);
}
}
}


/* ... */


Pair<Thread, HashMap<String, Integer>> a = /* ... */;
Pair<Thread, HashMap<String, Integer>> b = /* ... */;
//Runtime error here instead of compile error!
System.out.println(a.compareTo(b));

一个与编译时检查类型参数是否可比的可比较示例:

public class Pair<
F extends Comparable<? super F>,
S extends Comparable<? super S>
> implements Comparable<Pair<? extends F, ? extends S>> {
public final F first;
public final S second;
/* ... */
public int compareTo(Pair<? extends F, ? extends S> that) {
int cf = compare(first, that.first);
return cf == 0 ? compare(second, that.second) : cf;
}
//Why null is decided to be less than everything?
private static <
T extends Comparable<? super T>
> int compare(T l, T r) {
if (l == null) {
return r == null ? 0 : -1;
} else {
return r == null ? 1 : l.compareTo(r);
}
}
}


/* ... */


//Will not compile because Thread is not Comparable<? super Thread>
Pair<Thread, HashMap<String, Integer>> a = /* ... */;
Pair<Thread, HashMap<String, Integer>> b = /* ... */;
System.out.println(a.compareTo(b));

这很好,但是这次你不能在Pair中使用不可比较的类型作为类型参数。 在一些实用类中,可能会使用很多比较器,但是C++人可能不明白。另一种方法是在类型层次结构中编写大量类,在类型参数上具有不同的边界,但是可能的边界及其组合太多了…

我能想到的最短的一对是以下,使用龙目岛

@Data
@AllArgsConstructor(staticName = "of")
public class Pair<F, S> {
private F first;
private S second;
}

它具有来自@arturh的答案的所有优点(除了可比性),它具有hashCodeequalstoString和静态“构造函数”。

正如许多其他人已经指出的那样,它实际上取决于用例Pair类是否有用。

我认为对于一个私有辅助函数来说,使用Pair类是完全合法的,如果这使您的代码更具可读性,并且不值得使用其所有样板代码创建另一个值类。

另一方面,如果您的抽象级别要求您清楚地记录包含两个对象或值的类的语义学,那么您应该为它编写一个类。如果数据是业务对象,通常就是这种情况。

一如既往,它需要熟练的判断。

对于你的第二个问题,我推荐Apache Commons库中的Pair类。这些可能被视为Java的扩展标准库:

您可能还想看看Apache Commons的EqualsBuilderHashCodeBuilder创建工具ToStringBuilder,它们简化了为您的业务对象编写值类。

我注意到所有的Pair实现都散落在这里,将意义属性为两个值的顺序。当我想到一对时,我想到的是两个项目的组合,其中两个的顺序并不重要。这是我对无序对的实现,hashCodeequals覆盖以确保集合中所需的行为。也可克隆。

/**
* The class <code>Pair</code> models a container for two objects wherein the
* object order is of no consequence for equality and hashing. An example of
* using Pair would be as the return type for a method that needs to return two
* related objects. Another good use is as entries in a Set or keys in a Map
* when only the unordered combination of two objects is of interest.<p>
* The term "object" as being a one of a Pair can be loosely interpreted. A
* Pair may have one or two <code>null</code> entries as values. Both values
* may also be the same object.<p>
* Mind that the order of the type parameters T and U is of no importance. A
* Pair&lt;T, U> can still return <code>true</code> for method <code>equals</code>
* called with a Pair&lt;U, T> argument.<p>
* Instances of this class are immutable, but the provided values might not be.
* This means the consistency of equality checks and the hash code is only as
* strong as that of the value types.<p>
*/
public class Pair<T, U> implements Cloneable {


/**
* One of the two values, for the declared type T.
*/
private final T object1;
/**
* One of the two values, for the declared type U.
*/
private final U object2;
private final boolean object1Null;
private final boolean object2Null;
private final boolean dualNull;


/**
* Constructs a new <code>Pair&lt;T, U&gt;</code> with T object1 and U object2 as
* its values. The order of the arguments is of no consequence. One or both of
* the values may be <code>null</code> and both values may be the same object.
*
* @param object1 T to serve as one value.
* @param object2 U to serve as the other value.
*/
public Pair(T object1, U object2) {


this.object1 = object1;
this.object2 = object2;
object1Null = object1 == null;
object2Null = object2 == null;
dualNull = object1Null && object2Null;


}


/**
* Gets the value of this Pair provided as the first argument in the constructor.
*
* @return a value of this Pair.
*/
public T getObject1() {


return object1;


}


/**
* Gets the value of this Pair provided as the second argument in the constructor.
*
* @return a value of this Pair.
*/
public U getObject2() {


return object2;


}


/**
* Returns a shallow copy of this Pair. The returned Pair is a new instance
* created with the same values as this Pair. The values themselves are not
* cloned.
*
* @return a clone of this Pair.
*/
@Override
public Pair<T, U> clone() {


return new Pair<T, U>(object1, object2);


}


/**
* Indicates whether some other object is "equal" to this one.
* This Pair is considered equal to the object if and only if
* <ul>
* <li>the Object argument is not null,
* <li>the Object argument has a runtime type Pair or a subclass,
* </ul>
* AND
* <ul>
* <li>the Object argument refers to this pair
* <li>OR this pair's values are both null and the other pair's values are both null
* <li>OR this pair has one null value and the other pair has one null value and
* the remaining non-null values of both pairs are equal
* <li>OR both pairs have no null values and have value tuples &lt;v1, v2> of
* this pair and &lt;o1, o2> of the other pair so that at least one of the
* following statements is true:
* <ul>
* <li>v1 equals o1 and v2 equals o2
* <li>v1 equals o2 and v2 equals o1
* </ul>
* </ul>
* In any other case (such as when this pair has two null parts but the other
* only one) this method returns false.<p>
* The type parameters that were used for the other pair are of no importance.
* A Pair&lt;T, U> can return <code>true</code> for equality testing with
* a Pair&lt;T, V> even if V is neither a super- nor subtype of U, should
* the the value equality checks be positive or the U and V type values
* are both <code>null</code>. Type erasure for parameter types at compile
* time means that type checks are delegated to calls of the <code>equals</code>
* methods on the values themselves.
*
* @param obj the reference object with which to compare.
* @return true if the object is a Pair equal to this one.
*/
@Override
public boolean equals(Object obj) {


if(obj == null)
return false;


if(this == obj)
return true;


if(!(obj instanceof Pair<?, ?>))
return false;


final Pair<?, ?> otherPair = (Pair<?, ?>)obj;


if(dualNull)
return otherPair.dualNull;


//After this we're sure at least one part in this is not null


if(otherPair.dualNull)
return false;


//After this we're sure at least one part in obj is not null


if(object1Null) {
if(otherPair.object1Null) //Yes: this and other both have non-null part2
return object2.equals(otherPair.object2);
else if(otherPair.object2Null) //Yes: this has non-null part2, other has non-null part1
return object2.equals(otherPair.object1);
else //Remaining case: other has no non-null parts
return false;
} else if(object2Null) {
if(otherPair.object2Null) //Yes: this and other both have non-null part1
return object1.equals(otherPair.object1);
else if(otherPair.object1Null) //Yes: this has non-null part1, other has non-null part2
return object1.equals(otherPair.object2);
else //Remaining case: other has no non-null parts
return false;
} else {
//Transitive and symmetric requirements of equals will make sure
//checking the following cases are sufficient
if(object1.equals(otherPair.object1))
return object2.equals(otherPair.object2);
else if(object1.equals(otherPair.object2))
return object2.equals(otherPair.object1);
else
return false;
}


}


/**
* Returns a hash code value for the pair. This is calculated as the sum
* of the hash codes for the two values, wherein a value that is <code>null</code>
* contributes 0 to the sum. This implementation adheres to the contract for
* <code>hashCode()</code> as specified for <code>Object()</code>. The returned
* value hash code consistently remain the same for multiple invocations
* during an execution of a Java application, unless at least one of the pair
* values has its hash code changed. That would imply information used for
* equals in the changed value(s) has also changed, which would carry that
* change onto this class' <code>equals</code> implementation.
*
* @return a hash code for this Pair.
*/
@Override
public int hashCode() {


int hashCode = object1Null ? 0 : object1.hashCode();
hashCode += (object2Null ? 0 : object2.hashCode());
return hashCode;


}


}

此实现已经过适当的单元测试,并且已经尝试在Set和Map中使用。

请注意,我并没有声称要在公共领域发布这个。这是我刚刚编写的用于应用程序的代码,所以如果你要使用它,请避免直接复制,并在注释和名称上乱七八糟。明白我的意思吗?

public class Pair<K, V> {


private final K element0;
private final V element1;


public static <K, V> Pair<K, V> createPair(K key, V value) {
return new Pair<K, V>(key, value);
}


public Pair(K element0, V element1) {
this.element0 = element0;
this.element1 = element1;
}


public K getElement0() {
return element0;
}


public V getElement1() {
return element1;
}


}

用法:

Pair<Integer, String> pair = Pair.createPair(1, "test");
pair.getElement0();
pair.getElement1();

一成不变,只有一对!

如果有人想要一个非常简单易用的版本,我在https://github.com/lfac-pt/Java-Pair上提供了我的版本。此外,非常欢迎改进!

http://www.javatuples.org/index.html怎么样?我发现它非常有用。

javatuples为您提供从一到十个元素的元组类:

Unit<A> (1 element)
Pair<A,B> (2 elements)
Triplet<A,B,C> (3 elements)
Quartet<A,B,C,D> (4 elements)
Quintet<A,B,C,D,E> (5 elements)
Sextet<A,B,C,D,E,F> (6 elements)
Septet<A,B,C,D,E,F,G> (7 elements)
Octet<A,B,C,D,E,F,G,H> (8 elements)
Ennead<A,B,C,D,E,F,G,H,I> (9 elements)
Decade<A,B,C,D,E,F,G,H,I,J> (10 elements)

根据Java语言的性质,我想人们实际上并不需要Pair,他们通常需要一个接口。下面是一个例子:

interface Pair<L, R> {
public L getL();
public R getR();
}

因此,当人们想要返回两个值时,他们可以执行以下操作:

... //Calcuate the return value
final Integer v1 = result1;
final String v2 = result2;
return new Pair<Integer, String>(){
Integer getL(){ return v1; }
String getR(){ return v2; }
}

这是一个非常轻量级的解决方案,它回答了“Pair<L,R>的语义是什么?”这个问题。答案是,这是一个包含两种(可能是不同的)类型的接口构建,它有方法来返回每种类型。你可以自己为其添加进一步的语义。例如,如果你使用位置并且真的想在代码中指示它,你可以定义包含IntegerPositionXPositionY来组成Pair<PositionX,PositionY>。如果JSR 308可用,你也可以使用Pair<@PositionX Integer, @PositionY Ingeger>来简化它。

编辑: 我应该在这里指出的一件事是,上面的定义显式地关联了类型参数名称和方法名称。这是对那些认为Pair缺乏语义信息的回答。实际上,方法getL意味着“给我对应于类型参数L类型的元素”,这确实意味着什么。

编辑: 这里有一个简单的实用工具类,可以让生活更轻松:

class Pairs {
static <L,R> Pair<L,R> makePair(final L l, final R r){
return new Pair<L,R>(){
public L getL() { return l; }
public R getR() { return r; }
};
}
}

用法:

return Pairs.makePair(new Integer(100), "123");

最大的问题可能是不能确保A和B的不变性(参见如何确保类型参数是不可变的),因此hashCode()可能会给出不一致的结果,例如在集合中插入相同的Pair之后(这会给出未定义的行为,参见根据可变字段定义等于)。对于特定的(非泛型)Pair类,程序员可以通过仔细选择A和B是不可变的来确保不变性。

无论如何,从@PeterLawrey的回答(java 1.7)中清除泛型的警告:

public class Pair<A extends Comparable<? super A>,
B extends Comparable<? super B>>
implements Comparable<Pair<A, B>> {


public final A first;
public final B second;


private Pair(A first, B second) {
this.first = first;
this.second = second;
}


public static <A extends Comparable<? super A>,
B extends Comparable<? super B>>
Pair<A, B> of(A first, B second) {
return new Pair<A, B>(first, second);
}


@Override
public int compareTo(Pair<A, B> o) {
int cmp = o == null ? 1 : (this.first).compareTo(o.first);
return cmp == 0 ? (this.second).compareTo(o.second) : cmp;
}


@Override
public int hashCode() {
return 31 * hashcode(first) + hashcode(second);
}


// TODO : move this to a helper class.
private static int hashcode(Object o) {
return o == null ? 0 : o.hashCode();
}


@Override
public boolean equals(Object obj) {
if (!(obj instanceof Pair))
return false;
if (this == obj)
return true;
return equal(first, ((Pair<?, ?>) obj).first)
&& equal(second, ((Pair<?, ?>) obj).second);
}


// TODO : move this to a helper class.
private boolean equal(Object o1, Object o2) {
return o1 == o2 || (o1 != null && o1.equals(o2));
}


@Override
public String toString() {
return "(" + first + ", " + second + ')';
}
}

补充/更正非常欢迎:)特别是我不太确定我使用Pair<?, ?>

有关此语法的更多信息,请参阅确保对象实现可比和详细解释如何在Java中实现泛型max(Comparable a, Comparable b)函数?

许多人正在发布Pair代码,这些代码可以用作Map中的键……如果您尝试使用一对作为散列键(一种常见的习语),请务必查看Guava的Table<R,C,V>http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Table。他们给出了以下图形边的示例用法:

Table<Vertex, Vertex, Double> weightedGraph = HashBasedTable.create();
weightedGraph.put(v1, v2, 4);
weightedGraph.put(v1, v3, 20);
weightedGraph.put(v2, v3, 5);


weightedGraph.row(v1); // returns a Map mapping v2 to 4, v3 to 20
weightedGraph.column(v3); // returns a Map mapping v1 to 20, v2 to 5

Table将两个键映射到一个值,并单独为这两种类型的键提供有效的查找。我已经开始在代码的许多部分中使用这种数据结构而不是Map<Pair<K1,K2>, V>。有数组、树和其他用于密集和稀疏用途的实现,可以选择指定自己的中间映射类。

对于像Java这样的编程语言,大多数程序员用来表示成对数据结构的替代数据结构是两个数组,数据通过相同的索引访问

示例:http://www-igm.univ-mlv.fr/~lecroq/string/node8.html#SECTION0080

这并不理想,因为数据应该绑定在一起,但也变得非常便宜。此外,如果您的用例需要存储坐标,那么最好构建您自己的数据结构。

我的书房里有这样的东西

public class Pair<First,Second>{.. }

尽管句法上相似,Java和C++却有截然不同的范式。写C++Java是坏C++,写JavaC++是坏Java。

使用像Eclipse这样基于反射的IDE,编写“对”类的必要功能既快速又简单。创建类,定义两个字段,使用各种“生成XX”菜单选项在几秒钟内填写类。如果你想要比较接口,也许你必须快速输入“comareTo”。

在语言中使用单独的声明/定义选项C++代码生成器不是那么好,所以手写小实用程序类更耗时乏味。因为这对是一个模板,你不必为不使用的函数付费,而且typedef工具允许为代码分配有意义的类型名,所以关于“没有语义学”的反对意见并不真正成立。

Android提供了Pairclass(http://developer.android.com/reference/android/util/Pair.html),这里实现:

public class Pair<F, S> {
public final F first;
public final S second;


public Pair(F first, S second) {
this.first = first;
this.second = second;
}


@Override
public boolean equals(Object o) {
if (!(o instanceof Pair)) {
return false;
}
Pair<?, ?> p = (Pair<?, ?>) o;
return Objects.equal(p.first, first) && Objects.equal(p.second, second);
}


@Override
public int hashCode() {
return (first == null ? 0 : first.hashCode()) ^ (second == null ? 0 : second.hashCode());
}


public static <A, B> Pair <A, B> create(A a, B b) {
return new Pair<A, B>(a, b);
}
}

com.sun.tools.javac.util.对是对的简单实现。 它可以在jdk1.7中找到。0_51\lib\tools.jar.

除了org.apache.commons.lang3.tuple.对,它不仅仅是一个界面。

您可以使用Google的AutoValue库-https://github.com/google/auto/tree/master/value

您创建一个非常小的抽象类并使用@AutoValue对其进行注释,注释处理器为您生成一个具有值语义的具体类。

JavaFX(与Java8捆绑在一起)具有Pair

地图入口的接口非常接近c++对。看看具体的实现,比如抽象地图。简单入口和AbstractMap。 第一项是getKey(),第二项是getValue()。

以下是一些具有多级元组的库,以方便您使用:

  • JavaTuples。1-10度的元组就是它所拥有的全部。
  • JavaSlang。0-8度的元组和许多其他功能好东西。
  • jOλ。0-16度的元组和其他一些功能好东西。(免责声明,我为维护者公司工作)
  • 功能Java。0-8度的元组和许多其他功能好东西。

其他库已经提到至少包含Pair元组。

具体来说,在使用大量结构类型而不是名义类型(正如公认的答案所主张的那样)的函数式编程的上下文中,这些库及其元组非常方便。

好消息JavaFX有一个键值Pair。

只需添加JavaFX作为依赖项并导入javafx.util.Pair,然后像C++一样简单地使用。

Pair <Key, Value>

e. g.

Pair <Integer, Integer> pr = new Pair<Integer, Integer>()


pr.get(key);// will return corresponding value

使用新版本的龙目岛,您可以编译那个可爱的类:

@Value(staticConstructor = "of") public class Pair <E> {
E first, second;
}

并像这样使用它:Pair<Value> pairOfValues = Pair.of(value1, value2);

Collections.singletonMap(left, rigth);

Brian Goetz,Paul Sandoz和Stuart Marks解释为什么在Devoxx'14的QA会议期间。

一旦引入值类型,标准库中的泛型对类将变成技术债务。

另见:JavaSE 8有对还是元组?

您可以使用javafx实用程序类Pair,其用途与c++中的对<>相同。https://docs.oracle.com/javafx/2/api/javafx/util/Pair.html

@Andreas Krey的回答实际上是好的。任何Java困难的事情,你可能都不应该做。

在我的经验中,Pair最常见的用途是来自方法的多个返回值和哈希图中的VALUES(通常由字符串索引)。

在后一种情况下,我最近使用了一个数据结构,如下所示:

class SumHolder{MyObject trackedObject, double sum};

这是你的整个“Pair”类,与泛型“Pair”的代码量几乎相同,但具有描述性名称的优势。它可以在它使用的方法中内联定义,这将消除公共变量等的典型问题。换句话说,就这种用法而言,它绝对比一对更好(由于命名成员),也不会更差。

如果你真的想要一个hashmap的键的“Pair”,你本质上是在创建一个双键索引。我认为这可能是“Pair”代码明显减少的一种情况。这并不容易,因为你可以让eclipse在你的小数据类上生成equals/hash,但这会是更多的代码。这里的Pair将是一个快速的解决方案,但是如果你需要一个双索引哈希,谁能说你不需要一个n索引哈希呢?数据类解决方案会扩大,除非你嵌套它们,否则Pair不会!

所以第二种情况,从方法返回,有点困难。你的类需要更多的可见性(调用者也需要看到它)。你可以在方法之外定义它,但在类内部也可以像上面一样定义它。此时你的方法应该能够返回一个MyClass. SumHolder对象。调用者可以看到返回对象的名称,而不仅仅是一个“Pair”。再次注意包级别的“Default”安全性非常好——它的限制足够严格,你不应该给自己带来太多麻烦。无论如何都比“Pair”对象好。

我可以看到Pair的另一种用途是一个公共api,它为当前包之外的调用者提供返回值。为此,我只是创建了一个真正的对象——最好是不可变的。最终调用者将共享这个返回值,让它可变可能会有问题。这是Pair对象更糟糕的另一种情况——大多数对不能成为不可变的。

所有这些情况的另一个优点-java类扩展,我的sum类需要第二个sum和一个“Created”标志,当我完成时,我将不得不扔掉Pair并使用其他东西,但如果这对有意义,我的类具有4个值仍然至少有同样的意义。

另一个简洁的龙目岛实现

import lombok.Value;


@Value(staticConstructor = "of")
public class Pair<F, S> {
private final F first;
private final S second;
}

尝试VAVR元组

vavr不仅有一组很好的元组类型,它对函数式编程也有很好的支持。

Spring数据有一对,可以像下面这样使用,

Pair<S, T> pair = Pair.of(S type data, T type data)

虽然这个问题已经有十多年的历史了,但我确实觉得有必要提到,截至Java14,记录可以为这个问题提供一个非常简单和轻量级的解决方案,而不需要任何形式的外部库或依赖项。

例如,以下记录类声明将是实现所需功能所需的全部内容:

record Pair<K, V>(K key, V value) { }

这样的记录类可以这样使用:

// Declare a pair object containing two integers
var integerIntegerPair = new Pair<>(1, 2);


// Declare a pair object containing a String and an integer
var stringIntegerPair = new Pair<>("String", 20);


// Declare a pair object containing two other pairs!
var pairPairPair = new Pair<>(new Pair<>(1, 2), new Pair<>("String", 20));