ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

集合将保持其给定的顺序,并且不会将其中一个值视为唯一键(如在地图中)。

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

我意识到我可以创建一个只包含 2 个变量的类,但这似乎过于冗长。

我也意识到我可以使用 2D 数组,但由于我需要使用不同的类型,我必须将它们设为 OBJECT 数组,然后我必须一直进行转换。

我只需要在集合中存储对,因此每个条目只需要两个值。如果不走课程路线,这样的事情是否存在?谢谢!

我想知道番石榴可能也有这方面的课程。
Guava 非常反对Pair,Google 的人们甚至创造了一个更好的替代方案 - Auto/Value。它使您可以轻松创建 类型良好的 值类型类,并具有适当的 equals/hashCode 语义。您再也不需要 Pair 类型了!
我本来希望答案能走得更远,并进入通用 n 元组的主题,不仅仅是对,而是 arity n 的元组。

D
Dave Jarvis

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 的对都将产生相同的(零)哈希码。
D
Dave Jarvis

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 适配器类是否也被认为很容易自己编写?
H
Hans Brende

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 上测试


然而,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 不包含 PairTuple 之类的类,因为语言本身不允许这些结构。也许 JDK 作者不希望人们使用元组只是为了避免编写更有意义的类。
B
Basil Bourque

地图入口

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

AbstractMap.SimpleEntry

AbstractMap.SimpleImmutableEntry

https://i.stack.imgur.com/r72Q0.png


C
Community

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 可用,这是最简单的选项。
X
XåpplI'-I0llwlg'I -

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


d
danidemi

“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>

D
Dan Dyer

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


我很想看到一个例子!
丹,这样做的坏处是因为类型擦除而不能只接受例如 Pairs,不是吗?但是Java也是...
我认为这不适用于普通数组,但它肯定适用于其他集合。
@Outlaw Programmer:好点,您可以将它与数组一起使用,但它很难看,因为您必须使用 hack 来创建通用数组。
s
simbo1905

扩展其他答案,一个通用的不可变 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 的另一个原因...


J
Jeremy Rishel

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

为此,您将创建一个 Pair 类以在您的集合中使用。这是一个有用的实用程序类,可以添加到您的代码库中。

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

请记住,集合旨在包含单一类型的元素。 Map 等相关实用程序接口实际上并不是Collection(即Map 没有实现Collection 接口)。 Pair 也不会实现 Collection 接口,但显然是构建更大数据结构的有用类。


我发现这种解决方案比使用通用 Pair 的“获胜”解决方案更好。它可以满足所有要求并且开箱即用。我用这个来实现我的。
d
deamon

Java 14+ 版本

您可以创建一个开箱即用地实现 equalshashCodetoStringrecord。如果需要,也可以实现像 Comparable 这样的接口。

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

记录是不可变的。


J
JMD

我想问你是否不想只使用List<Pair<T, U>>?但是,当然,JDK 没有 Pair<>班级。但是快速的 Google 在 Wikipediaforums.sun.com 上都找到了一个。干杯


L
Leo Ufimtsev

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

m
muttonUp

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

B
Bobak_KS

Apache Crunch 还有一个 Pair 类:http://crunch.apache.org/apidocs/0.5.0/org/apache/crunch/Pair.html


Apache Crunch 作为一个项目退役。除此之外,仅仅为了拥有这样一个简单的类型而引入一个大框架并不是一个好主意。
S
SomeDude

我的意思是,即使 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")})无法将它们成对存储在地图中,因为每个人都是地图的关键,并且每个人只允许一个属性。
M
Mukit09

只需创建一个类

class Tuples 
{ 
    int x;
    int y;
} 

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

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

所以你也可以用同样的方式实现其他新的数据结构。


R
Rene H.

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


此类型位于 tools.jar - javac 编译器的“Sun”实现的一部分。它仅作为 JDK 的一部分分发,可能不存在于其他供应商的实现中,也不太可能成为公共 API 的一部分。
m
muka90

谈到键/值对时,我首先想到的是 Properties Class,您可以在其中保存项目并将其加载到流/文件中。


T
Tires

在项目 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));

S
Syed Aqeel Ashiq

您可以重用现有的 Pair 或“天知道有多少库已经提供此类类”中的任何此类。如果您不想要 Hans Brende's answer on this question 中的任何东西,那么我看不出有任何理由不使用 2D ArrayObject Arrays/ArrayLists used as Pairs/TuplesList。您提到不使用 Array 的原因:

我也意识到我可以使用 2D 数组,但由于我需要使用不同的类型,我必须将它们设为 OBJECT 数组,然后我必须一直进行转换。

即使您使用 Accepted Answer 中的 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<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];