Java值对集合?(元组?)


345

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

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

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

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

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

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

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


我想知道番石榴是否还会为此提供一门课程。
西科斯基

番石榴是非常反抗的Pair,而Google的员工则走得更远,以至于创造了更好的替代品-Auto / Value。它使您可以使用适当的equals / hashCode语义轻松创建类型良好的值类型类。您再也不需要Pair类型了!
dimo414

Answers:


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类永远不会有任何逻辑关联,并且所有客户端都需要访问“ left”和“ right”,所以为什么不那么简单呢?
Outlaw程序员,

43
嗯...不,不会。这些字段被标记为最终字段,因此无法重新分配。而且它不是线程安全的,因为“ left”和“ right”可能是可变的。除非getLeft()/ getRight()返回防御性副本(在这种情况下没有用),否则我看不出有什么大不了的。
Outlaw程序员

17
请注意,如果左右互换,则hashCode()照原样提供相同的值。也许:long l = left.hashCode() * 2654435761L; return (int)l + (int)(l >>> 32) + right.hashCode();
karmakaze 2012年

68
因此,如果我正确理解,推出一个简单的配对类会产生语法错误,subpar hashCode方法,空指针异常,没有compareTo方法,设计问题...并且人们仍然主张推出此类,因为它存在于Apache commons中。请,如果不想包含JAR,只需复制代码即可,但不要重新发明轮子了!
cquezel

38
您是什么意思,“足够容易自己编写”?那真是糟糕的软件工程。是否在MyPair和SomeoneElsesPair之间转换的N ^ 2适配器类是否也足够容易编写自己编写?
djechlin

298

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
仅供参考:这建议SimpleEntry省略同级的类setValue,使其不可变。故名SimpleImmutableEntry
罗勒·布尔克

3
这比自我实现更好。易于实现这一事实并不是每次都实现的借口。
pevogam

6
我不会认为它是“标准”。Entry意味着是一个键值对,这很好。但是它有一个真正的元组的弱点。例如hashcodeSimpleEntry仅将元素代码异或的实现,因此<a,b>哈希值与相同<b,a>。元组的实现通常按字典顺序排序,但SimpleEntry 不实现Comparable。所以要当心……
Gene

166

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编写的,请使用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;
}

龙目岛将在构造函数中,吸气剂,填充equals()hashCode()toString()方法自动生成的字节码为您服务。如果要使用静态工厂方法而不是构造函数(例如a)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风格的答案。所有可用的实现。非常详尽,非常感谢!
麦克(Mike)

嗨@Hans我正在使用一个列表List<Pair<Integer, String> listOfTuple。如何使用listOfTuple.add()?如果我愿意- listOfTuple.add(Pair<someInteger, someString>);,那将不起作用。提前致谢。
Me_developer

android版本在单元测试中不可用,这使其并没有真正的用处...
OznOg


31

Apache通用lang3具有Pair类和该线程中提及的其他几个库 C ++ Pair <L,R>的等效项是什么?

符合您原始问题要求的示例:

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

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

11

您可以编写一个通用的Pair <A,B>类,并在数组或列表中使用它。是的,您必须编写一个类,但是您可以为所有类型重用同一类,因此只需要执行一次即可。


我希望看到一个例子!
DivideByHero

1
丹,关于这一点的坏事是,由于类型擦除,不可能仅接受例如Pair <String,Integer> s,不是吗?但是Java也是如此...
Johannes Weiss


我认为这不适用于纯数组,但绝对适用于其他Collections。
Outlaw程序员,

@Outlaw程序员:很好,您可以将其与数组一起使用,但是这很丑陋,因为您必须使用hack来创建通用数组。
丹·代尔

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”或“ 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或其他第三方来做这样的基本工作。升级到Scala的另一个原因...



6

正如您所描述的,首选的解决方案是成对清单(即清单)。

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

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

请记住,集合被设计为包含单一类型的元素。相关实用程序接口(例如Map)实际上不是Collections(即Map未实现Collection接口)。一对也不会实现Collection接口,但是显然在构建更大的数据结构中是有用的类。


我发现这种解决方案比使用通用Pair <K,V>的“获胜”方法更好。它可以完成所有要求的操作,并且开箱即用。我将其用于实现。
绍尔2014年

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

我的意思是,即使PairJava中没有类,也有一些类似的东西:Map.Entry

Map.Entry文档

这是(简化了很多)what 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中-javac编译器的“ Sun”实现的一部分。它仅作为JDK的一部分分发,可能不存在于其他供应商实现中,并且不太可能成为公共API的一部分。
McDowell 2012年

0

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


0

在项目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));
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.