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 + ')';
}
}
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);
}
}
0
这取决于你想用它做什么 . 这样做的典型原因是遍历 Map ,您只需执行此操作(Java 5):
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());
}
编辑:我应该指出的一件事是上面的定义明确地涉及类型参数名称和方法名称 . 对于那些认为 Pair 缺乏语义信息的人来说,这是一个答案 . 实际上,方法 getL 表示"give me the element that correspond to the type of type parameter 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; }
};
}
}
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));
Comparable的一个例子,编译时检查类型参数是否具有可比性:
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中使用非可比类型作为类型参数 . 在某些实用程序类中,可以使用大量的Comparators for Pair,但C人可能无法获得它 . 另一种方法是在类型层次结构中编写许多类,在类型参数上使用不同的边界,但是有太多可能的边界及其组合......
/**
* 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<T, U> can still return <code>true</code> for method <code>equals</code>
* called with a Pair<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<T, U></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 <v1, v2> of
* this pair and <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<T, U> can return <code>true</code> for equality testing with
* a Pair<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;
}
}
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;
}
}
30 回答
最大的问题可能是无法确保A和B的不变性(参见How to ensure that type parameters are immutable)所以
hashCode()
可能会在插入集合之后为同一对提供不一致的结果(这会产生未定义的行为,请参阅Defining equals in terms of mutable fields) . 对于特定(非泛型)Pair类,程序员可以通过仔细选择A和B为不可变来确保不变性 .无论如何,从@PeterLawrey的回答中清除泛型的警告(java 1.7):
非常欢迎添加/更正:)特别是我不太确定我使用
Pair<?, ?>
.有关此语法原因的详细信息,请参阅Ensure that objects implement Comparable以及详细说明How to implement a generic max(Comparable a, Comparable b) function in Java?
许多人发布的
Pair
代码可用作 Map 中的键...如果你're trying to use a pair as a hashing key (a common idiom), be sure to check out Guava' sTable<R,C,V>
:http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Table . 对于图形边缘,它们给出以下示例用法:Table
将两个键映射到单个值,并且仅为两种类型的键提供有效的查找 . 我已经开始在我的代码的许多部分使用这个数据结构而不是Map<Pair<K1,K2>, V>
. 密集和稀疏用途都有数组,树和其他实现,可以选择指定自己的中间映射类 .以下是一些具有多个元组度的库,以方便您:
JavaTuples . 1-10级的元组就是它的全部 .
JavaSlang . 0-8级的元组和许多其他功能性好东西 .
jOOλ . 0-16级的元组和其他一些功能性的好东西 . (免责声明,我为维护公司工作)
Functional Java . 0-8级的元组和许多其他功能性好东西 .
已经提到其他库至少包含
Pair
元组 .具体来说,在使用大量结构类型而不是名义类型(as advocated in the accepted answer)的函数式编程的上下文中,这些库及其元组非常方便 .
android提供
Pair
类(http://developer.android.com/reference/android/util/Pair.html),这里实现:这取决于你想用它做什么 . 这样做的典型原因是遍历 Map ,您只需执行此操作(Java 5):
您可以使用Google的AutoValue库 - https://github.com/google/auto/tree/master/value .
您创建了一个非常小的抽象类,并使用@AutoValue对其进行批注,并且注释处理器为您生成具有值语义的具体类 .
在a thread on comp.lang.java.help中,Hunter Gratzner提出了一些反对在Java中存在
Pair
结构的论点 . 主要论点是类Pair
没有传达关于两个值之间关系的任何语义(你怎么知道"first"和"second"是什么意思?) .更好的做法是为每个应用程序编写一个非常简单的类,就像Mike提出的那样,对于
Pair
类 .Map.Entry
是一个在其名称中带有其含义的对的示例 .总而言之,在我看来,最好有一个类
Position(x,y)
,一个类Range(begin,end)
和一个类Entry(key,value)
而不是一个没有't tell me anything about what it'应该做的泛型Pair(first,second)
.好消息
Java
增加了关键值对 .只需导入
javafx.util.Pair
;并在
c++
中使用 .例如
HashMap兼容对类:
Apache Commons Lang 3.0有几个Pair类:http://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/tuple/package-summary.html
根据Java语言的本质,我认为人们实际上并不需要
Pair
,接口通常是他们所需要的 . 这是一个例子:因此,当人们想要返回两个值时,他们可以执行以下操作:
这是一个非常轻量级的解决方案,它回答了“
Pair<L,R>
的语义是什么?”的问题 . 答案是,这是一个具有两个(可能是不同的)类型的接口构建,并且它具有返回每个类型的方法 . 您可以在其中添加更多语义 . 例如,如果您正在使用位置并且真的想在您的代码中指明它,您可以定义包含Integer
的PositionX
和PositionY
,以组成Pair<PositionX,PositionY>
. 如果JSR 308可用,您也可以使用Pair<@PositionX Integer, @PositionY Ingeger>
来简化它 .编辑:我应该指出的一件事是上面的定义明确地涉及类型参数名称和方法名称 . 对于那些认为
Pair
缺乏语义信息的人来说,这是一个答案 . 实际上,方法getL
表示"give me the element that correspond to the type of type parameter L",这意味着什么 .编辑:这是一个简单的实用工具类,可以使生活更轻松:
用法:
Map.Entry界面非常接近c对 . 看看具体的实现,比如AbstractMap.SimpleEntry和AbstractMap.SimpleImmutableEntry第一项是getKey(),第二项是getValue() .
com.sun.tools.javac.util.Pair是一对的简单实现 . 它可以在jdk1.7.0_51 \ lib \ tools.jar中找到 .
除了org.apache.commons.lang3.tuple.Pair之外,它不仅仅是一个界面 .
对于像Java这样的编程语言,大多数程序员用来表示像数据结构这样的对的备用数据结构是两个数组,并且通过相同的索引访问数据
示例:http://www-igm.univ-mlv.fr/~lecroq/string/node8.html#SECTION0080
这并不理想,因为数据应绑定在一起,但结果也相当便宜 . 此外,如果您的用例需要存储坐标,那么最好 Build 自己的数据结构 .
我的图书馆里有这样的东西
尽管语法相似,但Java和C有着截然不同的范例 . 像Java一样编写C是坏C,像C一样编写Java是糟糕的Java .
使用像Eclipse这样的基于反射的IDE,编写“对”类的必要功能非常简单快捷 . 创建类,定义两个字段,使用各种“Generate XX”菜单选项在几秒钟内填写该类 . 如果你想要Comparable界面,也许你必须快速输入“compareTo” .
在C语言中使用单独的声明/定义选项代码生成器并不是那么好,因此手工编写小实用程序类会耗费时间 . 因为该对是模板,所以您不必为不使用的函数付费,并且typedef工具允许为代码分配有意义的类型名,因此关于“无语义”的异议并没有真正成功 .
简单的方法Object [] - 可以用作自己的维度元组
这是Java . 你必须使用描述性的类和字段名称来创建自己的Pair类,并且不要介意通过编写hashCode()/ equals()或一次又一次地实现Comparable来重新发明轮子 .
配对将是一个很好的东西,成为复杂泛型的基本构造单元,例如,这来自我的代码:
它和Haskell的Tuple一样
实现Pair的另一种方法 .
公共不可变字段,即简单数据结构 .
可比较 .
简单哈希和等号 .
简单工厂,因此您无需提供类型 . 例如Pair.of(“你好”,1);
Brian Goetz,Paul Sandoz和Stuart Marks explain why在Devoxx'14的QA Session 期间 .
一旦value types引入,标准库中的通用对类将变为技术债务 .
另见:Does Java SE 8 have Pairs or Tuples?
在我看来,Java中没有Pair,因为如果你想在对上直接添加额外的功能(例如Comparable),你必须绑定类型 . 在C中,我们只是不关心,如果组成一对的类型没有
operator <
,pair::operator <
也不会编译 .没有边界的可比较的示例:
Comparable的一个例子,编译时检查类型参数是否具有可比性:
这很好,但这次你不能在Pair中使用非可比类型作为类型参数 . 在某些实用程序类中,可以使用大量的Comparators for Pair,但C人可能无法获得它 . 另一种方法是在类型层次结构中编写许多类,在类型参数上使用不同的边界,但是有太多可能的边界及其组合......
JavaFX(与Java 8捆绑在一起)具有Pair <A,B>类
我注意到所有的Pair实现都在这里散布,属性意味着两个值的顺序 . 当我想到一对时,我想到了两个项目的组合,其中两个项目的顺序并不重要 . 这是我对无序对的实现,使用
hashCode
和equals
覆盖以确保集合中的所需行为 . 也可克隆 .该实现已经过适当的单元测试,并且已经尝试了在Set和Map中的使用 .
请注意,我并未声称要在公共领域发布此内容 . 这是我刚刚编写的用于应用程序的代码,所以如果你打算使用它,请不要直接复制并搞砸注释和名称 . grab 我的漂移?
如果有人想要一个简单易用且易于使用的版本,我可以在https://github.com/lfac-pt/Java-Pair上找到它 . 此外,非常欢迎改进!
我能想出的最短的一对是,使用Lombok:
它具有the answer from @arturh的所有好处(除了可比性),它有
hashCode
,equals
,toString
和静态“构造函数” .用法:
永恒,只有一对!
您可以使用javafx实用程序类
Pair
,其作用与c中的pair <>相同 . https://docs.oracle.com/javafx/2/api/javafx/util/Pair.html正如许多其他人已经说过的那样,如果Pair类是否有用,它实际上取决于用例 .
我认为它是一个私人助手功能完全合法使用Pair类,如果这使得您的代码更具可读性,并且不值得使用其所有样板代码创建另一个值类 .
另一方面,如果您的抽象级别要求您清楚地记录包含两个对象或值的类的语义,那么您应该为它编写一个类 . 通常情况下,如果数据是业务对象 .
一如既往,需要熟练的判断力 .
对于第二个问题,我推荐Apache Commons库中的Pair类 . 那些可能被视为Java的扩展标准库:
https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/tuple/Pair.html
您可能还想查看Apache Commons的EqualsBuilder,HashCodeBuilder和ToStringBuilder,它们简化了为业务对象编写值类的过程 .
怎么样http://www.javatuples.org/index.html我发现它非常有用 .
javatuples为您提供一到十个元素的元组类: