2008-10-01 12 views
581

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

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

ответ

355

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

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

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

13

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

Я не уверен, что пользовательский класс помог бы в этом случае :) – 2011-04-23 16:49:25

+23

«Типичная причина для этого - перебрать карты». В самом деле? – 2011-11-28 22:02:03

98

HashMap совместимый класс Pair:

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

Возможно, вы захотите удалить сеттеры и сделать первый и второй финал, сделав пару неизменной. (Если кто-то изменил компоненты после использования их в качестве хеш-ключа, будут происходить странные вещи). – Thilo 2009-08-28 09:21:06

+20

return "(" + first.toString() + "," + second.toString() + ")" в toString() метод может вызывать NullPointerExceptions. Это лучше: return "(" + first + "," + second + ")"; – 2009-09-01 08:31:29

+5

Также отметим пару как «final» или измените первую строку равным «if (other! = Null && this.getClass() == other.getClass()) ' – sargas 2010-05-29 00:45:07

2

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

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

Это то же самое, что и у Haskell's Tuple

+1

Теперь я могу сказать, что использование пары делает код менее информативным, а реализация специальных объектов вместо использования пары намного лучше – 2010-02-18 13:02:45

131

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

+46

Это не отвечает на вопрос «почему». (Если вы не считаете «это java» ответом) – 2011-04-23 16:48:33

+91

+1 для насмешливой верстки Java. -1 для ответа на вопрос не на самом деле. – 2011-11-28 22:00:25

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 **. – 2011-04-05 19:35:16

+7

Вы в какой-то момент бросаете `o1` в` Comparable`, хотя ничто не указывает, что он действительно реализует этот интерфейс. Если это требование, параметр типа `FIRST` должен быть` FIRST extends Comparable `. – 2011-04-20 17:36:49

1

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

+1

Любое измерение, да. Но: громоздко создавать, а не безопасно. – 2011-12-15 08:57:14

4

На моем взгляде, нет пара в Java, потому что, если вы хотите добавить дополнительную функциональность непосредственно на пару (например, Comparable), вы должны связать типы. В 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)); 

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

46

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

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

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

5

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

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» – naiad 2011-04-30 17:44:07

+0

А, я этого не заметил. Спасибо за хедз-ап. В этом случае используйте код, как вы считаете подходящим для этой лицензии. – 2011-05-02 13:47:47

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

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

+0

О, ничего себе. Еще один? Попробуйте использовать ваши с более сложными Generics - в какой-то момент он не сможет вывести соответствующие типы. Кроме того, должно быть возможно следующее: `Пара Пара = Pair.createPair (" abc "," def ")` , но я полагаю, что нужно написать `Pair.createPair ((Object)" abc " , (Object) "def") `с вашим кодом? – 2011-12-09 11:01:40

+0

вы можете заменить статический метод с помощью этого: \t `@SuppressWarnings ("непроверенные") \t общественности статической пара createPair (ключ K, V значение) { \t \t возврата ключа новая пара ((X), (Y)); \t} ` , но я не знаю, хорошая ли это практика – Baptiste 2011-12-09 11:31:18

+0

Нет, это, скорее всего, только испортит все больше. По моему опыту, по крайней мере один из компиляторов (попробуйте java6, java7, javadoc и java-компилятор eclipse) будет жаловаться. Традиционная «новая пара» («abc», «def») `была самой надежной в моих экспериментах. – 2011-12-10 13:57:21

1

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

26

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

Самая большая проблема, вероятно, что один не может гарантировать неизменность на А и B (см. How to ensure that type parameters are immutable), поэтому hashCode() может приводить непоследовательные результаты для той же пары после того, как вставляется в коллекцию (это даст неопределенное поведение, см. Defining equals in terms of mutable fields). Для конкретного (не общего) класса Pair программист может обеспечить неизменность, тщательно выбирая A и B, чтобы быть неизменными.

В любом случае, очистка предупреждений общего характера от ответа Питера Лаврея (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<?, ?>.

Для получения дополнительной информации о том, почему этот синтаксис см Ensure that objects implement Comparable и для детального объяснения How to implement a generic max(Comparable a, Comparable b) function in Java?

0

Многие люди вывешивают Pair код, который можно использовать как ключ в карте ... Если вы пытаетесь использовать пару как хэширующий ключ (общая идиома), обязательно проверьте Guava Table<R,C,V>: http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Table. Они дают следующий пример использования, для ребер графа:

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 Maps два ключа к одному значению, и обеспечивает эффективное просмотр для обоих типов ключей в одиночку, а также. Я начал использовать эту структуру данных вместо Map<Pair<K1,K2>, V> во многих частях моего кода. Существуют массивы, деревья и другие реализации как для плотного, так и для разреженного использования, с возможностью указания ваших собственных промежуточных классов карт.

2

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

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

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

Я что-то подобное в моей библиотеке

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

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

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

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

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

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

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

1

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

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

5

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

4

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

1

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

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

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

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

4

Хорошие новости Java добавлено ключевое значение Пара.

только импорт javafx.util.Pair;

и использовать просто как в c++.

Pair < Key , Value > 

e.g.

Pair < Integer , Integer > pr = new Pair<Integer , Integer>() 

pr.get(key); // will return corresponding value 
0

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

@Value(staticConstructor = "of") public class Pair <E> { 
    E first, second; 
} 

и использовать его как: Pair<Value> pairOfValues = Pair.of(value1, value2);

0

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

Самые распространенные применения Pair в моем опыте были множественными возвращаемыми значениями из метода и как VALUES в хэшмапе (часто индексируются по строкам).

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

class SumHolder{MyObject trackedObject, double sum}; 

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

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

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

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

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