Что эквивалентно паре C ++ <L, R> в Java? - PullRequest
638 голосов
/ 01 октября 2008

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

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

Ответы [ 33 ]

2 голосов
/ 03 февраля 2010

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

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

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

2 голосов
/ 06 октября 2014

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

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

2 голосов
/ 18 марта 2013

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

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

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

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

public class Pair<First,Second>{.. }
2 голосов
/ 22 октября 2015

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

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

В других библиотеках упоминается как минимум кортеж Pair.

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

1 голос
/ 05 мая 2014

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

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

1 голос
/ 06 января 2012

Если кому-то нужна очень простая и легкая в использовании версия, я сделал ее доступной на https://github.com/lfac-pt/Java-Pair. Кроме того, улучшения приветствуются!

1 голос
/ 29 сентября 2010

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

1 голос
/ 19 ноября 2018

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

import lombok.Value;

@Value(staticConstructor = "of")
public class Pair<F, S> {
    private final F first;
    private final S second;
}
1 голос
/ 20 апреля 2011

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

    }

}

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

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

0 голосов
/ 18 июля 2017

Ответ @Andreas Krey действительно хороший. Все, что Java делает трудным, вы, вероятно, не должны делать.

В моем опыте наиболее часто использовались пары для получения нескольких возвращаемых значений из метода и в качестве ЗНАЧЕНИЙ в хэш-карте (часто индексируемой строками).

В последнем случае я недавно использовал структуру данных, что-то вроде этого:

class SumHolder{MyObject trackedObject, double sum};

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

Если вы действительно хотите «пару» для ключа хэш-карты, вы, по сути, создаете индекс двойного ключа. Я думаю, что это может быть один случай, когда «Пара» значительно меньше кода. На самом деле это не так просто, потому что вы могли бы генерировать eclipse equals / hash для своего небольшого класса данных, но это было бы намного больше кода. Здесь пара будет быстрым решением, но если вам нужен хэш с двойным индексированием, кто скажет, что вам не нужен хеш с n индексированием? Решение для класса данных будет расширяться, а для пары - только если вы их вложите!

Итак, второй случай, возвращающийся из метода, немного сложнее. Ваш класс нуждается в большей видимости (вызывающий должен видеть это тоже). Вы можете определить его вне метода, но внутри класса точно так же, как указано выше. На этом этапе ваш метод должен иметь возможность вернуть объект MyClass.SumHolder. Вызывающая сторона видит имена возвращаемых объектов, а не просто «Пара». Еще раз обратите внимание, что «Уровень безопасности» на уровне пакета довольно хороший - он достаточно ограничительный, чтобы не доставлять себе слишком много хлопот. В любом случае, лучше, чем объект "Pair".

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

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

...