Колекція пар значень Java? (кортежі?)


345

Мені подобається, як у Java є карта, де ви можете визначити типи кожного запису на карті, наприклад <String, Integer>.

Що я шукаю - це тип колекції, де кожен елемент колекції - пара значень. Кожне значення пари може мати власний тип (наприклад, приклад String і Integer вище), який визначається під час оголошення.

Колекція буде підтримувати заданий порядок і не сприйме жодне зі значень як унікальний ключ (як на карті).

По суті, я хочу мати змогу визначити тип типу ARRAY <String,Integer>або будь-які інші 2 типи.

Я розумію, що я можу створити клас, не маючи нічого, крім двох змінних у ньому, але це здається надмірно багатослівним.

Я також усвідомлюю, що я міг би використовувати двовимірний масив, але через різні типи, які мені потрібно використовувати, я мав би зробити їх масивами OBJECT, і тоді мені доведеться весь час передавати.

Мені потрібно лише зберігати пари в колекції, тому мені потрібно лише два значення на запис. Чи існує щось подібне, не йдучи маршрутом маршруту? Дякую!


мені цікаво, що у Гуави також може бути клас для цього.
Sikorski

Guava досить анти- Pair, і люди в Google зайшли так далеко, що створили набагато кращу альтернативу - Auto / Value . Це дозволяє вам легко створювати добре набрані класи типів значень із належною семантикою рівних / хеш-кодів. Вам більше ніколи не знадобиться Pairтип!
dimo414

Відповіді:


255

Клас Pair - це один із тих прикладів дженерики "gimme", який досить просто написати самостійно. Наприклад, вгорі голови:

public class Pair<L,R> {

  private final L left;
  private final R right;

  public Pair(L left, R right) {
    assert left != null;
    assert right != null;

    this.left = left;
    this.right = right;
  }

  public L getLeft() { return left; }
  public R getRight() { return right; }

  @Override
  public int hashCode() { return left.hashCode() ^ right.hashCode(); }

  @Override
  public boolean equals(Object o) {
    if (!(o instanceof Pair)) return false;
    Pair pairo = (Pair) o;
    return this.left.equals(pairo.getLeft()) &&
           this.right.equals(pairo.getRight());
  }

}

І так, це існує в багатьох місцях в Мережі, з різним ступенем повноти та особливості. (Мій приклад вище покликаний бути незмінним.)


17
Мені це подобається, але що ви думаєте про те, щоб зробити ліве та праве поле публічними? Цілком зрозуміло, що для класу Pair ніколи не буде пов'язана будь-яка логіка, і всі клієнти потребують доступу до "ліворуч" та "праворуч", то чому б не зробити його легким?
Програматор поза законом

43
Гм ... ні, це не буде. Поля позначені як кінцеві, тому їх не можна перепризначити. І це не безпечно для потоків, тому що "лівий" і "правий" можуть бути незмінними. Якщо getLeft () / getRight () не повернув оборонні копії (даремно в цьому випадку), я не бачу, у чому полягає велика справа.
Програматор поза законом

17
Зауважте, що значення hashCode () як і є дає те саме значення, якщо обмінятись ліворуч і праворуч. Можливо:long l = left.hashCode() * 2654435761L; return (int)l + (int)(l >>> 32) + right.hashCode();
karmakaze

68
Тож якщо я правильно розумію, розгортання простого класу пар призводить до помилки синтаксису, методу хеш-коду subpar, винятку нульових покажчиків, немає порівнянняДо методу, питань дизайну ... і люди все ще виступають за розгортання цього класу, поки він існує в загальній версії Apache. Будь ласка, просто скопіюйте код, якщо ви не хочете включати JAR, але перестаньте винаходити колесо!
cquezel

38
Що ви маєте на увазі «досить легко писати самостійно»? Це жахлива інженерія програмного забезпечення. Чи вважаються перехідні класи N ^ 2 для перетворення між MyPair та SomeoneElsesPair досить простими для написання самостійно?
djechlin

299

AbstractMap.SimpleEntry

Легко ви шукаєте це:

java.util.List<java.util.Map.Entry<String,Integer>> pairList= new java.util.ArrayList<>();

Як можна заповнити його?

java.util.Map.Entry<String,Integer> pair1=new java.util.AbstractMap.SimpleEntry<>("Not Unique key1",1);
java.util.Map.Entry<String,Integer> pair2=new java.util.AbstractMap.SimpleEntry<>("Not Unique key2",2);
pairList.add(pair1);
pairList.add(pair2);

Це спрощує:

Entry<String,Integer> pair1=new SimpleEntry<>("Not Unique key1",1);
Entry<String,Integer> pair2=new SimpleEntry<>("Not Unique key2",2);
pairList.add(pair1);
pairList.add(pair2);

І за допомогою createEntryметоду можна додатково зменшити багатослівність до:

pairList.add(createEntry("Not Unique key1", 1));
pairList.add(createEntry("Not Unique key2", 2));

Оскільки ArrayListне є остаточним, його можна підкласирувати, щоб викрити ofметод (і вищезгаданий createEntryметод), що призведе до синтаксичного короткого:

TupleList<java.util.Map.Entry<String,Integer>> pair = new TupleList<>();
pair.of("Not Unique key1", 1);
pair.of("Not Unique key2", 2);

11
FYI: Запропонований метод SimpleEntryпропускання класу рідних братів setValueє незмінним. Таким чином назва SimpleImmutableEntry.
Василь Бурк

3
Це краще, ніж самореалізація. Той факт, що це легко здійснити, не є приводом для його впровадження кожного разу.
pevogam

6
Я б не вважав це "стандартним". Entryпризначена для пари ключ-значення, і це добре для цього. Але він має слабкі місця як справжній кортеж. Наприклад, hashcodeреалізація SimpleEntryкодів елементів у просто xors, так що хеширує <a,b>те саме значення, що і <b,a>. Реалізовані кортежі часто сортують лексикографічно, але SimpleEntry не реалізують Comparable. Так що будьте обережні, там ...
Гена

167

Java 9+

На Java 9 можна просто написати: Map.entry(key, value) створити незмінну пару.

Примітка. Цей метод не дозволяє нулю клавіш або значень. Якщо ви хочете , щоб значення NULL, наприклад, ви хочете змінити це: Map.entry(key, Optional.ofNullable(value)).


Java 8+

У Java 8 ви можете використовувати більш загальні цілі javafx.util.Pairдля створення непорушної, серіалізаційної пари. Цей клас має дозволяють ключі нульових значень і нульові. (У Java 9 цей клас включений в javafx.baseмодуль). ВИДАЛЕННЯ: Як і в Java 11, JavaFX був від'єднаний від JDK, тому вам знадобиться додатковий артефакт Maven org.openjfx: javafx-base.


Java 6+

У Java 6 і вище ви можете використовувати більш багатослівний AbstractMap.SimpleImmutableEntryдля незмінної пари або AbstractMap.SimpleEntryдля пари, значення якої можна змінити. Ці класи також дозволяють нульові ключі та нульові значення та є серіалізаційними.


Android

Якщо ви пишете для Android, просто використовуйте Pair.create(key, value)для створення непорушної пари.


Apache Commons

Apache Commons Langзабезпечує корисну Pair.of(key, value)для створення незмінну, порівнянну, серіалізаційну пару.


Колекції затемнення

Якщо ви використовуєте пари, що містять примітиви, Eclipse Collections надає кілька дуже ефективних класів примітивних пар, що дозволить уникнути всіх неефективних автобоксингу та авторозпакування.

Наприклад, ви можете використовувати PrimitiveTuples.pair(int, int)для створення IntIntPairабо PrimitiveTuples.pair(float, long)для створення FloatLongPair.


Проект Ломбок

Використовуючи Project Lombok , ви можете створити непорушний клас пар просто, написавши:

@Value
public class Pair<K, V> {
    K key;
    V value;
}

Ломбок заповнить конструктора, здобувач, equals(), hashCode()і toString()методи для вас автоматично згенерованих байт - коди. Якщо ви хочете статичний метод фабрики замість конструктора, наприклад, Pair.of(k, v)просто змінити анотацію: @Value(staticConstructor = "of").


Інакше

Якщо жодне з перерахованих вище рішень не плаває на вашому човні, ви можете просто скопіювати та вставити наступний код (який, на відміну від класу, зазначеного у прийнятій відповіді, захищає від NullPointerExceptions):

import java.util.Objects;

public class Pair<K, V> {

    public final K key;
    public final V value;

    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }

    public boolean equals(Object o) {
        return o instanceof Pair && Objects.equals(key, ((Pair<?,?>)o).key) && Objects.equals(value, ((Pair<?,?>)o).value);
    }

    public int hashCode() {
        return 31 * Objects.hashCode(key) + Objects.hashCode(value);
    }

    public String toString() {
        return key + "=" + value;
    }
}

3
Однак JavaFX призначений лише для настільних комп'ютерів, додаючи зайву залежність для середовищ, що не є робочим столом (наприклад, сервери).
foo

2
Eclipse Collections також має Pairінтерфейс для об'єктів, який можна створити інстанцією за допомогою виклику Tuples.pair(object1, object2). eclipse.org/collections/javadoc/9.2.0/org/eclipse/collections/…
Дональд Рааб

2
Це відчувається як найбільш справді відповідь на Java. ВСІ доступні реалізації. Дуже ретельно, велике спасибі!
Майк

Привіт @Hans Я використовую список List<Pair<Integer, String> listOfTuple. Як я можу використовувати listOfTuple.add()? Якщо я зроблю - listOfTuple.add(Pair<someInteger, someString>);це не вийде. Заздалегідь спасибі.
Me_developer

версія Android не доступна в одиничному тесті, що робить її не дуже корисною ...
OznOg


31

Apache common lang3 має клас Pair та кілька інших бібліотек, згаданих у цій темі. Що таке еквівалент C ++ Pair <L, R> у Java?

Приклад, що відповідає вимозі від вашого початкового запитання:

List<Pair<String, Integer>> myPairs = new ArrayList<Pair<String, Integer>>();
myPairs.add(Pair.of("val1", 11));
myPairs.add(Pair.of("val2", 17));

//...

for(Pair<String, Integer> pair : myPairs) {
  //following two lines are equivalent... whichever is easier for you...
  System.out.println(pair.getLeft() + ": " + pair.getRight());
  System.out.println(pair.getKey() + ": " + pair.getValue());
}

1
Це найпростіший варіант, якщо Apache Commons доступний.
Кіп


15

А як щодо Pairкласу "Apache Commons Lang 3" та відносних підкласів?

    import org.apache.commons.lang3.tuple.ImmutablePair;
    import org.apache.commons.lang3.tuple.Pair;
    ...
    @SuppressWarnings("unchecked")
    Pair<String, Integer>[] arr = new ImmutablePair[]{
            ImmutablePair.of("A", 1),
            ImmutablePair.of("B", 2)};

    // both access the 'left' part
    String key = arr[0].getKey();
    String left = arr[0].getLeft();

    // both access the 'right' part
    Integer value = arr[0].getValue();
    Integer right = arr[0].getRight();

ImmutablePair- це специфічний підклас, який не дозволяє змінювати значення в парі, але є й інші реалізації з різною семантичною ознакою. Це координати Мейвена, якщо вони вам потрібні.

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.4</version>
        </dependency>

11

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


Я хотів би побачити приклад цього!
DivideByHero

1
Ден, погано в тому, що неможливо прийняти, наприклад, лише пару <String, Integer> s через стирання типу, ні? Але так і Java ...
Йоганнес Вайс


Я не думаю, що це буде працювати для звичайних масивів, але це, безумовно, буде працювати для інших колекцій.
Програматор поза законом

@Outlaw програміст: Добре, ви можете використовувати його з масивами, але це некрасиво, оскільки вам потрібно використовувати хак для створення загальних масивів.
Дан Дайер

7

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

class Pair<L,R> {
      final L left;
      final R right;

      public Pair(L left, R right) {
        this.left = left;
        this.right = right;
      }

      static <L,R> Pair<L,R> of(L left, R right){
          return new Pair<L,R>(left, right);
      }
}

якщо ви називаєте статичний метод "з" або "параOf", код стає вільним, оскільки ви можете написати:

    list.add(Pair.of(x,y)); // my preference
    list.add(pairOf(x,y)); // use with import static x.y.Pair.pairOf

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


6

Я збирався запитати, чи не хочете ви просто використовувати List<Pair<T, U>>? але тоді, звичайно, JDK не має класу Pair <>. Але швидкий Google знайшов його і в Вікіпедії , і на forums.sun.com . Ура


6

Краще рішення, як ви його описали, - це список пар (тобто список).

