Что эквивалентно C++ Pair <L, R> в Java?

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

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

Почему AbstractMap.SimpleEntry запутан?

CurtainDog 28.06.2012 05:36

См. Мой ответ stackoverflow.com/a/16089164/173149, где я без проблем использую AbstractMap.SimpleImmutableEntry (сложный пример).

gavenkoa 18.04.2013 21:10

SimpleEntry имеет три метода, не унаследованных от Object. Почему он должен быть «запутанным»?

haansn08 08.08.2014 23:04

Из-за namig произвольное наименование одного ключа и одного значения.

Enerccio 15.09.2014 18:00

См. Также stackoverflow.com/questions/6271731/…

Raedwald 17.12.2014 00:22

Если вы используете Java 8, import javafx.util.Pair решит ваши проблемы.

sffc 23.10.2015 14:52

@sffc JavaFX отсутствует ни в одном из путей к классам по умолчанию в JDK7, его использование требует добавления библиотек времени выполнения JFX вручную.

Cord Rehn 27.05.2016 22:40

@Enerccio: Итак, вы на самом деле утверждаете, что «первое» и «второе» не произвольно, а «ключ» и «значение» - нет? Тогда это одна из веских причин отсутствия такого класса в SDK. Будет вечный спор о «правильном» именовании.

fdreger 11.09.2016 22:08
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
690
8
473 309
35
Перейти к ответу Данный вопрос помечен как решенный

Ответы 35

Это зависит от того, для чего вы хотите его использовать. Типичная причина для этого - перебирать карты, для чего вы просто делаете это (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());
}

Я не уверен, что в этом случае поможет специальный класс :)

Nikita Rybak 23.04.2011 20:49

«Типичная причина для этого - перебирать карты». Действительно?

Bennett McElwee 29.11.2011 02:02
Ответ принят как подходящий

В нить на comp.lang.java.help Хантер Грацнер приводит некоторые аргументы против наличия конструкции Pair в Java. Главный аргумент заключается в том, что класс Pair не передает никакой семантики относительно отношений между двумя значениями (как узнать, что означают «первое» и «второе»?).

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

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

Гратцнер секет волосы. Мы вполне счастливы вернуть одно значение как примитивный или встроенный класс, не инкапсулируя его в класс. Если бы мы вернули кортеж из дюжины элементов, никто бы не возражал, у него должен быть свой собственный класс. Где-то посередине проходит (нечеткая) разделительная линия. Думаю, наш мозг ящерицы легко справляется с парами.

Ian 25.10.2016 12:31

Я согласен с Яном. Java позволяет возвращать int; он не заставляет вас создавать псевдоним для int каждый раз, когда вы его используете. Пары не сильно отличаются.

Clément 04.12.2016 21:20

Если бы мы могли распаковать пару непосредственно в ваши локальные переменные или передать ее методу, который принимает два аргумента, Pair была бы полезным классом. Поскольку мы не можем распаковать его таким образом, создание значимого класса и сохранение значений вместе не выглядит так уж плохо. И, если вам действительно нужна пара, несмотря на ограничения, всегда есть Object [2] + cast :-)

marcus 14.12.2016 03:41

Дело в том, что если вы не согласны с Грацнером, то в нескольких местах есть реализации Pair. У Apache Commons и Guava есть IIRC. Используйте те. Но помещать что-то в основные библиотеки Java означает, что это благородный и одобренный способ делать вещи (с заглавными буквами), и, поскольку люди не согласны с этим, мы не должны помещать это туда. В старых библиотеках уже достаточно хлама, давайте не будем добавлять туда лишнего.

Haakon Løtveit 11.08.2017 12:38

Я не понимаю, что такое java "благородный и одобренный способ". Допустим, я определенно не хочу создавать новый класс, тогда самая простая альтернатива - использовать массив с двумя записями, который намного хуже пары с точки зрения передачи семантики.

largest_prime_is_463035818 05.04.2018 12:29

@ user463035818 У каждого языка есть свое предназначение. Мне кажется, что вам вообще не нужна java, а нужен какой-то другой язык, который не так ограничен и позволяет вам взламывать.

Dragas 17.02.2019 10:39

@Dragas Когда мне нужна пара значений, тогда это не Java ... серьезно?

largest_prime_is_463035818 17.02.2019 12:59

Мы все время используем карты и списки в Java, если семантика была наиболее важным аспектом, тогда карты и списки (являющиеся общими типами данных) также не скрывают смысл, Map <String, Int> и Pair of Pair < String, int> выглядит похоже

Mushtaq Jameel 26.02.2019 15:34

Совместимый с 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;
    }
}

