Есть ли веская причина, по которой в Java нет Pair<L,R>? Что было бы эквивалентом этой конструкции C++? Я бы предпочел избегать повторной реализации своей собственной.
Кажется, что 1.6 предоставляет нечто подобное (AbstractMap.SimpleEntry<K,V>), но это выглядит довольно запутанным.
См. Мой ответ stackoverflow.com/a/16089164/173149, где я без проблем использую AbstractMap.SimpleImmutableEntry (сложный пример).
SimpleEntry имеет три метода, не унаследованных от Object. Почему он должен быть «запутанным»?
Из-за namig произвольное наименование одного ключа и одного значения.
См. Также stackoverflow.com/questions/6271731/…
Если вы используете Java 8, import javafx.util.Pair решит ваши проблемы.
@sffc JavaFX отсутствует ни в одном из путей к классам по умолчанию в JDK7, его использование требует добавления библиотек времени выполнения JFX вручную.
@Enerccio: Итак, вы на самом деле утверждаете, что «первое» и «второе» не произвольно, а «ключ» и «значение» - нет? Тогда это одна из веских причин отсутствия такого класса в SDK. Будет вечный спор о «правильном» именовании.




Это зависит от того, для чего вы хотите его использовать. Типичная причина для этого - перебирать карты, для чего вы просто делаете это (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());
}
Я не уверен, что в этом случае поможет специальный класс :)
«Типичная причина для этого - перебирать карты». Действительно?
В нить на comp.lang.java.help Хантер Грацнер приводит некоторые аргументы против наличия конструкции Pair в Java. Главный аргумент заключается в том, что класс Pair не передает никакой семантики относительно отношений между двумя значениями (как узнать, что означают «первое» и «второе»?).
Лучше всего написать очень простой класс, подобный тому, который предложил Майк, для каждого приложения, которое вы бы сделали из класса Pair. Map.Entry - это пример пары, значение которой заложено в ее названии.
Подводя итог, на мой взгляд, лучше иметь класс Position(x,y), класс Range(begin,end) и класс Entry(key,value), чем общий Pair(first,second), который ничего не говорит мне о том, что он должен делать.
Гратцнер секет волосы. Мы вполне счастливы вернуть одно значение как примитивный или встроенный класс, не инкапсулируя его в класс. Если бы мы вернули кортеж из дюжины элементов, никто бы не возражал, у него должен быть свой собственный класс. Где-то посередине проходит (нечеткая) разделительная линия. Думаю, наш мозг ящерицы легко справляется с парами.
Я согласен с Яном. Java позволяет возвращать int; он не заставляет вас создавать псевдоним для int каждый раз, когда вы его используете. Пары не сильно отличаются.
Если бы мы могли распаковать пару непосредственно в ваши локальные переменные или передать ее методу, который принимает два аргумента, Pair была бы полезным классом. Поскольку мы не можем распаковать его таким образом, создание значимого класса и сохранение значений вместе не выглядит так уж плохо. И, если вам действительно нужна пара, несмотря на ограничения, всегда есть Object [2] + cast :-)
Дело в том, что если вы не согласны с Грацнером, то в нескольких местах есть реализации Pair. У Apache Commons и Guava есть IIRC. Используйте те. Но помещать что-то в основные библиотеки Java означает, что это благородный и одобренный способ делать вещи (с заглавными буквами), и, поскольку люди не согласны с этим, мы не должны помещать это туда. В старых библиотеках уже достаточно хлама, давайте не будем добавлять туда лишнего.
Я не понимаю, что такое java "благородный и одобренный способ". Допустим, я определенно не хочу создавать новый класс, тогда самая простая альтернатива - использовать массив с двумя записями, который намного хуже пары с точки зрения передачи семантики.
@ user463035818 У каждого языка есть свое предназначение. Мне кажется, что вам вообще не нужна java, а нужен какой-то другой язык, который не так ограничен и позволяет вам взламывать.
@Dragas Когда мне нужна пара значений, тогда это не Java ... серьезно?
Мы все время используем карты и списки в Java, если семантика была наиболее важным аспектом, тогда карты и списки (являющиеся общими типами данных) также не скрывают смысл, Map <String, Int> и Pair of Pair < String, int> выглядит похоже
Совместимый с 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;
}
}
Вероятно, вы захотите удалить сеттеры и сделать первый и второй финальными, таким образом сделав пару неизменной. (Если кто-то изменил компоненты после использования их в качестве хеш-ключа, произойдут странные вещи).
return "(" + first.toString () + "," + second.toString () + ")" в методе toString () может вызывать исключения NullPointerExceptions. Это лучше: return "(" + first + "," + second + ")";
Кроме того, либо пометьте пару как «окончательную», либо измените первую строку равенства на 'if (other! = Null && this.getClass () == other.getClass ())'
Извините за случайный вопрос nooby, но почему у вас есть вызов super () в конструкторе?
@Ibrahim: В данном случае это лишнее - поведение будет точно таким же, если вы вытащили super(). Обычно я бы просто отрезал его, если это необязательно, как здесь.
Это необязательно, но только в том смысле, что компилятор вставит вызов за вас, если вы не напишете его явно. Даже когда суперклассом является Object, существует метод java/lang/Object/<init>()V, и его нужно вызывать.
&& otherPair.first != null и && otherPair.second != null являются избыточными (потому что this.first/sec == otherPair.first/sec был бы истинным, если бы оба были нулевыми)
В строке, где вы пишете Pair otherPair = (Pair) other, Eclipse дает мне дружеское предупреждение: References to generic type Pair<A, B> should be parameterized. И в обоих случаях предлагается поменять Pair на Pair<?, ?>. Все хорошо?
Object.equals? Это может быть проще, чем вещи if (x == y) / if (x! = Null && x.equals (y)).
Пара была бы хорошей вещью, чтобы быть базовой конструкционной единицей для сложных дженериков, например, это из моего кода:
WeakHashMap<Pair<String, String>, String> map = ...
Это то же самое, что и Tuple в Haskell
Теперь я могу сказать, что использование Pair <A, B> делает код менее информативным, а реализация специальных объектов вместо использования Pair намного лучше.
Лучше или хуже. Представьте, что у вас есть функция, объединяющая два своих аргумента (например, слияние графиков в один), и вам необходимо кэшировать ее. Здесь Pair оптимален, так как особой семантики нет. Иметь ясное название для ясной концепции - это хорошо, но искать имя, в котором хорошо работают «первый» и «второй», - нет.
Это Java. Вы должны создать свой собственный класс Pair с описательными классами и именами полей, не говоря уже о том, что вы изобретете колесо, написав hashCode () / equals () или реализовав Comparable снова и снова.
Насмешка над Java была бы хорошей, если бы вы указали на Apache Common Lang, который содержит класс Pair.
Или вы можете просто использовать SimpleImmutableEntry
Наиболее распространенные IDE сгенерируют для вас соответствующий HashCode () / equals.
Я согласен. «Потому что это Java» - хороший ответ. Помните, что язык Java намеренно лишен определенных (кашлял C++) функций, которые сбивают с толку среднего программиста. Вот почему Java не позволяет вам переопределять операторы. Он также не допускает множественного наследования. Суть в том, что если какой-нибудь глупый программист, вероятно, злоупотребит этим, то Java сделает это трудным.
@JohnHenckel Это как инструктор, который задает простые вопросы на экзамене, потому что он хочет, чтобы все добились успеха. Тупой.
@TimGoodman Gratzner в спартанской одежде кричит, когда пинает нового ученика в яму стандартных имен
Другой способ реализовать 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 + ')';
}
}
Мне нравится статический заводской метод of. Это напоминает неизменяемые коллекции Google Guava.
В какой-то момент вы преобразуете o1 в Comparable, хотя ничто не указывает на то, что он действительно реализует этот интерфейс. Если это требование, параметр типа FIRST должен быть FIRST extends Comparable<?>.
Я не сторонник Java, так что, пожалуйста, простите меня за мое невежество, но о каких вспомогательных классах вы думали в комментариях TODO?
Может быть любым, например CompareableUtils или Comparables.
31 - плохая константа для hashCode. Например, если вы используете HashMap с ключом Pair <Integer, Integer> для 2D-карты, вы получите много коллизий. Например, (a * 65497) ^ b подойдет лучше.
@ MichałZieliński Я думаю, что 31 существует, потому что 31 - это любимое простое число Eclipse (появляется, когда вы используете «сгенерировать hashCode () и equals ()». Однако вычисление мощности было бы слишком дорогим для такого универсального класса, как этот.
@MarioCarneiro Java String.hashCode () used 31 имеет простое умножение задолго до запуска eclipse.
@MarioCarneiro ^ - это xor, а не сила
@PeterLawrey ситуация со строками другая - размер алфавита обычно меньше (char vs int)
Простой способ Object [] - может использоваться как кортеж любого измерения
Любое измерение, да. Но: громоздко в создании и небезопасно по типу.
На мой взгляд, в Java нет Pair, потому что, если вы хотите добавить дополнительные функции непосредственно в пару (например, Comparable), вы должны связать типы. В C++ нас просто не волнует, и если типы, составляющие пару, не имеют operator <, pair::operator < также не будет компилироваться.
Пример Comparable без ограничений:
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. Можно использовать множество компараторов для пары в каком-то служебном классе, но люди, работающие с C++, могут этого не понять. Другой способ - написать множество классов в иерархии типов с разными границами аргументов типа, но существует слишком много возможных границ и их комбинаций ...
Самая короткая пара, которую я мог придумать, с использованием Ломбок выглядит следующим образом:
@Data
@AllArgsConstructor(staticName = "of")
public class Pair<F, S> {
private F first;
private S second;
}
Он обладает всеми преимуществами ответ от @arturh (кроме сопоставимости), имеет hashCode, equals, toString и статический «конструктор».
Отлично! Понравилось это!
знакомство с ломбоком, бесценно.
Как уже заявляли многие другие, действительно зависит от варианта использования, полезен ли класс Pair или нет.
Я думаю, что для частной вспомогательной функции вполне законно использовать класс Pair, если это делает ваш код более читабельным и не стоит усилий по созданию еще одного класса значений со всем его кодом шаблона.
С другой стороны, если ваш уровень абстракции требует, чтобы вы четко документировали семантику класса, который содержит два объекта или значения, тогда вам следует написать для него класс. Обычно так бывает, если данные представляют собой бизнес-объект.
Как всегда, это требует умелого суждения.
Для вашего второго вопроса я рекомендую класс Pair из библиотек Apache Commons. Их можно рассматривать как расширенные стандартные библиотеки для Java:
https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/tuple/Pair.html
Вы также можете взглянуть на Apache Commons EqualsBuilder, HashCodeBuilder и ToStringBuilder, которые упрощают запись классов значений для ваших бизнес-объектов.
Обновленный URL-адрес - commons.apache.org/lang/api-release/index.html?org/apache/…, поскольку commons-lang3 вышел из стадии бета-тестирования. Это даже короче, чем мое собственное решение Lombok, если вы уже используете commons-lang 3.
Я заметил, что все реализации 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.
Заметьте, я не претендую на то, чтобы опубликовать это в открытом доступе. Это код, который я только что написал для использования в приложении, поэтому, если вы собираетесь его использовать, пожалуйста, воздержитесь от создания прямой копии и немного возитесь с комментариями и именами. Поймать мой дрейф?
на самом деле, проверьте внизу каждой страницы: «пользовательские материалы под лицензией cc-wiki»
Ах, я этого не заметил. Спасибо за предупреждение. В этом случае используйте код по своему усмотрению в рамках этой лицензии.
Речь идет об эквивалентной паре C++ - которая упорядочена. Также я думаю, что до тех пор, пока у кого-то есть ссылка на объект Pair, и они являются изменяемыми, вставка Pairs в коллекции может привести к неопределенному поведению.
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();
Неизменный, только пара!
Ух ты. Еще один? Попробуйте использовать свой с более сложными Generics - в какой-то момент он не сможет определить подходящие типы. Кроме того, должно быть возможно следующее: Pair<Object, Object> pair = Pair.createPair("abc", "def"), но я полагаю, что нужно написать Pair.createPair((Object)"abc", (Object)"def") с вашим кодом?
вы можете заменить статический метод следующим образом: @SuppressWarnings("unchecked") public static <K, V, X, Y> Pair<X, Y> createPair(K key, V value) { return new Pair<X, Y>((X) key, (Y) value); }, но я не знаю, является ли это хорошей практикой
Нет, это, скорее всего, только усугубит ситуацию. По моему опыту, по крайней мере один из компиляторов (попробуйте java6, java7, javadoc и eclipse java compiler) будет жаловаться. Традиционный new Pair<Object, Object>("abc", "def") оказался самым надежным в моих экспериментах.
Если кому-то нужна чрезвычайно простая и легкая в использовании версия, я сделал ее доступной на 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)
Забавно, но есть как минимум на 5 классов больше, чем я мог себе представить.
Они не поставляются со стандартной библиотекой java, не так ли?
@maaartinus По крайней мере, на 10 больше, чем я бы использовал.
@Boann: Хорошо, я остаюсь исправленным. Раньше я использовал Pair и мог представить, что буду использовать Triplet раз в 50 лет. Теперь я использую Lombok и создаю крошечный класс из 4 строк каждый раз, когда мне нужна пара. Так что «10 многовато» - это точно.
Нужен ли нам класс Bottom (0 element)? :)
Вау, это уродливо. Я знаю, что они пытаются сделать это явным, но кортеж с перегруженными параметрами, как в C#, был бы лучше.
Не могу поверить, что кто-то на самом деле нашел время, чтобы это написать.
Спасибо за эту ссылку. На мой взгляд это полезно. Большой плюс в том, что у этой библиотеки нет других зависимостей.
Унарный (не нулевой!) Unit. А зачем? Они даже не поняли латынь, почему это «Пара», а не «Дуплет» или что-то в этом роде ... Это просто глупо.
Вы знаете ... Вам просто нужен Pair, а затем вы можете написать Pair<A, Pair<B, Pair<C, Pair<D, Pair<E, Pair<F, Pair<G, Pair<H, Pair<I, J>>>>>>>>>, когда вам понадобится Decade.
@tsh: Вы сказали Headache<...>?
Apache Commons Lang 3.0+ имеет несколько парных классов: http://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/tuple/package-summary.html
В соответствии с природой языка 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>?». Ответ таков: это сборка интерфейса с двумя (могут быть разными) типами, и у нее есть методы для возврата каждого из них. Вы должны добавить к нему дополнительную семантику. Например, если вы используете Position и ДЕЙСТВИТЕЛЬНО хотите указать это в своем коде, вы можете определить PositionX и PositionY, которые содержат Integer, чтобы составить 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");
А как насчет equals, hashCode и toString?
ну это всего лишь минимальная реализация. Если вам нужно больше, вы можете написать несколько вспомогательных функций, чтобы упростить задачу, но все же вам нужно написать код.
Чтобы внедрить toString, вам нужно больше знать о взаимосвязи между двумя полями.
Я хочу сказать, что class может быть лучше, чем просто interface, потому что он может реализовать эти вещи.
Самая большая проблема, вероятно, заключается в том, что нельзя гарантировать неизменяемость для 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<?, ?>.
Для получения дополнительной информации о том, почему этот синтаксис, см. Убедитесь, что объекты реализуют Comparable и для подробного объяснения Как реализовать общую функцию max(Comparable a, Comparable b) в Java?
Поскольку целые числа Java 32-битные, не будет ли умножение первого хэш-кода на 31 означать, что он переполняется? Не лучше ли выполнить эксклюзивное ИЛИ?
Многие люди публикуют код Pair, который можно использовать в качестве ключа на карте ... Если вы пытаетесь использовать пару в качестве ключа хеширования (обычная идиома), обязательно ознакомьтесь с Table<R,C,V>: http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Table от Guava. Они дают следующий пример использования для ребер графа:
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> во многих частях своего кода. Существуют массивы, деревья и другие реализации как для плотного, так и для разреженного использования, с возможностью указания ваших собственных промежуточных классов карты.
Как это отвечает на вопрос?
Многие люди публикуют код Pair, который можно использовать в качестве ключа в Картах, поэтому я подумал, что опубликую более прямой подход. Я обновлю свой пост для контекста.
Где в предлагаемом вами коде находится парная структура данных?
Пары вообще нет, потому что эта структура данных избавляет от необходимости создавать пару в качестве ключа хеширования. Я решил опубликовать пост из-за других ответов - «это зависит от того, для чего вы хотите использовать [пару]».
Без обид, но я просто не думаю, что это ответ на вопрос. Использование пар для представления ключа / значения в хэше - это особый способ использования один, но, во-первых, это скорее аргумент счетчика для реализации обобщенной парной структуры данных (потому что структура данных 'hash-entry' была бы больше соответствующий - и это именно то, что представляет собой Java HashMap.Entry), и, во-вторых, ваш пример не о ключах и значениях, а о тройки, хранящемся в карте в стиле bindex, и даже структура данных, представляющая тройку, не делается явной в вашем ответе.
Пара в этом случае не для пары (ключ, значение). Это для пары объектов, представляющих уникальный ключ. Это определенно не то же самое, что Map.Entry. Если вы посмотрите на другие ответы, есть много предлагаемых пар, которые реализуют equals и hashCode специально для этой цели. Table не хранит троек. Он отображает пару ключей на значение и имеет функции поиска специально для этой цели.
Для языков программирования, таких как Java, альтернативная структура данных, используемая большинством программистов для представления пары, такой как структуры данных, представляет собой два массива, а доступ к данным осуществляется через один и тот же индекс.
пример: http://www-igm.univ-mlv.fr/~lecroq/string/node8.html#SECTION0080
Это не идеально, поскольку данные должны быть связаны вместе, но также оказывается довольно дешевым. Кроме того, если ваш вариант использования требует хранения координат, лучше создать свою собственную структуру данных.
Что-то подобное есть в моей библиотеке
public class Pair<First,Second>{.. }
Несмотря на синтаксическое сходство, Java и C++ имеют очень разные парадигмы. Написание C++ как Java - это плохой C++, а написание Java как C++ - плохая Java.
С помощью IDE, основанной на отражении, такой как Eclipse, написание обязательной функциональности «парного» класса выполняется быстро и просто. Создайте класс, определите два поля, используйте различные опции меню «Создать XX», чтобы заполнить класс за считанные секунды. Возможно, вам придется очень быстро набрать «compareTo», если вам нужен интерфейс Comparable.
С отдельными параметрами объявления / определения на языке генераторы кода C++ не так хороши, поэтому ручное написание небольших служебных классов отнимает больше времени. Поскольку пара является шаблоном, вам не нужно платить за функции, которые вы не используете, а функция typedef позволяет назначать значимые имена типов коду, поэтому возражения по поводу «отсутствия семантики» на самом деле не актуальны.
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);
}
}
Objects.equal(..) требует библиотеки Guava.
Измените его на Objects.equals(...), который используется в Java с 2011 года (1.7).
com.sun.tools.javac.util.Pair - это простая реализация пары. Его можно найти в jdk1.7.0_51 \ lib \ tools.jar.
Помимо org.apache.commons.lang3.tuple.Pair, это не просто интерфейс.
Однако никто не должен использовать внутренние API JDK.
Вы можете использовать библиотеку AutoValue от Google - https://github.com/google/auto/tree/master/value.
Вы создаете очень маленький абстрактный класс и аннотируете его с помощью @AutoValue, а обработчик аннотаций генерирует для вас конкретный класс, который имеет семантическое значение.
JavaFX (который поставляется в комплекте с Java 8) имеет класс Pair <A, B>
реализация hashCode в javafx.util.Pair может привести к коллизиям в тривиальных случаях. Использование его в HashMap / HashTable по-прежнему будет работать, поскольку Java проверяет равенство значений в дополнение к хэш-кодам, но об этом следует знать.
Это очень стандартная и часто рекомендуемая реализация hashCode. Коллизии следует ожидать кодом Любые, который вызывает hashCode(). Обратите внимание, что сама Java не вызывает этот метод. Это для пользовательского кода, включая библиотеки.
Интерфейс Map.Entry довольно близок к паре C++. Посмотрите на конкретную реализацию, например AbstractMap.SimpleEntry и AbstractMap.SimpleImmutableEntry Первый элемент - это getKey (), а второй - getValue ().
OP уже знает об этом варианте, и он подробно обсуждался.
Вот несколько библиотек, которые для вашего удобства имеют несколько степеней кортежей:
Было упомянуто, что другие библиотеки содержат как минимум кортеж Pair.
В частности, в контексте функционального программирования, в котором используется много структурной типизации, а не номинальной типизации (как указано в принятом ответе), эти библиотеки и их кортежи очень удобны.
Хорошие новости JavaFX имеет пару ключевых значений.
просто добавьте javafx в качестве зависимости и импортируйте javafx.util.Pair;
и использовать просто как в c++.
Pair <Key, Value>
например
Pair <Integer, Integer> pr = new Pair<Integer, Integer>()
pr.get(key);// will return corresponding value
Плохая новость в том, что не все используют JavaFX.
С новой версией Ломбок вы можете скомпилировать этот милый класс:
@Value(staticConstructor = "of") public class Pair <E> {
E first, second;
}
и используйте его как: Pair<Value> pairOfValues = Pair.of(value1, value2);
Collections.singletonMap(left, rigth);
Брайан Гетц, Пол Сандос и Стюарт Маркс объяснить, почему во время сессии QA на Devoxx'14.
Наличие общего парного класса в стандартной библиотеке превратится в технический долг после введения типы значений.
Смотрите также:Есть ли в Java SE 8 пары или кортежи?
Вы можете использовать служебный класс javafx Pair, который служит той же цели, что и pair <> в C++. https://docs.oracle.com/javafx/2/api/javafx/util/Pair.html
Ответ @Andreas Krey действительно хорош. Все, что Java усложняет, вам, вероятно, не следует делать.
По моему опыту, наиболее распространенными применениями Pair были множественные возвращаемые значения из метода и как ЗНАЧЕНИЯ в хэш-карте (часто индексируемой строками).
В последнем случае я недавно использовал структуру данных, примерно такую:
class SumHolder{MyObject trackedObject, double sum};
Есть весь ваш класс «Pair», примерно такой же объем кода, как и общий «Pair», но с преимуществом описательных имен. Его можно определить прямо в используемом методе, что устранит типичные проблемы с общедоступными переменными и т.п. Другими словами, это абсолютно лучше, чем пара для этого использования (из-за названных членов) и не хуже.
Если вам действительно нужна «пара» для ключа хэш-карты, вы, по сути, создаете индекс с двойным ключом. Я думаю, что это может быть тот случай, когда «Пара» значительно меньше кода. На самом деле это не проще, потому что вы могли бы заставить eclipse сгенерировать equals / hash для вашего небольшого класса данных, но это было бы намного больше кода. Здесь пара была бы быстрым решением, но если вам нужен хеш с двойным индексом, кто скажет, что вам не нужен хеш с n-индексом? Решение класса данных будет масштабироваться, а Pair - нет, если вы их не вложите!
Таким образом, второй случай, связанный с возвратом из метода, немного сложнее. Ваш класс нуждается в большей видимости (вызывающий тоже должен это видеть). Вы можете определить его вне метода, но внутри класса точно так же, как указано выше. На этом этапе ваш метод должен иметь возможность возвращать объект MyClass.SumHolder. Вызывающий может видеть имена возвращенных объектов, а не только «пару». Заметим еще раз, что уровень безопасности "По умолчанию" на уровне пакета довольно хорош - он достаточно строгий, чтобы вы не навлекли на себя слишком много проблем. В любом случае, лучше, чем объект "Пара".
Другой случай, который я вижу для использования Pairs, - это общедоступный api с возвращаемыми значениями для вызывающих абонентов за пределами вашего текущего пакета. Для этого я бы просто создал настоящий объект - желательно неизменный. В конце концов, вызывающий объект поделится этим возвращаемым значением, и его изменение может быть проблематичным. Это еще один случай, когда объект Pair хуже - большинство пар нельзя сделать неизменяемыми.
Еще одно преимущество для всех этих случаев - класс java расширяется, моему классу суммы потребовалась вторая сумма и флаг «Создано» к тому времени, когда я закончил, мне пришлось бы выбросить пару и использовать что-то еще, но если пара имела смысл, мой класс с 4 значениями по-прежнему имеет, по крайней мере, столько же смысла.
У вас должна быть аннотация @ lombok.Value, и вы также бесплатно получите equals, hashCode, геттеры, частный финал для полей и метод toString. В этом случае функция Pair <T, Z> явно уступает по качеству, поскольку она плохо обрабатывает примитивные типы.
еще одна лаконичная реализация ломбока
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)
Почему
AbstractMap.SimpleEntryзапутан?