Який еквівалент пари C ++ <L, R> на Java?


671

Чи є вагома причина, чому її немає Pair<L,R>на Яві? Що було б еквівалентом цієї конструкції 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 , Хантер Грацнер наводить деякі аргументи проти наявності Pairконструкції на Java. Основний аргумент полягає в тому, що клас Pairне передає жодної семантики щодо взаємозв'язку двох значень (як ви знаєте, що означають "перше" та "друге"?).

Краща практика - написати дуже простий клас, як той, який запропонував Майк, для кожної програми, яку ви зробили б із Pairкласу. Map.Entryє прикладом пари, яка носить своє значення в своєму імені.

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


143
Грацнер розщеплює волоски. Ми дуже раді повернути одне значення як примітивний або вбудований клас, не вкладаючи його в клас. Якби ми повернули сюжет з десятка елементів, ніхто б не погодився, він мав би свій клас. Десь посередині розташована (нечітка) лінія розмежування. Я думаю, що наші мізки ящірок досить легко впораються з Парими.
Ян

25
Я згоден з Ієном. Java дозволяє повернути Int; це не змушує вас створювати псевдонім для int кожного разу, коли ви його використовуєте. Пари не дуже відрізняються.
Клімент

5
Якби ми могли розпакувати пару безпосередньо до ваших локальних змінних або переслати її до методу, який бере два аргументи, Pair був би корисним класом. Оскільки ми не можемо розпакувати його так, створення значущого класу та збереження значень разом не виглядає занадто погано. І якщо ви дійсно хочете пари, незважаючи на обмеження, завжди є Object [2] + casts :-)
marcus

Річ у тім, що якщо ви не погоджуєтесь з Gratzner, значить, є пара реалізацій у кількох місцях. Apache Commons і Guava мають його IIRC. Використовуйте ці. Але поміщати щось у основні бібліотеки Java означає, що це шляхетний та затверджений спосіб робити речі (з великої літери), і оскільки люди не згодні з цим, ми не повинні його розміщувати там. У старих гілочках достатньо суворості, як би там не було, зайве не ставити більше.
Haakon Løtveit

1
@Dragas Коли мені потрібна пара значень, то це не Java ... серйозно?
idclev 463035818

156

Це Java. Ви повинні створити свій власний індивідуальний клас з описовими назвами класів та полів, і не заперечувати, що ви будете винаходити колесо, записуючи hashCode () / equals () або впроваджуючи Порівнювані знову і знову.


61
Це не відповідає на питання "чому". (Якщо ви не вважаєте, що це "ява", відповідь)
Микита Рибак

127
+1 - глузування з багатослов'я Яви. -1 за те, що насправді не відповів на питання.
Bennett McElwee

19
Java-глузування було б чудово, якби ви вказали на Apache Commong Lang, який містить клас пари.
хайлем

6
Або ви просто можете скористатисяSimpleImmutableEntry
CurtainDog

33
Перше речення відповідає на питання "чому?". Це Java, і все.
мастерів

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 () може кинути NullPointerExceptions. Це краще: повернення "(" + перший + "," + другий + ")";
Juha Syrjälä

6
Також позначте пару як "остаточну" або змініть перший рядок, рівний на ", якщо (інше! = Null && this.getClass () == other.getClass ())"
саргаз

8
Вибачте за випадкове запитання про нобі, але чому ви маєте заклик до super () у конструкторі?
Ібрагім

6
@Ibrahim: У цьому випадку це зайве --- поведінка точно така сама, якби ти super()вийшов. Зазвичай я б просто відключити його, якщо це необов'язково, як це є тут.
Кріс Єстер-Янг

53

Найкоротший пара , що я міг придумати , полягає в наступному, використовуючи Ломбок :

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

Він має всі переваги відповіді від @arturh (крім порівнянності), тобто hashCode, equals, toStringі статичний «конструктор».


Вишуканий! Сподобалось!
Ахмет Іпкін


31

Ще один спосіб втілити пару з.

  • Публічні непорушні поля, тобто проста структура даних.
  • Порівнюваний
  • Простий хеш і дорівнює.
  • Проста фабрика, тому вам не доведеться надавати типи. наприклад 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 .
Jarek Przygódzki

7
Ви в якийсь момент звертаєтесь o1до них Comparable, хоча нічого не говорить про те, що він реально реалізує цей інтерфейс. Якщо це вимога, FIRSTпараметр типу повинен бути FIRST extends Comparable<?>.
G_H

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

3
31 - погана константа для hashCode. Наприклад, якщо ви використовуєте HashMap, накреслений Pair <Integer, Integer> для двовимірної карти, ви отримаєте багато зіткнень. Наприклад (a * 65497) ^ b краще підходить.
Michał Zieliński

1
@MarioCarneiro ^ є xor, а не влада
Michał Zieliński

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 більше, ніж я б використав.
Боан