Вероятно, вы захотите удалить сеттеры и сделать первый и второй финальными, таким образом сделав пару неизменной. (Если кто-то изменил компоненты после использования их в качестве хеш-ключа, произойдут странные вещи).

Thilo 28.08.2009 13:21

return "(" + first.toString () + "," + second.toString () + ")" в методе toString () может вызывать исключения NullPointerExceptions. Это лучше: return "(" + first + "," + second + ")";

Juha Syrjälä 01.09.2009 12:31

Кроме того, либо пометьте пару как «окончательную», либо измените первую строку равенства на 'if (other! = Null && this.getClass () == other.getClass ())'

sargas 29.05.2010 04:45

Извините за случайный вопрос nooby, но почему у вас есть вызов super () в конструкторе?

Ibrahim 16.06.2010 23:09

@Ibrahim: В данном случае это лишнее - поведение будет точно таким же, если вы вытащили super(). Обычно я бы просто отрезал его, если это необязательно, как здесь.

Chris Jester-Young 30.09.2010 22:58

Это необязательно, но только в том смысле, что компилятор вставит вызов за вас, если вы не напишете его явно. Даже когда суперклассом является Object, существует метод java/lang/Object/<init>()V, и его нужно вызывать.

Daniel Lubarov 04.07.2011 06:38

&& otherPair.first != null и && otherPair.second != null являются избыточными (потому что this.first/sec == otherPair.first/sec был бы истинным, если бы оба были нулевыми)

Mr_and_Mrs_D 01.12.2012 18:20

В строке, где вы пишете Pair otherPair = (Pair) other, Eclipse дает мне дружеское предупреждение: References to generic type Pair<A, B> should be parameterized. И в обоих случаях предлагается поменять Pair на Pair<?, ?>. Все хорошо?

pratnala 12.03.2014 19:28

Object.equals? Это может быть проще, чем вещи if (x == y) / if (x! = Null && x.equals (y)).

Paul Stelian 14.11.2019 00:57

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

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

Это то же самое, что и Tuple в Haskell

Теперь я могу сказать, что использование Pair <A, B> делает код менее информативным, а реализация специальных объектов вместо использования Pair намного лучше.

Illarion Kovalchuk 18.02.2010 16:02

Лучше или хуже. Представьте, что у вас есть функция, объединяющая два своих аргумента (например, слияние графиков в один), и вам необходимо кэшировать ее. Здесь Pair оптимален, так как особой семантики нет. Иметь ясное название для ясной концепции - это хорошо, но искать имя, в котором хорошо работают «первый» и «второй», - нет.

maaartinus 03.11.2012 23:51

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

Насмешка над Java была бы хорошей, если бы вы указали на Apache Common Lang, который содержит класс Pair.

haylem 29.02.2012 13:10

Или вы можете просто использовать SimpleImmutableEntry

CurtainDog 28.06.2012 05:40

Наиболее распространенные IDE сгенерируют для вас соответствующий HashCode () / equals.

java-addict301 13.11.2017 18:12

Я согласен. «Потому что это Java» - хороший ответ. Помните, что язык Java намеренно лишен определенных (кашлял C++) функций, которые сбивают с толку среднего программиста. Вот почему Java не позволяет вам переопределять операторы. Он также не допускает множественного наследования. Суть в том, что если какой-нибудь глупый программист, вероятно, злоупотребит этим, то Java сделает это трудным.

John Henckel 15.11.2017 18:01

@JohnHenckel Это как инструктор, который задает простые вопросы на экзамене, потому что он хочет, чтобы все добились успеха. Тупой.

stillanoob 24.07.2018 18:16

@TimGoodman Gratzner в спартанской одежде кричит, когда пинает нового ученика в яму стандартных имен

Swift - Friday Pie 27.09.2020 17:19

Другой способ реализовать 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.

Jarek Przygódzki 05.04.2011 23:35

В какой-то момент вы преобразуете o1 в Comparable, хотя ничто не указывает на то, что он действительно реализует этот интерфейс. Если это требование, параметр типа FIRST должен быть FIRST extends Comparable<?>.

G_H 20.04.2011 21:36

Я не сторонник Java, так что, пожалуйста, простите меня за мое невежество, но о каких вспомогательных классах вы думали в комментариях TODO?

user220878 29.03.2012 18:41

Может быть любым, например CompareableUtils или Comparables.

Peter Lawrey 29.03.2012 23:08

31 - плохая константа для hashCode. Например, если вы используете HashMap с ключом Pair <Integer, Integer> для 2D-карты, вы получите много коллизий. Например, (a * 65497) ^ b подойдет лучше.

Michał Zieliński 29.06.2013 22:58