Для цього вам слід створити клас «Пара» для використання у вашій колекції. Це корисний клас утиліти, який потрібно додати до кодової бази.

Найближчим класом у Sun JDK, що забезпечує функціональність, схожу на типовий клас Pair, є AbstractMap.SimpleEntry. Ви могли б використовувати цей клас, а не створювати свій власний клас Pair, хоча вам доведеться жити з деякими незручними обмеженнями, і я думаю, що більшість людей нахмуриться на це як насправді не задуману роль SimpleEntry. Наприклад, SimpleEntry не має методу "setKey ()" і не має конструктора за замовчуванням, тому ви можете вважати це занадто обмежуючим.

Майте на увазі, що колекції створені для того, щоб містити елементи одного типу. Пов'язані інтерфейси утиліти, такі як Map, насправді не є колекціями (тобто Map не реалізує інтерфейс колекції). Пара також не реалізує інтерфейс колекції, але, очевидно, є корисним класом для створення більших структур даних.


Я вважаю, що це рішення краще, ніж "переможець" із загальним пар <K, V>. Він робить все, що вимагається, і виходить з коробки. Я використовую цей для моєї реалізації.
Зауер

5

Це засновано на коді JavaHelp4u.

Менш багатослівний і показує, як робити в одному рядку і як робити петлю над речами.

//======>  Imports
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;

//======>  Single Entry
SimpleEntry<String, String> myEntry = new SimpleEntry<String, String>("ID", "Text");
System.out.println("key: " + myEntry.getKey() + "    value:" + myEntry.getValue());
System.out.println();

//======>  List of Entries
List<Entry<String,String>> pairList = new ArrayList<>();

//-- Specify manually
Entry<String,String> firstButton = new SimpleEntry<String, String>("Red ", "Way out");
pairList.add(firstButton);

//-- one liner:
pairList.add(new SimpleEntry<String,String>("Gray", "Alternate route"));  //Ananomous add.

//-- Iterate over Entry array:
for (Entry<String, String> entr : pairList) {
    System.out.println("Button: " + entr.getKey() + "    Label: " + entr.getValue());
}


4

просто створити клас на кшталт

class tuples 
{ 
int x;
int y;
} 

потім створіть Список цих об'єктів кортежів

List<tuples> list = new ArrayList<tuples>();

тому ви також можете реалізувати інші нові структури даних аналогічно.


4

Я маю на увазі, хоча Pairв Java немає класу, є щось досить симпатичне:Map.Entry

Карта. Вхідна документація

Це (спрощуючи зовсім небагато) що HashMap, чи насправді будь-які Mapмагазини.

Ви можете створити екземпляр Mapзберігання в ньому своїх значень і отримати набір записів. Ви закінчите з тим, Set<Map.Entry<K,V>>що ефективно - це те, що ви хочете.

Тому:

public static void main(String []args)
{    
    HashMap<String, Integer> values = new HashMap<String,Integer>();
    values.put("A", 235);//your custom data, the types may be different
    //more data insertions....
    Set<Map.Entry<String,Integer>> list = values.entrySet();//your list 
    //do as you may with it
}

Ця опція має проблему унікальних клавіш. Скажімо, у вас є атрибути осіб (наприклад, {(person1, "блакитноокий"), (person1, "рудий", (person2, "короткозорий"), (person2, "швидкодумний")}) не в змозі зберігати їх як пари на карті, оскільки кожна людина є ключем до карти і дозволятиме лише один атрибут на людину.
manuelvigarcia


0

Що з com.sun.tools.javac.util.Pair?


7
Цей тип є в tools.jar - частина реалізації "Sun" компілятора javac. Він поширюється лише як частина JDK, може не бути присутнім в інших реалізаціях постачальників і навряд чи буде частиною публічного API.
МакДауелл

0

Перше, що мені спадає на думку, коли йдеться про пари ключів / значень, - це клас властивостей, де ви можете зберігати та завантажувати елементи в потік / файл.


0

У проектному реакторі (io.projectreactor: реактор-ядро) є вдосконалена підтримка n-Tuples:

Tuple2<String, Integer> t = Tuples.of("string", 1)

Там ви можете потрапити t.getT1(), t.getT2(), ...Особливо за допомогою Stream або Flux, ви навіть можете скласти карту елементів:

Stream<Tuple2<String, Integer>> s;
s.map(t -> t.mapT2(i -> i + 2));
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.