Что является эквивалентом пары C ++ <L, R> в Java?


671

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

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


7
Почему AbstractMap.SimpleEntryзапутанный?
CurtainDog

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

2
Смотрите также stackoverflow.com/questions/6271731/...
Raedwald

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

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

Ответы:


400

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

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

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


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

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

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

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

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

156

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


61
Это не отвечает на вопрос «почему». (Если вы не считаете «это Java» ответом)
Никита Рыбак

127
+1 за издевательство над многословностью Java. -1 за то, что не ответил на вопрос.
Беннет МакЭлви,

19
Java-издевательство было бы хорошо, если бы вы указали на Apache Commong Lang, который содержит класс Pair.
Хайлем

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

33
Первое предложение отвечает на вопрос «почему?». Это Java, и все тут.
masterziv

103

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

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

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

6
Также пометьте пару как «окончательную» или измените первую строку на «if (other! = Null && this.getClass () == other.getClass ())»
sargas

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

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

53

Самая короткая пара, которую я смог придумать, это следующая, используя Lombok :

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

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


Острота! Понравилось!
Ахмет Ипкин


31

Еще один способ реализации 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 + ')';
        }
    }

10
Мне нравится статический фабричный метод of. Это напоминает неизменные коллекции Google Guava .
Ярек Пшигодзки

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

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

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

1
@MarioCarneiro ^ это xor, а не власть
Михал Зелиньски

27

Как насчет 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)

6
Забавно, но есть как минимум на 5 классов больше, чем я мог себе представить.
Maaartinus

3
@maaartinus Как минимум на 10 больше, чем я бы использовал.
Boann

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

5
Нужен ли нам Bottom (0 element)класс? :)
Земной двигатель

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

12

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

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

31
«Типичная причина для этого - перебирать карты». В самом деле?
Беннет МакЭлви,

12

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

1
Objects.equal(..)требует библиотеки Guava.
Маркус Л

3
Измените его на тот, Objects.equals(...)который был в Java с 2011 года (1.7).
AndrewF

9

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

@ Дэн, не стесняйтесь редактировать, я отошел от Java :)
Mr_and_Mrs_D

5

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


5

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


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

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

5

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

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

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

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

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

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

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


Обновленный URL-адрес: commons.apache.org/lang/api-release/index.html?org/apache/… поскольку commons-lang3 находится вне бета-версии. Это даже короче, чем мое собственное решение Lombok, если вы уже используете commons-lang 3.
Michael Piefel


5

Хорошие новости 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
Михал Доби Добжаньский

4

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


1
ОП уже знает об этой опции, и она обсуждалась подробно.
Киган


3

В соответствии с природой языка 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; }
}

Это довольно легкое решение, и оно отвечает на вопрос «Что такое семантика a Pair<L,R>?». Ответ таков: это интерфейсная сборка с двумя (может быть разными) типами, и у нее есть методы для возврата каждого из них. Это зависит от вас, чтобы добавить дополнительную семантику к нему. Например, если вы используете позиции и действительно хотите , чтобы указать его в вас коде, вы можете определить 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

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

Для реализации toStringвам нужно больше знаний об отношениях между двумя полями.
Земной двигатель

Моя точка зрения заключается в том, что предоставление classможет быть лучше, чем просто interfaceпотому, что оно может реализовать эти вещи.
sdgfsdh

3

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

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

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


2

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

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

Это так же, как кортеж Хаскелла


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

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

2

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


2
Любое измерение, да. Но: неуклюжий в создании и небезопасный.
Майкл Пифель

2

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

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

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

У меня есть что-то подобное в моей библиотеке

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

2

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

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


2

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

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

Другие библиотеки были упомянуты, чтобы содержать по крайней мере Pairкортеж.

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


2

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

Наличие общего класса пар в стандартной библиотеке превратится в технический долг, как только будут введены типы значений .

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


2

другая краткая реализация ломбок

import lombok.Value;

@Value(staticConstructor = "of")
public class Pair<F, S> {
    private final F first;
    private final S second;
}

1

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

    }

}

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

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


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

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

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


1

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

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


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

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

Неизменный, только пара!


Ух ты. Другой? Попробуйте использовать свои с более сложными обобщениями - в какой-то момент он не сможет вывести соответствующие типы. Кроме того, должно быть возможно следующее: Pair<Object, Object> pair = Pair.createPair("abc", "def")но я полагаю, что нужно писать Pair.createPair((Object)"abc", (Object)"def")с вашим кодом?
ВЫЙТИ - Anony-Mousse

Вы можете заменить статический метод следующим образом: @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

Нет, это, скорее всего, только испортит ситуацию. По моему опыту, по крайней мере один из компиляторов (попробуйте java6, java7, javadoc и eclipse java compiler) будет жаловаться. Традиционный new Pair<Object, Object>("abc", "def")был самым надежным в моих экспериментах.
ВЫЙТИ - Anony-Mousse
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.