@ MichałZieliński Я думаю, что 31 существует, потому что 31 - это любимое простое число Eclipse (появляется, когда вы используете «сгенерировать hashCode () и equals ()». Однако вычисление мощности было бы слишком дорогим для такого универсального класса, как этот.

Mario Carneiro 12.01.2016 22:18

@MarioCarneiro Java String.hashCode () used 31 имеет простое умножение задолго до запуска eclipse.

Peter Lawrey 12.01.2016 23:39

@MarioCarneiro ^ - это xor, а не сила

Michał Zieliński 14.01.2016 00:03

@PeterLawrey ситуация со строками другая - размер алфавита обычно меньше (char vs int)

Michał Zieliński 14.01.2016 00:03

Простой способ Object [] - может использоваться как кортеж любого измерения

Любое измерение, да. Но: громоздко в создании и небезопасно по типу.

Michael Piefel 15.12.2011 12:57

На мой взгляд, в 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 и статический «конструктор».

Отлично! Понравилось это!

Ahmet Ipkin 29.11.2018 20:46

знакомство с ломбоком, бесценно.

Orwellophile 12.10.2020 19:11

Как уже заявляли многие другие, действительно зависит от варианта использования, полезен ли класс 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.

Michael Piefel 13.03.2012 17:59

Я заметил, что все реализации 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&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;

    }

}

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

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

на самом деле, проверьте внизу каждой страницы: «пользовательские материалы под лицензией cc-wiki»

amara 30.04.2011 21:44

Ах, я этого не заметил. Спасибо за предупреждение. В этом случае используйте код по своему усмотрению в рамках этой лицензии.

G_H 02.05.2011 17:47

Речь идет об эквивалентной паре C++ - которая упорядочена. Также я думаю, что до тех пор, пока у кого-то есть ссылка на объект Pair, и они являются изменяемыми, вставка Pairs в коллекции может привести к неопределенному поведению.

Mr_and_Mrs_D 02.12.2012 18:26

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") с вашим кодом?

Has QUIT--Anony-Mousse 09.12.2011 15:01

вы можете заменить статический метод следующим образом: @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); }, но я не знаю, является ли это хорошей практикой

Bastiflew 09.12.2011 15:31

Нет, это, скорее всего, только усугубит ситуацию. По моему опыту, по крайней мере один из компиляторов (попробуйте java6, java7, javadoc и eclipse java compiler) будет жаловаться. Традиционный new Pair<Object, Object>("abc", "def") оказался самым надежным в моих экспериментах.

Has QUIT--Anony-Mousse 10.12.2011 17:57

Если кому-то нужна чрезвычайно простая и легкая в использовании версия, я сделал ее доступной на 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 классов больше, чем я мог себе представить.

maaartinus 03.11.2012 23:43

Они не поставляются со стандартной библиотекой java, не так ли?

Bengalaa 21.12.2013 02:06

@maaartinus По крайней мере, на 10 больше, чем я бы использовал.

Boann 30.01.2014 20:07

@Boann: Хорошо, я остаюсь исправленным. Раньше я использовал Pair и мог представить, что буду использовать Triplet раз в 50 лет. Теперь я использую Lombok и создаю крошечный класс из 4 строк каждый раз, когда мне нужна пара. Так что «10 многовато» - это точно.

maaartinus 30.01.2014 20:16

Нужен ли нам класс Bottom (0 element)? :)

Earth Engine 24.09.2015 03:17

Вау, это уродливо. Я знаю, что они пытаются сделать это явным, но кортеж с перегруженными параметрами, как в C#, был бы лучше.

arviman 19.06.2016 14:12

Не могу поверить, что кто-то на самом деле нашел время, чтобы это написать.

jpangamarca 20.09.2016 19:06

Спасибо за эту ссылку. На мой взгляд это полезно. Большой плюс в том, что у этой библиотеки нет других зависимостей.

MAGx2 15.11.2017 14:26

Унарный (не нулевой!) Unit. А зачем? Они даже не поняли латынь, почему это «Пара», а не «Дуплет» или что-то в этом роде ... Это просто глупо.

Andrey Tyukin 15.10.2018 14:31

Вы знаете ... Вам просто нужен Pair, а затем вы можете написать Pair<A, Pair<B, Pair<C, Pair<D, Pair<E, Pair<F, Pair<G, Pair<H, Pair<I, J>>>>>>>>>, когда вам понадобится Decade.

tsh 21.07.2020 09:38

@tsh: Вы сказали Headache<...>?

Rainning 02.01.2021 00:04

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?

sdgfsdh 01.06.2017 14:46

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

Earth Engine 01.06.2017 15:37

Чтобы внедрить toString, вам нужно больше знать о взаимосвязи между двумя полями.

