值对的 Java 集合? (元组?)
Posted
技术标签:
【中文标题】值对的 Java 集合? (元组?)【英文标题】:A Java collection of value pairs? (tuples?) 【发布时间】:2010-10-05 23:46:20 【问题描述】:我喜欢 Java 有一个 Map,您可以在其中定义映射中每个条目的类型,例如 <String, Integer>
。
我正在寻找一种集合类型,其中集合中的每个元素都是一对值。对中的每个值都可以有自己的类型(如上面的 String 和 Integer 示例),它是在声明时定义的。
集合将保持其给定的顺序,并且不会将其中一个值视为唯一键(如在地图中)。
本质上,我希望能够定义 <String,Integer>
类型的 ARRAY 或任何其他 2 种类型。
我意识到我可以创建一个只包含 2 个变量的类,但这似乎过于冗长。
我也意识到我可以使用 2D 数组,但由于我需要使用不同的类型,我必须将它们设为 OBJECT 数组,然后我必须一直进行转换。
我只需要在集合中存储对,因此每个条目只需要两个值。如果不走课程路线,这样的事情是否存在?谢谢!
【问题讨论】:
我想知道 Guava 也可能为此开设一个课程。 Guava 非常反对Pair
,Google 的人们甚至创造了一个更好的替代方案 - Auto/Value。它可以让您轻松创建 类型良好的 值类型类,并具有适当的 equals/hashCode 语义。你再也不需要Pair
类型了!
【参考方案1】:
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);
【讨论】:
仅供参考:这表明SimpleEntry
有一个兄弟类省略 setValue
方法是不可变的。因此名称为SimpleImmutableEntry
。
这比自己实现要好。易于实施的事实并不是每次都实施的借口。
我不会认为它是“标准的”。 Entry
是一个键值对,这很好。但它作为一个真正的元组有弱点。例如,SimpleEntry
中的hashcode
实现只是对元素的代码进行异或运算,因此<a,b>
散列到与<b,a>
相同的值。元组实现通常按字典顺序排序,但SimpleEntry
没有实现Comparable
。所以在外面要小心……
感谢@Gene 提出哈希码问题,这让我很惊讶。 XOR 的使用还意味着一堆其他的冲突,包括所有 key == value 的对都将产生相同的(零)哈希码。【参考方案2】:
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());
是的,这存在于网络上的多个地方,具有不同程度的完整性和功能。 (我上面的例子是不可变的。)
【讨论】:
我喜欢这个,但是你觉得把左右字段公开怎么样?很明显,Pair 类永远不会关联任何逻辑,所有客户端都需要访问“左”和“右”,所以为什么不让它变得简单呢? 嗯...不,它不会。这些字段被标记为最终字段,因此无法重新分配。而且它不是线程安全的,因为“左”和“右”可能是可变的。除非 getLeft()/getRight() 返回防御副本(在这种情况下没用),否则我看不出有什么大不了的。 请注意,如果左右交换,hashCode() 原样给出相同的值。也许:long l = left.hashCode() * 2654435761L;
return (int)l + (int)(l >>> 32) + right.hashCode();
所以如果我理解正确,推出一个简单的pair类会产生语法错误、低于标准的hashCode方法、空指针异常、没有compareTo方法、设计问题......人们仍然提倡推出这个类,而它存在于 Apache commons 中。如果您不想包含 JAR 但不要重新发明***,请复制代码!
你是什么意思“很容易自己写”?那是可怕的软件工程。用于在 MyPair 和 SomeElsesPair 之间转换的 N^2 个适配器类是否也被认为很容易自己编写?【参考方案3】:
Java 9+
在 Java 9 中,您可以简单地写成:Map.entry(key, value)
创建一个不可变对。
注意:此方法不允许键或值为空。例如,如果您想允许空值,您需要将其更改为: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 编写代码,只需使用 Pair.create(key, value)
创建一个不可变对。
Apache Commons
Apache Commons Lang
提供有用的Pair.of(key, value)
来创建不可变、可比较、可序列化的对。
Eclipse 集合
如果您使用包含原语的对,Eclipse Collections 提供了一些非常有效的原语对类,可以避免所有低效的自动装箱和自动拆箱。
例如,您可以使用PrimitiveTuples.pair(int, int)
创建IntIntPair
,或使用PrimitiveTuples.pair(float, long)
创建FloatLongPair
。
龙目岛计划
使用Project Lombok,您可以简单地通过编写来创建一个不可变对类:
@Value
public class Pair<K, V>
K key;
V value;
Lombok 会在生成的字节码中自动为您填写构造函数、getter、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;
【讨论】:
JavaFX 仅适用于桌面,为非桌面环境(例如服务器)添加了不必要的依赖项。 Eclipse Collections 还有一个Pair
对象接口,可以通过调用Tuples.pair(object1, object2)
来实例化。 eclipse.org/collections/javadoc/9.2.0/org/eclipse/collections/…
嗨@Hans 我正在使用列表List<Pair<Integer, String> listOfTuple
。如何使用listOfTuple.add()
?如果我愿意 - listOfTuple.add(Pair<someInteger, someString>);
,这将不起作用。提前致谢。
android 版本在单元测试中不可用,这使得它没有真正的用处......
最佳答案。遗憾的是 JDK 不包含 Pair
或 Tuple
这样的类,因为语言本身不允许这些结构。也许 JDK 的作者不希望人们使用元组只是为了避免编写更有意义的类。【参考方案4】:
Map.Entry
这些内置类也是一种选择。两者都实现了Map.Entry
接口。
AbstractMap.SimpleEntry
AbstractMap.SimpleImmutableEntry
【讨论】:
【参考方案5】:Apache common lang3 有 Pair 类和在这个线程 What is the equivalent of the C++ Pair<L,R> in 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());
【讨论】:
如果 Apache Commons 可用,这是最简单的选项。【参考方案6】:对于任何为 Android 开发的人,您可以使用 android.util.Pair。 :)
【讨论】:
【参考方案7】:“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
是一个特定的子类,它不允许修改对中的值,但还有其他具有不同语义的实现。如果需要,这些是 Maven 坐标。
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
【讨论】:
【参考方案8】:您可以编写一个通用的 Pair 类并在数组或列表中使用它。是的,你必须写一个类,但是你可以为所有类型重用同一个类,所以你只需要写一次。
【讨论】:
我很想看看这样的例子! Dan,这样做的坏处是无法接受,例如由于类型擦除,只有 Pair扩展其他答案,一个通用的不可变 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 库在这样的事情上如此稀疏,以至于你必须使用 commons-lang 或其他 3rd 方来做这些基本的事情,这真是太可惜了。升级到 scala 的另一个原因...
【讨论】:
【参考方案10】:您所描述的首选解决方案是一对列表(即列表)。
为此,您将创建一个 Pair 类以在您的集合中使用。这是一个有用的实用程序类,可以添加到您的代码库中。
Sun JDK 中提供与典型 Pair 类相似功能的最接近的类是 AbstractMap.SimpleEntry。您可以使用这个类而不是创建自己的 Pair 类,尽管您将不得不忍受一些尴尬的限制,而且我认为大多数人会对此不以为然,因为这并不是 SimpleEntry 的预期角色。例如 SimpleEntry 没有“setKey()”方法,也没有默认构造函数,所以你可能会觉得它太局限了。
请记住,集合旨在包含单一类型的元素。相关的实用程序接口,例如 Map 实际上并不是 Collections(即 Map 没有实现 Collection 接口)。 Pair 也不会实现 Collection 接口,但显然是构建更大数据结构的有用类。
【讨论】:
我发现这种解决方案比使用通用 Pair我想问你是否不想只使用List<Pair<T, U>>
?但是,当然,JDK 没有 Pair 类。但是快速的谷歌在Wikipedia 和forums.sun.com 上都找到了一个。干杯
【讨论】:
【参考方案12】:这是基于 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());
【讨论】:
【参考方案13】:Spring 在 Data Utils 包中有一个 Pair<S,T>
类型 org.springframework.data.util
Pair<String,Integer> pair = Pair.of("Test", 123);
System.out.println(pair.getFirst());
System.out.println(pair.getSecond());
【讨论】:
【参考方案14】:Java 14+ 版本
您可以创建一个开箱即用的record
实现equals
、hashCode
和toString
。如果需要,也可以实现像 Comparable
这样的接口。
record Pair<A, B>(A first, B second)
记录是不可变的。
【讨论】:
【参考方案15】:Apache Crunch 还有一个 Pair
类:
http://crunch.apache.org/apidocs/0.5.0/org/apache/crunch/Pair.html
【讨论】:
Apache Crunch 已作为一个项目退役。除此之外,为了拥有这样一个简单的类型而引入一个大框架并不是一个好主意。【参考方案16】:只需创建一个类
class tuples
int x;
int y;
然后创建这个元组对象的列表
List<tuples> list = new ArrayList<tuples>();
所以你也可以用同样的方式实现其他新的数据结构。
【讨论】:
【参考方案17】:我的意思是,即使 Java 中没有 Pair
类,也有一些非常相似的东西:Map.Entry
Map.Entry Documentation
这是(相当简化)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, "blue-eyed"), (person1, "red-haired", (person2, "shortsighted"), (person2, "quick-thinker"))无法将它们成对存储在地图中,因为每个人都是地图的关键,并且每个人只允许一个属性。【参考方案18】:com.sun.tools.javac.util.Pair 呢?
【讨论】:
此类型位于 tools.jar - javac 编译器的“Sun”实现的一部分。它仅作为 JDK 的一部分分发,可能不存在于其他供应商的实现中,也不太可能成为公共 API 的一部分。【参考方案19】:在谈论键/值对时,我首先想到的是Properties Class,您可以在其中将项目保存和加载到流/文件中。
【讨论】:
【参考方案20】:在项目 Reactor (io.projectreactor: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));
【讨论】:
以上是关于值对的 Java 集合? (元组?)的主要内容,如果未能解决你的问题,请参考以下文章