题
是有一个很好的理由为什么没有 Pair<L,R>
在Java?什么是相当于这C++构?我宁愿避免重新实现我自己的。
它似乎 1.6 提供了类似的东西(AbstractMap.SimpleEntry<K,V>
),但这个看起来相当令人费解的。
解决方案
comp上的一个主题.lang.java.help
,Hunter Gratzner提出了一些反对在Java中存在 Pair
结构的论据。主要论点是类 Pair
没有传达关于两个值之间关系的任何语义(你怎么知道“first”和“second”是什么意思?)。
更好的做法是编写一个非常简单的类,就像Mike提出的那样,为每个应用程序编写 Pair
类。 Map.Entry
是一个在名称中带有意义的对的示例。
总而言之,在我看来,最好有一个类 Position(x,y)
,一个类 Range(开始,结束)
和一个类<代码>条目(键,值)而不是通用 Pair(第一,第二)
,它没有告诉我它应该做什么。
其他提示
这是Java。你必须使用描述性的类和字段名来制作自己的定制Pair类,而不要忘记你将通过编写hashCode()/ equals()或一次又一次地实现Comparable来重新发明轮子。
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;
}
}
Apache Commons Lang 3.0+有几个Pair类: http:// commons .apache.org /正确/公地琅/ apidocs /组织/阿帕奇/公地/ lang3 /元组/包summary.html
另一种方式来实现对。
- 公众不可改变的领域,即简单的数据结构。
- 可比性。
- 简单的散列和平等的。
简单的工厂所以你不必提供的类型。例如对。的("hello",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 + ')'; } }
我找到 http://www.javatuples.org/index.html 怎么样?它非常有用。
javatuples为您提供从1到10个元素的元组类:
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 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());
}
android提供 Pair
类( 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);
}
}
最大的问题可能是无法确保A和B的不变性(参见如何确保类型参数是不可变的)所以 hashCode()
可能会在插入之后为同一对提供不一致的结果例如一个集合(这将给出未定义的行为,请参阅根据可变字段定义等于)。对于特定(非泛型)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&lt;?,?&gt;
。
有关此语法原因的详细信息,请参阅确保对象实现可比较并获取详细说明如何在Java中实现通用的 max(Comparable a,Comparable b)
函数?
在我看来,Java中没有Pair,因为如果你想在对上直接添加额外的功能(例如Comparable),你必须绑定类型。在C ++中,我们只是不关心,如果组成一对的类型没有运算符&lt;
, pair :: operator&lt;
也不会编译。
没有边界的可比较的例子:
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 ++人员可能无法获得它。另一种方法是在类型层次结构中编写许多类,在类型参数上使用不同的边界,但是有太多可能的边界及其组合......
JavaFX(与Java 8捆绑在一起)具有Pair&lt; A,B&gt;类
正如许多其他人已经说过的那样,如果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 ,简化了业务对象的写入值类。
好消息 Java
添加了键值Pair。
只需导入 javafx.util.Pair
;
并简单地使用 c ++
。
Pair < Key , Value >
e.g。
Pair < Integer , Integer > pr = new Pair<Integer , Integer>()
pr.get(key); // will return corresponding value
您可以使用javafx实用程序类, Pair
,它的作用与对&lt;&gt;相同。在c ++中。 https://docs.oracle.com/javafx/2/api /javafx/util/Pair.html
Map.Entry 界面非常接近c ++对。查看具体实现,如 AbstractMap.SimpleEntry 和AbstractMap.SimpleImmutableEntry 第一项是getKey(),第二项是getValue()。
Collections.singletonMap(left, rigth);
根据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&lt; L,R&gt;
?&quot;”的语义是什么?答案是,这是一个具有两个(可能是不同的)类型的接口构建,并且它具有返回每个类型的方法。您可以在其中添加更多语义。例如,如果您正在使用Position并且真的想在您的代码中指明它,您可以定义包含 Integer
的 PositionX
和 PositionY
,组成 Pair&lt; PositionX,PositionY&gt;
。如果JSR 308可用,您也可以使用 Pair&lt; @PositionX Integer,@ PositionY Ingeger&gt;
来简化它。
编辑:
我应该在这里指出的一件事是上面的定义明确地涉及类型参数名称和方法名称。这是对那些认为 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");
虽然在语法上类似的、Java和C++有非常不同的模式。编写C++喜欢的是坏C++编写Java像C++是坏Java。
有反射的基础IDE喜欢食,编写一定功能的一个"配对"类快速和简单。创建类、定义两个领域,使用的各种"产生XX"选项菜单,填写类在几秒钟的事。也许你会需要输入一个"compareTo"真正的快速如果你想要可比较的接口。
与独立宣言》/定义的选择中的语言和数码发电机不太好,所以手写点的实用的课程被耗费更多的时间枯燥.因为对一个模板,你不必支付的功能,你不用和typedef设施,允许分配的有意义的typenames的代码,因此反对关于"没有义"不真的很抱了起来。
配对将是一个很好的东西,成为复杂泛型的基本构造单元,例如,这来自我的代码:
WeakHashMap<Pair<String, String>, String> map = ...
它与Haskell的Tuple
相同对于Java等编程语言,大多数程序员用来表示数据结构对的备用数据结构是两个数组,数据通过相同的索引访问
示例: http://www-igm.univ -mlv.fr/~lecroq/string/node8.html#SECTION0080
这并不理想,因为数据应绑定在一起,但结果也相当便宜。此外,如果您的用例需要存储坐标,那么最好建立自己的数据结构。
我的库里有这样的东西
public class Pair<First,Second>{.. }
您可以使用Google的AutoValue库 - https://github.com/google/auto /树/主/值。
您创建了一个非常小的抽象类,并使用@AutoValue对其进行注释,并且注释处理器为您生成具有值语义的具体类。
这里有一些图书馆有多个程度的组为了您的方便:
- JavaTuples.组从程度所有1-10。
- JavaSlang.组从程度的0-8和大量的其他功能的好东西。
- jOOλ.组从度0至16和其他一些功能的好东西。(免责声明,我的工作对维护者的公司)
- 功能Java.组从程度的0-8和大量的其他功能的好东西。
其他图书馆已经提到的至少包含的 Pair
元组。
具体地说,在上下文的编程功能,它使用了大量的结构类型,而不是名义打字(作为主张在公认的答案),这些图书馆和他们的组来非常方便。
简单方法Object [] - 可以用作&#1091;维度元组
我注意到所有的Pair实现都在这里散布,属性意味着两个值的顺序。当我想到一对时,我想到了两个项目的组合,其中两个项目的顺序并不重要。这是我对无序对的实现,使用 hashCode
和 equals
覆盖以确保集合中的所需行为。也可以复制。
/**
* 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;
}
}
此实现已经过适当的单元测试,并已尝试在Set和Map中使用。
请注意,我并未声称要在公共领域发布此内容。这是我刚刚编写的用于应用程序的代码,所以如果你打算使用它,请不要直接复制并搞砸注释和名称。抓住我的漂移?
如果有人想要一个简单易用且易于使用的版本,我可以通过 https:/ /github.com/lfac-pt/Java-Pair 。此外,非常欢迎改进!
com.sun.tools.javac.util.Pair是一对的简单实现。 它可以在jdk1.7.0_51 \ lib \ tools.jar中找到。
除了org.apache.commons.lang3.tuple.Pair之外,它不仅仅是一个界面。
另一个简洁的lombok实现
import lombok.Value;
@Value(staticConstructor = "of")
public class Pair<F, S> {
private final F first;
private final S second;
}
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();
永恒,只有一对!