Earth Engine 01.06.2017 15:42

Я хочу сказать, что class может быть лучше, чем просто interface, потому что он может реализовать эти вещи.

sdgfsdh 01.06.2017 16:04

Самая большая проблема, вероятно, заключается в том, что нельзя гарантировать неизменяемость для 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 означать, что он переполняется? Не лучше ли выполнить эксклюзивное ИЛИ?

Dan 15.03.2020 03:20

Многие люди публикуют код 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> во многих частях своего кода. Существуют массивы, деревья и другие реализации как для плотного, так и для разреженного использования, с возможностью указания ваших собственных промежуточных классов карты.

Как это отвечает на вопрос?

jogojapan 28.12.2012 08:47

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

Andrew Mao 28.12.2012 19:19

Где в предлагаемом вами коде находится парная структура данных?

jogojapan 28.12.2012 19:32

Пары вообще нет, потому что эта структура данных избавляет от необходимости создавать пару в качестве ключа хеширования. Я решил опубликовать пост из-за других ответов - «это зависит от того, для чего вы хотите использовать [пару]».

Andrew Mao 28.12.2012 22:04

Без обид, но я просто не думаю, что это ответ на вопрос. Использование пар для представления ключа / значения в хэше - это особый способ использования один, но, во-первых, это скорее аргумент счетчика для реализации обобщенной парной структуры данных (потому что структура данных 'hash-entry' была бы больше соответствующий - и это именно то, что представляет собой Java HashMap.Entry), и, во-вторых, ваш пример не о ключах и значениях, а о тройки, хранящемся в карте в стиле bindex, и даже структура данных, представляющая тройку, не делается явной в вашем ответе.

jogojapan 29.12.2012 08:35

Пара в этом случае не для пары (ключ, значение). Это для пары объектов, представляющих уникальный ключ. Это определенно не то же самое, что Map.Entry. Если вы посмотрите на другие ответы, есть много предлагаемых пар, которые реализуют equals и hashCode специально для этой цели. Table не хранит троек. Он отображает пару ключей на значение и имеет функции поиска специально для этой цели.

Andrew Mao 30.12.2012 00:29

Для языков программирования, таких как 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.

Markus L 31.10.2017 14:04

Измените его на Objects.equals(...), который используется в Java с 2011 года (1.7).

AndrewF 15.10.2018 21:38

com.sun.tools.javac.util.Pair - это простая реализация пары. Его можно найти в jdk1.7.0_51 \ lib \ tools.jar.

Помимо org.apache.commons.lang3.tuple.Pair, это не просто интерфейс.

Однако никто не должен использовать внутренние API JDK.

jpangamarca 20.09.2016 19:08

Вы можете использовать библиотеку AutoValue от Google - https://github.com/google/auto/tree/master/value.

Вы создаете очень маленький абстрактный класс и аннотируете его с помощью @AutoValue, а обработчик аннотаций генерирует для вас конкретный класс, который имеет семантическое значение.

JavaFX (который поставляется в комплекте с Java 8) имеет класс Pair <A, B>

реализация hashCode в javafx.util.Pair может привести к коллизиям в тривиальных случаях. Использование его в HashMap / HashTable по-прежнему будет работать, поскольку Java проверяет равенство значений в дополнение к хэш-кодам, но об этом следует знать.

sffc 23.10.2015 14:51

Это очень стандартная и часто рекомендуемая реализация hashCode. Коллизии следует ожидать кодом Любые, который вызывает hashCode(). Обратите внимание, что сама Java не вызывает этот метод. Это для пользовательского кода, включая библиотеки.

AndrewF 15.10.2018 21:53

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

OP уже знает об этом варианте, и он подробно обсуждался.

Keegan 15.05.2015 20:42

Вот несколько библиотек, которые для вашего удобства имеют несколько степеней кортежей:

  • JavaTuples. Кортежи от 1 до 10 - это все, что в нем есть.
  • JavaSlang. Кортежи от 0 до 8 и множество других функциональных вкусностей.
  • jOOλ. Кортежи от 0 до 16 и некоторые другие функциональные возможности. (Отказ от ответственности, я работаю в сопровождающей компании)
  • Функциональная Java. Кортежи от 0 до 8 и многое другое.

Было упомянуто, что другие библиотеки содержат как минимум кортеж 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.

Michał Dobi Dobrzański 05.05.2020 22:06

С новой версией Ломбок вы можете скомпилировать этот милый класс:

@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> явно уступает по качеству, поскольку она плохо обрабатывает примитивные типы.

Haakon Løtveit 11.08.2017 12:37

еще одна лаконичная реализация ломбока

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)

Другие вопросы по теме