Java中的C ++ Pair <L,R>等价于什么?

为什么在Java中没有Pair是否有很好的理由? 这个C ++构造的等价物是什么? 我宁愿避免重新实现自己的。

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

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

一个更好的做法是写一个非常简单的类,就像Mike提出的那样,对于每个应用程序来说,你将会用Pair类。 Map.Entry就是一个以它的名字来expression意义的例子。

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

这是Java。 你必须用描述性的类和字段名称来制作你自己定制的Pair类,而不要介意通过编写hashCode()/ equals()或者反复实现Comparable来重新发明轮子。

HashMap兼容配对类:

 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; } } 

我能拿出的最短的一对是使用龙目岛 :

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

它具有从@arturh得到的所有好处(除了可比性),它有hashCodeequalstoString和一个静态的“构造函数”。

另一种实现配对的方法。

  • 公有的不可变字段,即简单的数据结构。
  • 可比。
  • 简单的散列和等于。
  • 简单的工厂,所以你不必提供types。 例如Pair.of(“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 + ')'; } } 

Apache Commons Lang 3.0+有几个Pair类: http : //commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/tuple/package-summary.html

那怎么样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 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上的不变​​性(参见如何确保types参数是不可变的 ),所以hashCode()可能会给同一个Pair插入一个集合产生不一致的结果给出未定义的行为,请参阅使用可变字段定义等号 )。 对于一个特定的(非generics)Pair类,程序员可以通过仔细selectA和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<?, ?>

有关此语法的详细信息,请参阅此处以及此处的详细说明

正如许多其他人已经说过的,如果Pair类是有用的,它确实取决于用例。

我认为对于一个私人帮助函数来说,使用Pair类是完全合法的,如果这使得你的代码更具可读性,并且不值得用所有的锅炉代码创build另一个值类。

另一方面,如果你的抽象层次要求你清楚地logging包含两个对象或值的类的语义,那么你应该为它写一个类。 通常情况下,如果数据是业务对象的话。

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

对于第二个问题,我推荐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,因为如果你想直接在这个对上添加额外的function(比如Comparable),你必须绑定这些types。 在C ++中,我们不在意,如果构成一对的types没有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)); 

与编译时间相比较的一个例子,检查types参数是否可比较:

 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中使用非可比types作为types参数。 在某些实用程序类中,可能会使用大量的比较器对,但是C ++人可能不会得到它。 另一种方法是在types参数的不同边界上写入大量的类,但是有太多可能的边界及其组合。

JavaFX(与Java 8捆绑在一起)具有Pair <A,B>类

Map.Entry接口非常接近c ++对。 看一下具体的实现,比如AbstractMap.SimpleEntry和AbstractMap.SimpleImmutableEntry,第一项是getKey(),第二项是getValue()。

根据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; } } 

这是一个非常轻量级的解决scheme,它回答了“ Pair<L,R>的语义是什么?”的问题。 答案是,这是一个有两个(可能不同)types的接口构build,它有方法来返回它们中的每一个。 这是由你来进一步的语义。 例如,如果您正在使用Position并且真的想要在您的代码中指明它,您可以定义包含Integer PositionXPositionY ,以组成一个Pair<PositionX,PositionY> 。 如果JSR 308可用,您也可以使用Pair<@PositionX Integer, @PositionY Ingeger>来简化。

编辑:我应该指出的一件事是上面的定义明确地涉及types参数名称和方法名称。 这是一个答案,认为一个Pair缺乏语义信息。 实际上, getL方法的getL是“给我对应于types参数L的types的元素”,这意味着什么。

编辑:这是一个简单的工具类,可以使生活更轻松:

 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增加了键值对。

只需导入javafx.util.Pair ;

并简单地使用在c++

 Pair < Key , Value > 

例如

 Pair < Integer , Integer > pr = new Pair<Integer , Integer>() pr.get(key); // will return corresponding value 
 Collections.singletonMap(left, rigth); 

您可以使用javafx工具类Pair ,它与c ++中的pair <>具有相同的用途。 https://docs.oracle.com/javafx/2/api/javafx/util/Pair.html

对将是一个很好的东西,是一个复杂的generics的基本build设单位,例如,这是从我的代码:

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

它和Haskell的Tuple一样

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

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

数据绑定在一起并不理想,而且相当便宜。 另外,如果你的用例需要存储坐标,那么build立你自己的数据结构就更好了。

我在图书馆里有这样的事情

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

尽pipe在语法上类似,但Java和C ++有着非常不同的范例。 像Java一样编写C ++就是不好的C ++,像C ++一样编写Java就是不好的Java。

通过基于reflection的IDE(如Eclipse),编写“对”类的必要function将变得简单快捷。 创build类,定义两个字段,使用各种“生成XX”菜单选项在几秒钟内填写类。 如果你想要Comparable接口,也许你必须真正快速地input“compareTo”。

在语言中使用单独的声明/定义选项,C ++代码生成器并不是很好,所以只需编写一些实用程序类就可以节省大量的时间。 因为这个对是一个模板,所以你不必为没有使用的函数支付费用,typedef工具允许为代码分配有意义的types名,所以关于“无语义”的反对​​意见不会被阻止。

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

标准库中的通用对类在引入值types时会变成技术债务。

另请参见: Java SE 8是否具有对或元组?

简单的方法Object [] – 可以用作任何维数元组

我注意到所有的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; } } 

这个实现已经过适当的unit testing,并且在Set和Map中的使用已经被试用了。

注意我不是声称在公共领域释放这个。 这是我刚刚编写的用于应用程序的代码,所以如果您要使用它,请避免直接复制和混淆注释和名称。 赶上我的漂移?

如果有人想要一个简单易用的版本,我可以通过https://github.com/lfac-pt/Java-Pair获得; 。 另外,改进非常受欢迎!

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

你创build一个非常小的抽象类,并用@AutoValue注释它,注释处理器为你创build一个具有价值语义的具体类。

下面是一些为了方便而有多个元组的库:

  • JavaTuples 。 1-10级的元组就是它的全部。
  • JavaSlang 。 0-8级的元组和许多其他function性的东西。
  • jOOλ 。 0-16级的元组和其他一些function性的东西。 (免责声明,我为维护者公司工作)
  • function性的Java 。 0-8级的元组和许多其他function性的东西。

其他库已被提及至less包含Pair元组。

Specifically, in the context of functional programming which makes use of a lot of structural typing, rather than nominal typing ( as advocated in the accepted answer ), those libraries and their tuples come in very handy.

 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(); 

Immutable, only a pair !

Many people are posting Pair code that is usable as a key in a Map…If you're trying to use a pair as a hashing key (a common idiom), be sure to check out Guava's Table<R,C,V> : http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Table . They give the following example usage, for graph edges:

 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 

A Table maps two keys to a single value, and provides efficient lookups for both types of keys alone as well. I've started using this data structure instead of a Map<Pair<K1,K2>, V> in many parts of my code. There are array, tree, and other implementations for both dense and sparse uses, with the option of specifying your own intermediate map classes.

com.sun.tools.javac.util.Pair is an simple implementation of a pair. It can be found in jdk1.7.0_51\lib\tools.jar.

Other than the org.apache.commons.lang3.tuple.Pair, it's not just an interface.