Что эквивалентно C ++ Pair в Java?

Есть ли веская причина, почему в Java нет Pair ? Что было бы эквивалентом этой конструкции C ++? Я бы предпочел избежать переоценки своего.

Кажется, что 1.6 предоставляет нечто подобное ( AbstractMap.SimpleEntry ), но это выглядит довольно запутанным.

В streamе на comp.lang.java.help , Hunter Gratzner дает некоторые аргументы против наличия конструкции Pair в Java. Основной аргумент состоит в том, что class Pair не передает семантики о взаимосвязи между двумя значениями (откуда вы знаете, что означает «первый» и «второй»?).

Лучшая практика – написать очень простой class, например, предложенный Майком, для каждого приложения, которое вы сделали бы из classа Pair . Map.Entry – пример пары, Map.Entry в себе свое значение.

Подводя итог, на мой взгляд, лучше иметь class Position(x,y) , class Range(begin,end) и class Entry(key,value) а не общий Pair(first,second) который не «Расскажи мне что-нибудь о том, что он должен делать.

Это Java. Вы должны создать свой собственный class пар с дескриптивными именами classов и полей, а не учитывать, что вы изобретете колесо, написав hashCode () / equals () или реализуя Comparable снова и снова.

Совместимость с HashMap Класс пары:

 public class Pair { 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 { private F first; private S second; } 

Он имеет все преимущества ответа от @arturh (кроме сопоставимости), имеет hashCode , equals , toString и статический «конструктор».

Apache Commons Lang 3.0+ имеет несколько classов Pair: http://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/tuple/package-summary.html

Еще один способ реализовать Pair.

  • Публичные неизменяемые поля, т. Е. Простая структура данных.
  • Сопоставимые.
  • Простой hash и равный.
  • Простая фабрика, поэтому вам не нужно предоставлять типы. например Pair.of («привет», 1);

     public class Pair implements Comparable> { public final FIRST first; public final SECOND second; private Pair(FIRST first, SECOND second) { this.first = first; this.second = second; } public static  Pair of(FIRST first, SECOND second) { return new Pair(first, second); } @Override public int compareTo(Pair 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 предлагает вам кортежные classы от одного до десяти элементов:

 Unit (1 element) Pair (2 elements) Triplet (3 elements) Quartet (4 elements) Quintet (5 elements) Sextet (6 elements) Septet (7 elements) Octet (8 elements) Ennead (9 elements) Decade (10 elements) 

Это зависит от того, для чего вы хотите его использовать. Типичная причина для этого – перебрать карты, для которых вы просто это делаете (Java 5+):

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

android предоставляет class Pair ( http://developer.android.com/reference/android/util/Pair.html ), здесь реализация:

 public class Pair { 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  Pair  create(A a, B b) { return new Pair(a, b); } } 

Самая большая проблема, вероятно, в том, что нельзя гарантировать неизменность A и B (см. Как гарантировать, что параметры типа являются неизменяемыми ), поэтому hashCode() может давать непоследовательные результаты для той же пары после вставки в коллекцию (например, дать неопределенное поведение, см. Определение равных в терминах изменяемых полей ). Для конкретного (не общего) classа Pair программист может обеспечить неизменность, тщательно выбирая A и B, чтобы быть неизменными.

Во всяком случае, очистка предупреждений родословной от ответа @ PeterLawrey (java 1.7):

 public class Pair, B extends Comparable> implements Comparable> { public final A first; public final B second; private Pair(A first, B second) { this.first = first; this.second = second; } public static , B extends Comparable> Pair of(A first, B second) { return new Pair(first, second); } @Override public int compareTo(Pair 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 .

Для получения дополнительной информации о том, почему этот синтаксис см. Убедитесь, что объекты реализуют Comparable и для подробного объяснения. Как реализовать универсальную функцию max(Comparable a, Comparable b) в Java?

На мой взгляд, на Java нет пары, потому что, если вы хотите добавить дополнительную функциональность непосредственно в пару (например, Comparable), вы должны связать типы. В C ++ нам просто все равно, и если типы, составляющие пару, не имеют operator < , pair::operator < также не будет компилироваться.

Пример сопоставимого без ограничения:

 public class Pair implements Comparable> { public final F first; public final S second; /* ... */ public int compareTo(Pair 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> a = /* ... */; Pair> b = /* ... */; //Runtime error here instead of compile error! System.out.println(a.compareTo(b)); 

Пример сравнения с проверкой времени компиляции для сопоставимых аргументов типа:

 public class Pair< F extends Comparable, S extends Comparable > implements Comparable> { public final F first; public final S second; /* ... */ public int compareTo(Pair 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 > 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 Pair> a = /* ... */; Pair> b = /* ... */; System.out.println(a.compareTo(b)); 

Это хорошо, но на этот раз вы не можете использовать несопоставимые типы как аргументы типа в Pair. В некоторых classах утилиты можно использовать множество компараторов для пары, но люди C ++ могут не получить их. Другой способ - написать много classов в иерархии типов с разными ограничениями на аргументы типа, но существует слишком много возможных границ и их комбинаций ...

JavaFX (который поставляется в комплекте с Java 8) имеет class

Как уже отмечалось многими другими, это действительно зависит от варианта использования, если class Pair полезен или нет.

Я думаю, что для частной вспомогательной функции вполне законно использовать class Pair, если это делает ваш код более читаемым и не стоит усилий для создания еще одного classа значений со всем его кодовым табличным кодом.

С другой стороны, если ваш уровень абстракции требует четкого документирования семантики classа, который содержит два объекта или значения, тогда вы должны написать для него class. Обычно это тот случай, если данные являются бизнес-объектом.

Как всегда, это требует квалифицированного решения.

Для вашего второго вопроса я рекомендую class Pair из библиотек Apache Commons. Они могут рассматриваться как расширенные стандартные библиотеки для Java:

https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/tuple/Pair.html

Вы также можете захотеть взглянуть на EqualsBuilder , HashCodeBuilder и ToStringBuilder от Apache Commons, которые упрощают создание classов значений для ваших бизнес-объектов.

Интерфейс Map.Entry довольно близок к парам c ++. Посмотрите на конкретную реализацию, например AbstractMap.SimpleEntry и AbstractMap.SimpleImmutableEntry. Первый элемент – getKey (), а второй – getValue ().

Хорошая новость Java добавила ключевую пару.

просто импортируйте javafx.util.Pair ;

и использовать просто как в c++ .

 Pair < Key , Value > 

например

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

Вы можете использовать class утилиты javafx, Pair который выполняет ту же цель, что и пара <> в c ++. https://docs.oracle.com/javafx/2/api/javafx/util/Pair.html

В соответствии с природой языка Java, я полагаю, люди на самом деле не требуют Pair , интерфейс, как правило, то, что им нужно. Вот пример:

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

Поэтому, когда люди хотят вернуть два значения, они могут сделать следующее:

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

Это довольно легкое решение, и оно отвечает на вопрос «Что такое семантика Pair ?». Ответ заключается в том, что это assembly интерфейса с двумя (может быть разными) типами, и у нее есть методы для возврата каждого из них. Это зависит от вас, чтобы добавить к нему еще одну семантику. Например, если вы используете Position и REALLY хотите указать ее в своем коде, вы можете определить PositionX и PositionY которые содержат Integer , чтобы составить Pair . Если JSR 308 доступен, вы также можете использовать Pair<@PositionX Integer, @PositionY Ingeger> чтобы упростить это.

EDIT: Одна вещь, которую я должен указать здесь, состоит в том, что указанное выше определение явно связывает имя параметра типа и имя метода. Это ответ тем, кто утверждает, что Pair содержит семантической информации. На самом деле метод getL означает «дать мне элемент, соответствующий типу параметра типа L», что что-то значит.

EDIT: Вот простой class утилиты, который может облегчить жизнь:

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

Применение:

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

Пара была бы хорошим материалом, чтобы быть базовой конструкцией для сложных дженериков, например, это из моего кода:

 WeakHashMap, String> map = ... 

Это то же самое, что и у Haskell’s Tuple

Для языков программирования, таких как Java, альтернативная структура данных, используемая большинством программистов для представления пары, такой как структуры данных, представляет собой два массива, и к ним обращаются через один и тот же индекс

пример: http://www-igm.univ-mlv.fr/~lecroq/string/node8.html#SECTION0080

Это не идеально, так как данные должны быть связаны вместе, но также оказаться довольно дешевыми. Кроме того, если ваш вариант использования требует хранения координат, тогда лучше создать собственную структуру данных.

У меня в библиотеке что-то в этом роде

 public class Pair{.. } 

Несмотря на синтаксически подобный подход, Java и C ++ имеют очень разные парадигмы. Написание C ++, как Java, является плохим C ++, и писать Java, как C ++, является плохой Java.

С помощью IDE, основанного на отражении, например Eclipse, запись функциональности classа «пара» выполняется быстро и просто. Создайте class, определите два поля, используйте различные опции меню «Создать XX», чтобы заполнить class за считанные секунды. Может быть, вам придется набирать «compareTo» в реальном времени, если вам нужен интерфейс Comparable.

С отдельными параметрами декларации / определения в языке C ++ генераторы кода не так хороши, поэтому ручная работа с небольшими полезными classами – это более трудоемкая скука. Поскольку пара является шаблоном, вам не нужно платить за функции, которые вы не используете, и средство typedef позволяет назначать значащие типы имен для кода, поэтому возражения о «без семантики» действительно не задерживаются.

Брайан Гетц, Пол Сандоз и Стюарт Маркс объясняют, почему во время сеанса QA в Devoxx’14.

Наличие родового парного classа в стандартной библиотеке превратится в технический долг после введения значений типов .

См. Также: Есть ли в Java SE 8 пары или кортежи?

Простой способ. Объект [] – может использоваться как кортеж с неограниченным объемом

Я заметил, что все реализации Pair усеяны здесь атрибутом, означающим порядок двух значений. Когда я думаю о паре, я думаю о комбинации двух предметов, в которой порядок этих двух не имеет значения. Вот моя реализация неупорядоченной пары, с hashCode и equals переопределениями для обеспечения желаемого поведения в коллекциях. Также клонируется.

 /** * The class Pair 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.

* The term "object" as being a one of a Pair can be loosely interpreted. A * Pair may have one or two null entries as values. Both values * may also be the same object.

* Mind that the order of the type parameters T and U is of no importance. A * Pair<T, U> can still return true for method equals * called with a Pair<U, T> argument.

* 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.

*/ public class Pair 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 Pair<T, U> 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 null 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 clone() { return new Pair(object1, object2); } /** * Indicates whether some other object is "equal" to this one. * This Pair is considered equal to the object if and only if *

    *
  • the Object argument is not null, *
  • the Object argument has a runtime type Pair or a subclass, *
* AND *
    *
  • the Object argument refers to this pair *
  • OR this pair's values are both null and the other pair's values are both null *
  • 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 *
  • 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: *
      *
    • v1 equals o1 and v2 equals o2 *
    • v1 equals o2 and v2 equals o1 *
    *
* In any other case (such as when this pair has two null parts but the other * only one) this method returns false.

* The type parameters that were used for the other pair are of no importance. * A Pair<T, U> can return true 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 null. Type erasure for parameter types at compile * time means that type checks are delegated to calls of the equals * 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 null * contributes 0 to the sum. This implementation adheres to the contract for * hashCode() as specified for Object(). 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' equals 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; } }

Эта реализация была правильно протестирована на модуле, и было проверено использование в наборе и карте.

Заметьте, я не претендую на публикацию этого в общественном достоянии. Это код, который я только что написал для использования в приложении, поэтому, если вы собираетесь его использовать, пожалуйста, воздержитесь от прямого копирования и беспорядка с комментариями и именами. Поймай мой дрейф?

If anyone wants a dead-simple and easy to use version I made my available at https://github.com/lfac-pt/Java-Pair . Also, improvements are very much welcome!

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.

You can use Google’s AutoValue library – https://github.com/google/auto/tree/master/value .

You create a very small abstract class and annotate it with @AutoValue and the annotation processor generates a concrete class for you that has a value semantic.

Here are some libraries that have multiple degrees of tuples for your convenience:

  • JavaTuples . Tuples from degree 1-10 is all it has.
  • JavaSlang . Tuples from degree 0-8 and lots of other functional goodies.
  • jOOλ . Tuples from degree 0-16 and some other functional goodies. (Disclaimer, I work for the maintainer company)
  • Functional Java . Tuples from degree 0-8 and lots of other functional goodies.

Other libraries have been mentioned to contain at least the Pair tuple.

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 { private final K element0; private final V element1; public static  Pair createPair(K key, V value) { return new Pair(key, value); } public Pair(K element0, V element1) { this.element0 = element0; this.element1 = element1; } public K getElement0() { return element0; } public V getElement1() { return element1; } } 

usage :

 Pair 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 : http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Table . They give the following example usage, for graph edges:

 Table 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, 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.

Давайте будем гением компьютера.