7
@Boann: Гаразд, я залишаюся виправленим. Раніше я користувався Pairі міг уявити, що користуюся, Tripletможливо, раз на 50 років. Тепер я використовую Lombok і створюю крихітний 4-рядковий клас щоразу, коли мені потрібна пара. Тож "10 занадто багато" - це точно.
maaartinus

5
Чи потрібен нам Bottom (0 element)клас? :)
Earth Engine

2
Нічого це некрасиво. Я знаю, що вони намагаються зробити це явним, але кортеж із перевантаженими парами, як у 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
"Типовою причиною цього є перегляд карт". Дійсно?
Bennett McElwee

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()можуть дати непослідовні результати для тієї ж пари після того, як вона буде вставлена ​​в колекцію, наприклад (це призведе до невизначеної поведінки , див. Визначення рівних за змінами полів ). Для конкретного (не загального) класу пар програміст може забезпечити незмінність, ретельно вибираючи 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<?, ?>.

Для отримання додаткової інформації про те, чому цей синтаксис див. Переконайтеся, що об’єкти реалізують Порівнянні та для детального пояснення Як реалізувати загальну max(Comparable a, Comparable b)функцію в Java?


Оскільки Java Integers 32-бітний, чи не множення першого хеш-коду на 31 означає, що він переповнює? Не було б краще виконати ексклюзивне АБО?
День

@ Не соромтеся редагувати Я відійшов від Java :)
Mr_and_Mrs_D

5

На мою думку, у Java немає пари, оскільки, якщо ви хочете додати додаткову функціональність безпосередньо на парі (наприклад, Порівняльна), ви повинні зв'язати типи. У C ++ нас просто не хвилює, і якщо типів, що складають пару, немає operator <, pair::operator <також не буде компілюватися.

Приклад порівнянного без обмеження:

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

Приклад порівняння з перевіркою часу компіляції на наявність аргументів типу:

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

Це добре, але цього разу ви не можете використовувати непорівнянні типи як аргументи типів у парі. Можна використовувати безліч компараторів для пари в якомусь класі корисності, але люди C ++ можуть не отримати його. Інший спосіб - писати багато класів в ієрархії типів з різними межами на аргументах типів, але занадто багато можливих меж та їх комбінацій ...


5

JavaFX (який постачається в комплекті з Java 8) має клас Pair <A, B>


1
Реалізація хеш - код в javafx.util.Pair може призвести до сутичок на тривіальних випадках. Використання його в HashMap / HashTable все одно буде працювати, оскільки Java перевіряє рівність значень на додаток до хеш-кодів, але це щось, що слід пам’ятати.
sffc

Це дуже стандартна і часто рекомендована хеш-кодова реалізація. Зіткнення слід очікувати будь-яким кодом, який дзвонить hashCode(). Зауважте, що сама Java не називає цей метод. Він призначений для коду користувача, включаючи бібліотеки.
AndrewF

5

Як уже багато хто з них заявляв, це дійсно залежить від випадку використання, корисний чи клас 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 не ввімкнено . Це навіть коротше, ніж моє власне рішення Ломбок, якщо ви вже використовуєте 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
Michał Dobi Dobrzański

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

Це досить легке рішення, і воно відповідає на питання "Що таке семантика а Pair<L,R>?". Відповідь: це побудова інтерфейсу з двома (можуть бути різними) типами, і у нього є методи повернення кожного з них. Вам належить додати ще семантичне значення. Наприклад, якщо ви використовуєте позиції і дійсно хочете , щоб вказати його в вас коді, ви можете визначити PositionXі PositionYщо містить Integer, щоб зробити вгору Pair<PositionX,PositionY>. Якщо JSR 308 доступний, ви також можете використовуватиPair<@PositionX Integer, @PositionY Ingeger> для спрощення цього.

EDIT: Одне, що я маю зазначити тут, - це те, що вищевказане визначення прямо пов'язує назву параметра типу та ім'я методу. Це відповідь на ті аргументи, що а Pair- це відсутність смислової інформації. Власне, метод getLозначає "дайте мені елемент, який відповідає типу типу параметра L", який щось означає.

EDIT: Ось простий клас корисності, який може полегшити життя:

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», щоб заповнити клас за лічені секунди. Можливо, вам доведеться швидко набрати "сравнениеTo", якби вам потрібен інтерфейс "Порівнюваний".

Окремі параметри декларування / визначення на мовній генераторі коду 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

ще одна нетривала реалізація lombok

import lombok.Value;

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

1

Я помітив, що всі реалізації пар, що розгортаються тут, атрибутивно означають порядок двох значень. Коли я думаю про пару, я думаю про поєднання двох предметів, у яких порядок двох не має ніякого значення. Ось моя реалізація не упорядкованої пари, із 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.
jpangamarca

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")з вашим кодом?
Мав QUIT - Anonymous-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) поскаржиться. Традиційний new Pair<Object, Object>("abc", "def")був найнадійнішим у моїх експериментах.
Мав QUIT - Anonymous-Mousse
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.