我喜欢 Java 有一个 Map,您可以在其中定义映射中每个条目的类型,例如 <String, Integer>
。
我正在寻找的是一种集合,其中集合中的每个元素都是一对值。对中的每个值都可以有自己的类型(如上面的字符串和整数示例),它是在声明时定义的。
集合将保持其给定的顺序,并且不会将其中一个值视为唯一键(如在地图中)。
本质上,我希望能够定义类型为 <String,Integer>
或任何其他 2 种类型的 ARRAY。
我意识到我可以创建一个只包含 2 个变量的类,但这似乎过于冗长。
我也意识到我可以使用 2D 数组,但由于我需要使用不同的类型,我必须将它们设为 OBJECT 数组,然后我必须一直进行转换。
我只需要在集合中存储对,因此每个条目只需要两个值。如果不走课程路线,这样的事情是否存在?谢谢!
Pair
,Google 的人们甚至创造了一个更好的替代方案 - Auto/Value。它使您可以轻松创建 类型良好的 值类型类,并具有适当的 equals/hashCode 语义。您再也不需要 Pair
类型了!
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);
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());
}
}
是的,这存在于网络上的多个地方,具有不同程度的完整性和功能。 (我上面的例子是不可变的。)
long l = left.hashCode() * 2654435761L;
return (int)l + (int)(l >>> 32) + right.hashCode();
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 Lang
提供了有用的 Pair.of(key, value)
来创建不可变、可比较、可序列化的对。
日食系列
如果您使用包含基元的对,Eclipse Collections 提供了一些非常有效的基元对类,可以避免所有低效的自动装箱和自动拆箱。
例如,您可以使用 PrimitiveTuples.pair(int, int)
创建一个 IntIntPair
,或使用 PrimitiveTuples.pair(float, long)
创建一个 FloatLongPair
。
手动实施
从 Java 16 开始,records 已退出预览状态,因此您现在可以执行以下操作:
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);
}
}
上面的实现在未来会有很大的优势,因为它允许你做record deconstruction。
在 Java 16 之前,您可以使用 Project Lombok 实现相同的语义:
@Value(staticConstructor = "of")
public class Pair<K, V> {
K key;
V value;
}
或者,具有以下详细程度(与接受的答案中列出的类不同,它可以防止 NullPointerExceptions,并且具有与 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 上测试
Pair
接口,可以通过调用 Tuples.pair(object1, object2)
对其进行实例化。 eclipse.org/collections/javadoc/9.2.0/org/eclipse/collections/…
List<Pair<Integer, String> listOfTuple
。如何使用 listOfTuple.add()
?如果我愿意 - listOfTuple.add(Pair<someInteger, someString>);
,这将不起作用。提前致谢。
Pair
或 Tuple
之类的类,因为语言本身不允许这些结构。也许 JDK 作者不希望人们使用元组只是为了避免编写更有意义的类。
地图入口
这些内置类也是一种选择。两者都实现了 Map.Entry
接口。
AbstractMap.SimpleEntry
AbstractMap.SimpleImmutableEntry
https://i.stack.imgur.com/r72Q0.png
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 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>
您可以编写一个通用的 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 的另一个原因...
正如您所描述的,首选的解决方案是一对列表(即列表)。
为此,您将创建一个 Pair 类以在您的集合中使用。这是一个有用的实用程序类,可以添加到您的代码库中。
Sun JDK 中提供类似于典型 Pair 类的功能的最接近的类是 AbstractMap.SimpleEntry。你可以使用这个类而不是创建你自己的 Pair 类,尽管你将不得不忍受一些尴尬的限制,而且我认为大多数人会对此不以为然,因为这并不是 SimpleEntry 的预期角色。例如,SimpleEntry 没有“setKey()”方法,也没有默认构造函数,因此您可能会发现它过于局限。
请记住,集合旨在包含单一类型的元素。 Map 等相关实用程序接口实际上并不是Collection(即Map 没有实现Collection 接口)。 Pair 也不会实现 Collection 接口,但显然是构建更大数据结构的有用类。
Java 14+ 版本
您可以创建一个开箱即用地实现 equals
、hashCode
和 toString
的 record
。如果需要,也可以实现像 Comparable
这样的接口。
record Pair<A, B>(A first, B second) {}
记录是不可变的。
我想问你是否不想只使用List<Pair<T, U>>
?但是,当然,JDK 没有 Pair<>班级。但是快速的 Google 在 Wikipedia 和 forums.sun.com 上都找到了一个。干杯
这是基于 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());
}
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());
Apache Crunch 还有一个 Pair
类:http://crunch.apache.org/apidocs/0.5.0/org/apache/crunch/Pair.html
我的意思是,即使 Java 中没有 Pair
类,也有一些非常相似的东西: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
}
只需创建一个类
class Tuples
{
int x;
int y;
}
然后创建这个元组对象的列表
List<Tuples> list = new ArrayList<>();
所以你也可以用同样的方式实现其他新的数据结构。
com.sun.tools.javac.util.Pair 呢?
在项目 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));
您可以重用现有的 Pair
或“天知道有多少库已经提供此类类”中的任何此类。如果您不想要 Hans Brende's answer on this question 中的任何东西,那么我看不出有任何理由不使用 2D Array
或 Object Arrays/ArrayLists used as Pairs/Tuples
的 List
。您提到不使用 Array
的原因:
我也意识到我可以使用 2D 数组,但由于我需要使用不同的类型,我必须将它们设为 OBJECT 数组,然后我必须一直进行转换。
即使您使用 Accepted Answer 中的 Pair
类,您仍然必须转换键和值对象。因为您想在其中存储所有类型的对象。换句话说,List<Pair> pairs
与 List<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 array
或 List of Object Arrays/ArrayLists
用作 Pairs/Tuples
。以下是使用列表和数组的代码
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];
SimpleEntry
有一个兄弟类省略setValue
方法是不可变的。因此名称为SimpleImmutableEntry
。Entry
是一个键值对,这很好。但它作为一个真正的元组有弱点。例如,SimpleEntry
中的hashcode
实现只是对元素的代码进行异或运算,因此<a,b>
散列到与<b,a>
相同的值。元组实现通常按字典顺序排序,但SimpleEntry
不实现Comparable
。所以出门要小心...