值对的Java集合?(元组?)

我喜欢Java有一个映射,你可以在映射中定义每个条目的类型,例如<String, Integer>

我正在寻找的是一种类型的集合,其中集合中的每个元素都是一对值。对中的每个值都可以有自己的类型(如上面的String和Integer示例),该类型在声明时定义。

该集合将保持其给定的顺序,不会将其中一个值视为唯一键(就像在映射中一样)。

本质上,我希望能够定义类型为<String,Integer>或任何其他两种类型的ARRAY。

我意识到,我可以让一个类只有2个变量,但这似乎过于啰嗦。

我也意识到我可以使用一个2D数组,但因为我需要使用不同的类型,我必须使他们OBJECT数组,然后我必须一直强制转换。

我只需要在集合中存储对,所以每个条目只需要两个值。像这样的东西不走阶级路线就存在吗?谢谢!

595201 次浏览

你可以写一个通用的Pair< a, B>类,并在数组或列表中使用它。是的,你必须写一个类,但是你可以为所有类型重用同一个类,所以你只需要做一次。

我想问你是否不想只使用List<Pair<T, U>>?但是,JDK当然没有一个Pair<>类。但是快速谷歌在维基百科forums.sun.com上都找到了一个。干杯

Pair类是那些“给我”泛型示例之一,很容易自己编写。例如,在我的脑海中:

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


}

是的,它存在于网络上的多个地方,具有不同程度的完整性和特征。(我上面的例子是不可变的。)

Map.Entry

这些内置类也是一个选项。两者都实现了Map.Entry接口。

 Map的UML图。入口接口与对实现类 .

您所描述的首选解决方案是对列表(即列表)。

要做到这一点,您需要创建一个Pair类用于您的集合。这是一个可以添加到代码库的有用实用程序类。

Sun JDK中提供类似典型Pair类功能的最接近的类是AbstractMap.SimpleEntry。您可以使用这个类,而不是创建您自己的Pair类,尽管您将不得不忍受一些尴尬的限制,而且我认为大多数人会对此表示不满,因为这并不是SimpleEntry真正想要的角色。例如,SimpleEntry没有“setKey()”方法,也没有默认构造函数,所以您可能会发现它的限制太大了。

请记住,集合被设计用来包含单一类型的元素。相关的实用程序接口,比如Map,实际上并不是Collection(也就是说Map没有实现Collection接口)。Pair也不会实现Collection接口,但在构建更大的数据结构时显然是一个有用的类。

com.sun.tools.javac.util.Pair呢?

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

在其他答案上展开一个通用的不可变Pair应该有一个静态方法,以避免调用构造函数使你的代码变得混乱:

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”或“pairOf”,代码就会变得流畅,因为你可以写任何一个:

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

遗憾的是,核心Java库在这些事情上是如此的稀少,以至于你必须使用common -lang或其他第三方来做这些基本的事情。升级到scala的另一个原因…

只需创建一个类

class Tuples
{
int x;
int y;
}

然后创建这个对象的元组列表

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

因此,您还可以以相同的方式实现其他新的数据结构。

对于任何为Android开发的人,你都可以使用android.util.Pair。:)

这是基于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());
}

Java 9 +

在Java 9中,你可以简单地写:Map.entry(key, value) 创建一个不可变的pair

注意:此方法不允许键或值为空。例如,如果你想允许空值,你需要将其更改为:Map.entry(key, Optional.ofNullable(value))


Java 8 +

在Java 8中,你可以使用更通用的javafx.util.Pair来创建一个不可变的、可序列化的pair。这个类允许空键和空值。(在Java 9中,该类包含在javafx.base模块中)。编辑:从Java 11开始,JavaFX已经从JDK中解耦了,所以您需要额外的maven构件org.openjfx: JavaFX -base。


Java 6 +

在Java 6及以上版本中,可以使用更详细的AbstractMap.SimpleImmutableEntry表示不可变的pair,或者使用AbstractMap.SimpleEntry表示值可以更改的pair。这些类还允许空键和空值,并且是可序列化的。


安卓

如果你正在为Android编写程序,只需使用Pair.create(key, value)来创建一个不可变的pair。


Apache Commons

Apache Commons Lang提供了有用的Pair.of(key, value)来创建不可变的、可比较的、可序列化的pair。


Eclipse集合

如果你正在使用包含原语的对,Eclipse集合提供了一些非常有效的原语对类,可以避免所有低效的自动装箱和自动拆箱。

例如,你可以使用PrimitiveTuples.pair(int, int)创建一个IntIntPair,或者使用PrimitiveTuples.pair(float, long)创建一个FloatLongPair


手卷的实现

在Java 16中,记录已经脱离预览状态,所以你现在可以:

public record Pair<K, V>(K key, V value) {
public static <K, V> Pair<K, V> of(K key, V value) {
return new Pair<>(key, value);
}
}

上面的实现在将来会有很大的优势,因为它将允许你执行记录解构

在Java 16之前,你可以通过Project Lombok实现相同的语义:

@Value(staticConstructor = "of")
public class Pair<K, V> {
K key;
V value;
}

或者,使用以下冗长的语句(与已接受答案中列出的类不同,它可以防止nullpointerexception,并且具有与Records1相同的健壮的hashCode()实现):

import java.util.Objects;


public class Pair<K, V> {


public final K key;
public final V value;


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


public static <K, V> Pair<K, V> of(K key, V value) {
return new Pair<>(key, 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;
}
}

1在OpenJDK 17上测试

“Apache Commons Lang 3”Pair类和相关子类呢?

    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是一个特定的子类,它不允许修改pair中的值,但是还有其他具有不同语义的实现。这些是Maven的坐标,如果你需要的话。

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

当谈论键/值对时,我想到的第一件事是属性类,在那里你可以保存和加载项到流/文件中。

在project Reactor (io. projecreactor: Reactor -core)中,有对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));

Spring在Data Utils包org.springframework.data.util中有一个Pair<S,T>类型

Pair<String,Integer> pair = Pair.of("Test", 123);
System.out.println(pair.getFirst());
System.out.println(pair.getSecond());

java14 +版本

你可以创建一个record来实现equalshashCodetoString。如果需要,还可以实现Comparable这样的接口。

record Pair<A, B>(A first, B second) {}

记录是不可变的。

你可以重用现有的Pair或任何这样的类,“天知道有多少库已经提供了这样的类”。如果你不想从Hans Brende对这个问题的回答中得到任何东西,那么我不认为有任何理由不使用2D ArrayListObject Arrays/ArrayLists used as Pairs/Tuples。不使用Array的原因,你提到:

我也意识到我可以使用一个2D数组,但因为我需要使用不同的类型,我必须使他们OBJECT数组,然后我必须一直强制转换。

即使使用< >强接受答案< / >强中的Pair类,仍然必须强制转换键和值对象。因为你想要存储所有类型的对象。换句话说,List<Pair> pairsList<Pair<? extends Object>> pairs没有什么不同,而List<Pair<? extends Object>> pairs又与Object[][2]List<Object[]>List<List<Object>>没有什么不同。因为如果你写下面的代码:

List<Pair> pairs = new ArrayList<>();


// logic for populating pairs into list goes here


// then after sometime you need an element
Pair p = pairs.get(whateverIndex);


Object key = p.getKey(); // We don't know type of key, right?
Object value = p.getValue(); // We don't know the exact type here as well


// All sorts of type guessing statemntes go here
GuessedTypeOfKey finallyMyKey = (GuessedTypeOfKey) key;
GuessedTypeOfValue finallyMyValue = (GuessedTypeOfValue) value;

你仍然需要进行类型转换。所以我找不到任何其他理由不使用2d Object arrayList of Object Arrays/ArrayLists作为Pairs/Tuples。下面是使用List和数组的代码

List<Object[]> simplePairs = new ArrayList<>();


// Add values to pairs
simplePairs.add(new Object[]{1,"One"});
simplePairs.add(new Object[]{"Another Key of Different Type","Value"});
simplePairs.add(new Object[]{"Another Key of Different Type",new AnotherType("Another Value Type")});


// get values
Object[] pair = simplePairs.get(whateverIndex);
Object key = pair[0];
Object value = pair[1];