【问题标题】:What is the equivalent of the C++ Pair<L,R> in Java?Java 中的 C++ Pair<L,R> 等价物是什么?
【发布时间】:2010-09-14 10:50:10
【问题描述】:

Java 中没有Pair&lt;L,R&gt; 有充分的理由吗?这个 C++ 构造的等价物是什么?我宁愿避免重新实现我自己的。

1.6 似乎提供了类似的东西 (AbstractMap.SimpleEntry&lt;K,V&gt;),但这看起来很复杂。

【问题讨论】:

  • 为什么AbstractMap.SimpleEntry 很复杂?
  • 因为namig,任意命名一键一值。
  • @sffc JavaFX 不在 JDK7 中的任何默认类路径上,使用它需要手动添加 JFX 运行时库。
  • @Enerccio:所以,您实际上声明“第一”和“第二”不是任意的,而“键”和“值”是?那么这是在 SDK 中没有此类的一个很好的理由。关于“正确”命名的争论将永远存在。

标签: java tuples std-pair


【解决方案1】:

a thread on comp.lang.java.help 中,Hunter Gratzner 给出了一些反对 Java 中存在 Pair 构造的论据。主要论点是Pair 类没有传达关于两个值之间关系的任何语义(你怎么知道“第一”和“第二”是什么意思?)。

更好的做法是编写一个非常简单的类,就像 Mike 提议的那样,为您将使用 Pair 类创建的每个应用程序。 Map.Entry 是一对在其名称中包含其含义的示例。

总而言之,在我看来,最好有一个类Position(x,y)、一个类Range(begin,end) 和一个类Entry(key,value),而不是一个通用的Pair(first,second),它不会告诉我它应该是什么。去做。

【讨论】:

  • Gratzner 正在分裂头发。我们很高兴将单个值作为原始类或内置类返回,而无需将其封装在类中。如果我们要返回一个包含十几个元素的元组,没有人会不同意它应该有自己的类。中间的某个地方是一条(模糊的)分界线。我认为我们的蜥蜴大脑可以轻松应对 Pairs。
  • 我同意伊恩的观点。 Java 让你返回 int;它不会强制您在每次使用时都为 int 创建别名。对并没有太大的不同。
  • 如果我们可以将一对直接解包到您的局部变量中,或者将其转发给一个接受两个参数的方法,那么 Pair 将是一个有用的类。因为我们不能像这样解压它,所以创建一个有意义的类并将值保持在一起看起来并不算太糟糕。而且,如果你真的想要一对,尽管有限制,总有 Object[2] + casts :-)
  • 问题是,如果你不同意 Gratzner,那么在几个地方都有 Pair 实现。 Apache Commons 和 Guava 都有 IIRC。使用那些。但是将某些东西放在主要的 Java 库中意味着它是一种高贵且被认可的做事方式(大写),由于人们不同意它,我们不应该把它放在那里。旧的库中有足够的杂物,我们不要不必要地放更多。
  • @Dragas 当我需要一对值时,那不是 Java ......真的吗?
【解决方案2】:

这是Java。您必须使用描述性类和字段名称制作自己的定制 Pair 类,不要介意您将通过编写 hashCode()/equals() 或一次又一次地实现 Comparable 来重新发明轮子。

【讨论】:

  • 如果您指向包含 Pair 类的 Apache Commong Lang,Java 嘲弄会很好。
  • 或者你可以使用SimpleImmutableEntry
  • 最常见的 IDE 会为您生成适当的 HashCode()/equals。
  • 我同意。 “因为这是 Java”是一个很好的答案。请记住,Java 语言被故意剥夺了某些(咳嗽 c++)使普通程序员感到困惑的特性。这就是为什么 Java 不允许您重新定义运算符。它也不允许多重继承。底线是,如果某些愚蠢的程序员可能滥用它,那么 Java 将很难做到。
  • @JohnHenckel 这就像一位教师在考试中提出简单的问题,因为他希望每个人都能成功。哑巴。
【解决方案3】:

HashMap兼容Pair类:

public class Pair<A, B> {
    private A first;
    private B second;

    public Pair(A first, B second) {
        super();
        this.first = first;
        this.second = second;
    }

    public int hashCode() {
        int hashFirst = first != null ? first.hashCode() : 0;
        int hashSecond = second != null ? second.hashCode() : 0;

        return (hashFirst + hashSecond) * hashSecond + hashFirst;
    }

    public boolean equals(Object other) {
        if (other instanceof Pair) {
            Pair otherPair = (Pair) other;
            return 
            ((  this.first == otherPair.first ||
                ( this.first != null && otherPair.first != null &&
                  this.first.equals(otherPair.first))) &&
             (  this.second == otherPair.second ||
                ( this.second != null && otherPair.second != null &&
                  this.second.equals(otherPair.second))) );
        }

        return false;
    }

    public String toString()
    { 
           return "(" + first + ", " + second + ")"; 
    }

    public A getFirst() {
        return first;
    }

    public void setFirst(A first) {
        this.first = first;
    }

    public B getSecond() {
        return second;
    }

    public void setSecond(B second) {
        this.second = second;
    }
}

【讨论】:

  • 您可能想删除 setter,并设置 first 和 second final,从而使这对不可变。 (如果有人在将组件用作哈希键后更改了组件,就会发生奇怪的事情。
  • return "(" + first.toString() + ", " + second.toString() + ")" 在 toString() 方法中可能会抛出 NullPointerExceptions。这样更好:return "(" + first + ", " + second + ")";
  • 另外,要么将这对标记为“final”,要么将 equals 的第一行更改为 'if (other != null && this.getClass() == other.getClass())'跨度>
  • 抱歉这个随机的nooby问题,但是为什么你在构造函数中调用了super()?
  • @Ibrahim:在这种情况下,这是多余的——如果你把super() 去掉,行为是完全一样的。通常,如果它是可选的,我就会把它去掉,就像它在这里一样。
【解决方案4】:

我能想到的最短的一对如下,使用Lombok

@Data
@AllArgsConstructor(staticName = "of")
public class Pair<F, S> {
    private F first;
    private S second;
}

它具有the answer from @arturh 的所有优点(除了可比性),它具有hashCodeequalstoString 和一个静态“构造函数”。

【讨论】:

  • 漂亮!喜欢!
  • 向我介绍龙目岛,无价之宝。
【解决方案5】:
【解决方案6】:

另一种实现 Pair with 的方式。

  • 公共不可变字段,即简单的数据结构。
  • 相当。
  • 简单的哈希和等号。
  • 简单工厂,因此您不必提供类型。例如pair.of("你好", 1);

    public class Pair<FIRST, SECOND> implements Comparable<Pair<FIRST, SECOND>> {
    
        public final FIRST first;
        public final SECOND second;
    
        private Pair(FIRST first, SECOND second) {
            this.first = first;
            this.second = second;
        }
    
        public static <FIRST, SECOND> Pair<FIRST, SECOND> of(FIRST first,
                SECOND second) {
            return new Pair<FIRST, SECOND>(first, second);
        }
    
        @Override
        public int compareTo(Pair<FIRST, SECOND> o) {
            int cmp = compare(first, o.first);
            return cmp == 0 ? compare(second, o.second) : cmp;
        }
    
        // todo move this to a helper class.
        private static int compare(Object o1, Object o2) {
            return o1 == null ? o2 == null ? 0 : -1 : o2 == null ? +1
                    : ((Comparable) o1).compareTo(o2);
        }
    
        @Override
        public int hashCode() {
            return 31 * hashcode(first) + hashcode(second);
        }
    
        // todo move this to a helper class.
        private static int hashcode(Object o) {
            return o == null ? 0 : o.hashCode();
        }
    
        @Override
        public boolean equals(Object obj) {
            if (!(obj instanceof Pair))
                return false;
            if (this == obj)
                return true;
            return equal(first, ((Pair) obj).first)
                    && equal(second, ((Pair) obj).second);
        }
    
        // todo move this to a helper class.
        private boolean equal(Object o1, Object o2) {
            return o1 == null ? o2 == null : (o1 == o2 || o1.equals(o2));
        }
    
        @Override
        public String toString() {
            return "(" + first + ", " + second + ')';
        }
    }
    

【讨论】:

  • 我喜欢静态工厂方法of。它让人想起 Google Guava 不可变集合。
  • 您在某些时候将o1 转换为Comparable,尽管没有任何迹象表明它实际上会实现该接口。如果这是一个要求,FIRST 类型参数应该是FIRST extends Comparable&lt;?&gt;
  • 我不是 java 人,所以请原谅我的无知,但是您在 TODO cmets 中想到了什么样的帮助类?
  • 31 是 hashCode 的错误常量。例如,如果您使用由 Pair 键控的 HashMap 用于 2D 地图,您会遇到很多冲突。例如 (a*65497)^b 会更适合。
  • @MarioCarneiro ^ 是异或,不是幂
【解决方案7】:

http://www.javatuples.org/index.html 怎么样,我发现它非常有用。

javatuples 为您提供从一到十个元素的元组类:

Unit<A> (1 element)
Pair<A,B> (2 elements)
Triplet<A,B,C> (3 elements)
Quartet<A,B,C,D> (4 elements)
Quintet<A,B,C,D,E> (5 elements)
Sextet<A,B,C,D,E,F> (6 elements)
Septet<A,B,C,D,E,F,G> (7 elements)
Octet<A,B,C,D,E,F,G,H> (8 elements)
Ennead<A,B,C,D,E,F,G,H,I> (9 elements)
Decade<A,B,C,D,E,F,G,H,I,J> (10 elements)

【讨论】:

  • 很有趣,但至少比我想象的要多 5 个类。
  • @maaartinus 至少比我用的多 10 个。
  • @Boann:好的,我保持正确。我曾经使用Pair 并且可以想象可能每50 年使用一次Triplet。现在我使用 Lombok 并在每次需要一对时创建一个小的 4 行类。所以“10 太多”是准确的。
  • 我们需要Bottom (0 element) 类吗? :)
  • 哇,这太丑了。我知道他们正在尝试使其明确,但是像 C# 中具有重载参数的元组会更好。
【解决方案8】:

android提供了Pairclass (http://developer.android.com/reference/android/util/Pair.html),这里是实现:

public class Pair<F, S> {
    public final F first;
    public final S second;

    public Pair(F first, S second) {
        this.first = first;
        this.second = second;
    }

    @Override
    public boolean equals(Object o) {
        if (!(o instanceof Pair)) {
            return false;
        }
        Pair<?, ?> p = (Pair<?, ?>) o;
        return Objects.equal(p.first, first) && Objects.equal(p.second, second);
    }

    @Override
    public int hashCode() {
        return (first == null ? 0 : first.hashCode()) ^ (second == null ? 0 : second.hashCode());
    }

    public static <A, B> Pair <A, B> create(A a, B b) {
        return new Pair<A, B>(a, b);
    }
}

【讨论】:

  • Objects.equal(..) 需要 Guava 库。
  • 将其更改为自 2011 年 (1.7) 以来一直使用 Java 的 Objects.equals(...)
【解决方案9】:

这取决于你想用它做什么。这样做的典型原因是迭代地图,您只需这样做(Java 5+):

Map<String, Object> map = ... ; // just an example
for (Map.Entry<String, Object> entry : map.entrySet()) {
  System.out.printf("%s -> %s\n", entry.getKey(), entry.getValue());
}

【讨论】:

  • 我不确定自定义类在这种情况下会有所帮助:)
  • “这样做的典型原因是迭代地图”。真的吗?
【解决方案10】:

最大的问题可能是无法确保 A 和 B 的不变性(请参阅 How to ensure that type parameters are immutable),因此 hashCode() 可能会在 插入集合后对同一 Pair 给出不一致的结果例如(这会产生未定义的行为,请参阅Defining equals in terms of mutable fields)。对于特定的(非泛型)Pair 类,程序员可以通过仔细选择 A 和 B 来确保其不可变性。

无论如何,从@PeterLawrey 的回答(java 1.7)中清除泛型的警告:

public class Pair<A extends Comparable<? super A>,
                    B extends Comparable<? super B>>
        implements Comparable<Pair<A, B>> {

    public final A first;
    public final B second;

    private Pair(A first, B second) {
        this.first = first;
        this.second = second;
    }

    public static <A extends Comparable<? super A>,
                    B extends Comparable<? super B>>
            Pair<A, B> of(A first, B second) {
        return new Pair<A, B>(first, second);
    }

    @Override
    public int compareTo(Pair<A, B> o) {
        int cmp = o == null ? 1 : (this.first).compareTo(o.first);
        return cmp == 0 ? (this.second).compareTo(o.second) : cmp;
    }

    @Override
    public int hashCode() {
        return 31 * hashcode(first) + hashcode(second);
    }

    // TODO : move this to a helper class.
    private static int hashcode(Object o) {
        return o == null ? 0 : o.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof Pair))
            return false;
        if (this == obj)
            return true;
        return equal(first, ((Pair<?, ?>) obj).first)
                && equal(second, ((Pair<?, ?>) obj).second);
    }

    // TODO : move this to a helper class.
    private boolean equal(Object o1, Object o2) {
        return o1 == o2 || (o1 != null && o1.equals(o2));
    }

    @Override
    public String toString() {
        return "(" + first + ", " + second + ')';
    }
}

非常欢迎添加/更正 :) 特别是我不太确定我对 Pair&lt;?, ?&gt; 的使用。

有关此语法的原因的更多信息,请参阅Ensure that objects implement Comparable 和详细说明How to implement a generic max(Comparable a, Comparable b) function in Java?

【讨论】:

  • Java 整数是 32 位的,第一个哈希码乘以 31 会不会溢出?执行异或不是更好吗?
【解决方案11】:

好消息 JavaFX 有一个键值对。

只需将 JavaFX 添加为依赖项并导入 javafx.util.Pair,然后像在 C++ 中一样简单地使用。

Pair <Key, Value> 

例如

Pair <Integer, Integer> pr = new Pair<Integer, Integer>()

pr.get(key);// will return corresponding value

【讨论】:

  • 坏消息是不是每个人都使用 JavaFX
【解决方案12】:

在我看来,Java 中没有 Pair,因为如果你想直接在 pair 上添加额外的功能(例如 Comparable),你必须绑定类型。在 C++ 中,我们并不关心,如果组成对的类型没有 operator &lt;pair::operator &lt; 也不会编译。

Comparable 无边界示例:

public class Pair<F, S> implements Comparable<Pair<? extends F, ? extends S>> {
    public final F first;
    public final S second;
    /* ... */
    public int compareTo(Pair<? extends F, ? extends S> that) {
        int cf = compare(first, that.first);
        return cf == 0 ? compare(second, that.second) : cf;
    }
    //Why null is decided to be less than everything?
    private static int compare(Object l, Object r) {
        if (l == null) {
            return r == null ? 0 : -1;
        } else {
            return r == null ? 1 : ((Comparable) (l)).compareTo(r);
        }
    }
}

/* ... */

Pair<Thread, HashMap<String, Integer>> a = /* ... */;
Pair<Thread, HashMap<String, Integer>> b = /* ... */;
//Runtime error here instead of compile error!
System.out.println(a.compareTo(b));

Comparable 与编译时检查类型参数是否具有可比性的示例:

public class Pair<
        F extends Comparable<? super F>, 
        S extends Comparable<? super S>
> implements Comparable<Pair<? extends F, ? extends S>> {
    public final F first;
    public final S second;
    /* ... */
    public int compareTo(Pair<? extends F, ? extends S> that) {
        int cf = compare(first, that.first);
        return cf == 0 ? compare(second, that.second) : cf;
    }
    //Why null is decided to be less than everything?
    private static <
            T extends Comparable<? super T>
    > int compare(T l, T r) {
        if (l == null) {
            return r == null ? 0 : -1;
        } else {
            return r == null ? 1 : l.compareTo(r);
        }
    }
}

/* ... */

//Will not compile because Thread is not Comparable<? super Thread>
Pair<Thread, HashMap<String, Integer>> a = /* ... */;
Pair<Thread, HashMap<String, Integer>> b = /* ... */;
System.out.println(a.compareTo(b));

这很好,但是这次你不能在 Pair 中使用不可比较的类型作为类型参数。 人们可能会在某些实用程序类中使用很多比较器来进行配对,但 C++ 人可能不明白。另一种方法是在类型层次结构中编写大量类,类型参数具有不同的界限,但是可能的界限及其组合太多......

【讨论】:

    【解决方案13】:

    Map.Entry 接口非常接近 C++ 对。看具体实现,比如AbstractMap.SimpleEntry 和 AbstractMap.SimpleImmutableEntry 第一项是 getKey(),第二项是 getValue()。

    【讨论】:

    • OP 已经知道这个选项,并且已经详细讨论过。
    【解决方案14】:

    根据Java语言的性质,我想人们实际上并不需要Pair,通常他们需要一个接口。这是一个例子:

    interface Pair<L, R> {
        public L getL();
        public R getR();
    }
    

    因此,当人们想要返回两个值时,他们可以执行以下操作:

    ... //Calcuate the return value
    final Integer v1 = result1;
    final String v2 = result2;
    return new Pair<Integer, String>(){
        Integer getL(){ return v1; }
        String getR(){ return v2; }
    }
    

    这是一个非常轻量级的解决方案,它回答了“Pair&lt;L,R&gt; 的语义是什么?”这个问题。答案是,这是一个具有两种(可能不同)类型的接口构建,并且它具有返回每种类型的方法。您可以为其添加更多语义。例如,如果您正在使用位置并且真的想在代码中指出它,您可以定义包含IntegerPositionXPositionY,以组成Pair&lt;PositionX,PositionY&gt;。如果 JSR 308 可用,您也可以使用 Pair&lt;@PositionX Integer, @PositionY Ingeger&gt; 来简化它。

    编辑: 我应该在这里指出的一件事是,上面的定义明确地关联了类型参数名称和方法名称。这是对那些认为Pair 缺乏语义信息的人的回答。其实getL这个方法的意思是“给我对应类型参数L的类型的元素”,这确实有点意思。

    编辑: 这是一个简单的实用程序类,可以让生活更轻松:

    class Pairs {
        static <L,R> Pair<L,R> makePair(final L l, final R r){
            return new Pair<L,R>(){
                public L getL() { return l; }
                public R getR() { return r; }   
            };
        }
    }
    

    用法:

    return Pairs.makePair(new Integer(100), "123");
    

    【讨论】:

    • equalshashCodetoString 怎么样?
    • 好吧,这只是一个最小的实现。如果你需要更多,你可以编写一些辅助函数来使它更容易,但你仍然需要编写代码。
    • 要实现toString,您需要更多地了解这两个字段之间的关系。
    • 我的意思是提供class 可能比interface 更好,因为它可以实现这些东西。
    【解决方案15】:

    JavaFX(与 Java 8 捆绑在一起)具有 Pair

    【讨论】:

    • implementation of hashCode in javafx.util.Pair 可能会导致琐碎案例的冲突。在 HashMap/HashTable 中使用它仍然可以工作,因为 Java 除了检查哈希码之外还检查值的相等性,但需要注意这一点。
    • 这是一个非常标准且普遍推荐的 hashCode 实现。调用hashCode()任何 代码应该会发生冲突。请注意,Java 本身不调用此方法。它用于用户代码,包括库。
    【解决方案16】:

    正如许多其他人已经说过的那样,Pair 类是否有用实际上取决于用例。

    我认为,对于私有辅助函数,使用 Pair 类是完全合法的,如果这样可以使您的代码更具可读性,并且不值得使用其所有样板代码创建另一个值类。

    另一方面,如果您的抽象级别要求您清楚地记录包含两个对象或值的类的语义,那么您应该为它编写一个类。如果数据是业务对象,通常就是这种情况。

    与往常一样,这需要熟练的判断力。

    对于第二个问题,我推荐 Apache Commons 库中的 Pair 类。这些可能被视为 Java 的扩展标准库:

    https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/tuple/Pair.html

    您可能还想看看 Apache Commons 的 EqualsBuilderHashCodeBuilderToStringBuilder,它们简化了为您的业务对象编写值类。

    【讨论】:

    【解决方案17】:

    您可以使用 javafx 实用程序类 Pair,它的用途与 c++ 中的 pair 相同。 https://docs.oracle.com/javafx/2/api/javafx/util/Pair.html

    【讨论】:

      【解决方案18】:
      Collections.singletonMap(left, rigth);
      

      【讨论】:

        【解决方案19】:

        另一个简洁的 lombok 实现

        import lombok.Value;
        
        @Value(staticConstructor = "of")
        public class Pair<F, S> {
            private final F first;
            private final S second;
        }
        

        【讨论】:

          【解决方案20】:

          简单的方式 Object [] - 可以用作任何维度的元组

          【讨论】:

          • 任何维度,是的。但是:创建起来很麻烦,而且不是类型安全的。
          • 对象[] 对 = {left, right}; // 不麻烦。
          【解决方案21】:

          尽管在语法上相似,Java 和 C++ 有非常不同的范例。像 Java 那样写 C++ 是糟糕的 C++,像 C++ 那样写 Java 是糟糕的 Java。

          使用像 Eclipse 这样的基于反射的 IDE,编写“pair”类的必要功能既快速又简单。创建类,定义两个字段,使用各种“生成 XX”菜单选项在几秒钟内填写类。如果您想要 Comparable 界面,也许您必须快速键入“compareTo”。

          在语言中使用单独的声明/定义选项,C++ 代码生成器并不是那么好,因此手工编写小的实用程序类更加耗时乏味。因为这对是一个模板,你不必为你不使用的函数付费,并且 typedef 工具允许为代码分配有意义的类型名,所以关于“无语义”的反对​​意见并没有真正成立。

          【讨论】:

            【解决方案22】:

            Pair 是个好东西,可以作为复杂泛型的基本构造单元,例如,这是来自我的代码:

            WeakHashMap<Pair<String, String>, String> map = ...
            

            和 Haskell 的元组一样

            【讨论】:

            • 更好或更坏。想象一下,您有一个函数组合了它的两个参数(例如,将图形合并为一个)并且需要缓存它。在这里,Pair 是最优的,因为没有特殊的语义。为一个清晰的概念取一个清晰的名称固然好,但寻找一个“第一”和“第二”都很好用的名称就不行了。
            【解决方案23】:

            对于像 Java 这样的编程语言,大多数程序员用来表示类似对的数据结构的替代数据结构是两个数组,并且通过相同的索引访问数据

            示例:http://www-igm.univ-mlv.fr/~lecroq/string/node8.html#SECTION0080

            这并不理想,因为数据应该绑定在一起,但结果也很便宜。此外,如果您的用例需要存储坐标,那么最好构建自己的数据结构。

            我的图书馆里有这样的东西

            public class Pair<First,Second>{.. }
            

            【讨论】:

              【解决方案24】:

              您可以使用 Google 的 AutoValue 库 - https://github.com/google/auto/tree/master/value

              您创建一个非常小的抽象类并使用@AutoValue 对其进行注释,注释处理器会为您生成一个具有值语义的具体类。

              【讨论】:

                【解决方案25】:

                为方便起见,这里有一些具有多个元组度数的库:

                • JavaTuples。 1-10 级的元组就是它的全部。
                • JavaSlang。 0-8 级元组和许多其他功能性好东西。
                • jOOλ。 0-16 级的元组和其他一些功能性好东西。 (免责声明,我在维护公司工作)
                • Functional Java。 0-8 级元组和许多其他功能性好东西。

                已提到其他库至少包含 Pair 元组。

                具体来说,在使用大量结构类型而非名义类型 (as advocated in the accepted answer) 的函数式编程环境中,这些库及其元组非常方便。

                【讨论】:

                  【解决方案26】:

                  在 Devoxx'14 的 QA 会议期间,Brian Goetz、Paul Sandoz 和 Stuart Marks explain why

                  一旦引入value types,在标准库中拥有泛型对类将变成技术债务。

                  另请参阅: Does Java SE 8 have Pairs or Tuples?

                  【讨论】:

                    【解决方案27】:

                    我注意到散布在此处的所有 Pair 实现都将含义归因于两个值的顺序。当我想到一对时,我会想到两个项目的组合,其中两者的顺序并不重要。这是我对无序对的实现,使用 hashCodeequals 覆盖以确保集合中的所需行为。也可以克隆。

                    /**
                     * The class <code>Pair</code> models a container for two objects wherein the
                     * object order is of no consequence for equality and hashing. An example of
                     * using Pair would be as the return type for a method that needs to return two
                     * related objects. Another good use is as entries in a Set or keys in a Map
                     * when only the unordered combination of two objects is of interest.<p>
                     * The term "object" as being a one of a Pair can be loosely interpreted. A
                     * Pair may have one or two <code>null</code> entries as values. Both values
                     * may also be the same object.<p>
                     * Mind that the order of the type parameters T and U is of no importance. A
                     * Pair&lt;T, U> can still return <code>true</code> for method <code>equals</code>
                     * called with a Pair&lt;U, T> argument.<p>
                     * Instances of this class are immutable, but the provided values might not be.
                     * This means the consistency of equality checks and the hash code is only as
                     * strong as that of the value types.<p>
                     */
                    public class Pair<T, U> implements Cloneable {
                    
                        /**
                         * One of the two values, for the declared type T.
                         */
                        private final T object1;
                        /**
                         * One of the two values, for the declared type U.
                         */
                        private final U object2;
                        private final boolean object1Null;
                        private final boolean object2Null;
                        private final boolean dualNull;
                    
                        /**
                         * Constructs a new <code>Pair&lt;T, U&gt;</code> with T object1 and U object2 as
                         * its values. The order of the arguments is of no consequence. One or both of
                         * the values may be <code>null</code> and both values may be the same object.
                         *
                         * @param object1 T to serve as one value.
                         * @param object2 U to serve as the other value.
                         */
                        public Pair(T object1, U object2) {
                    
                            this.object1 = object1;
                            this.object2 = object2;
                            object1Null = object1 == null;
                            object2Null = object2 == null;
                            dualNull = object1Null && object2Null;
                    
                        }
                    
                        /**
                         * Gets the value of this Pair provided as the first argument in the constructor.
                         *
                         * @return a value of this Pair.
                         */
                        public T getObject1() {
                    
                            return object1;
                    
                        }
                    
                        /**
                         * Gets the value of this Pair provided as the second argument in the constructor.
                         *
                         * @return a value of this Pair.
                         */
                        public U getObject2() {
                    
                            return object2;
                    
                        }
                    
                        /**
                         * Returns a shallow copy of this Pair. The returned Pair is a new instance
                         * created with the same values as this Pair. The values themselves are not
                         * cloned.
                         *
                         * @return a clone of this Pair.
                         */
                        @Override
                        public Pair<T, U> clone() {
                    
                            return new Pair<T, U>(object1, object2);
                    
                        }
                    
                        /**
                         * Indicates whether some other object is "equal" to this one.
                         * This Pair is considered equal to the object if and only if
                         * <ul>
                         * <li>the Object argument is not null,
                         * <li>the Object argument has a runtime type Pair or a subclass,
                         * </ul>
                         * AND
                         * <ul>
                         * <li>the Object argument refers to this pair
                         * <li>OR this pair's values are both null and the other pair's values are both null
                         * <li>OR this pair has one null value and the other pair has one null value and
                         * the remaining non-null values of both pairs are equal
                         * <li>OR both pairs have no null values and have value tuples &lt;v1, v2> of
                         * this pair and &lt;o1, o2> of the other pair so that at least one of the
                         * following statements is true:
                         * <ul>
                         * <li>v1 equals o1 and v2 equals o2
                         * <li>v1 equals o2 and v2 equals o1
                         * </ul>
                         * </ul>
                         * In any other case (such as when this pair has two null parts but the other
                         * only one) this method returns false.<p>
                         * The type parameters that were used for the other pair are of no importance.
                         * A Pair&lt;T, U> can return <code>true</code> for equality testing with
                         * a Pair&lt;T, V> even if V is neither a super- nor subtype of U, should
                         * the the value equality checks be positive or the U and V type values
                         * are both <code>null</code>. Type erasure for parameter types at compile
                         * time means that type checks are delegated to calls of the <code>equals</code>
                         * methods on the values themselves.
                         *
                         * @param obj the reference object with which to compare.
                         * @return true if the object is a Pair equal to this one.
                         */
                        @Override
                        public boolean equals(Object obj) {
                    
                            if(obj == null)
                                return false;
                    
                            if(this == obj)
                                return true;
                    
                            if(!(obj instanceof Pair<?, ?>))
                                return false;
                    
                            final Pair<?, ?> otherPair = (Pair<?, ?>)obj;
                    
                            if(dualNull)
                                return otherPair.dualNull;
                    
                            //After this we're sure at least one part in this is not null
                    
                            if(otherPair.dualNull)
                                return false;
                    
                            //After this we're sure at least one part in obj is not null
                    
                            if(object1Null) {
                                if(otherPair.object1Null) //Yes: this and other both have non-null part2
                                    return object2.equals(otherPair.object2);
                                else if(otherPair.object2Null) //Yes: this has non-null part2, other has non-null part1
                                    return object2.equals(otherPair.object1);
                                else //Remaining case: other has no non-null parts
                                    return false;
                            } else if(object2Null) {
                                if(otherPair.object2Null) //Yes: this and other both have non-null part1
                                    return object1.equals(otherPair.object1);
                                else if(otherPair.object1Null) //Yes: this has non-null part1, other has non-null part2
                                    return object1.equals(otherPair.object2);
                                else //Remaining case: other has no non-null parts
                                    return false;
                            } else {
                                //Transitive and symmetric requirements of equals will make sure
                                //checking the following cases are sufficient
                                if(object1.equals(otherPair.object1))
                                    return object2.equals(otherPair.object2);
                                else if(object1.equals(otherPair.object2))
                                    return object2.equals(otherPair.object1);
                                else
                                    return false;
                            }
                    
                        }
                    
                        /**
                         * Returns a hash code value for the pair. This is calculated as the sum
                         * of the hash codes for the two values, wherein a value that is <code>null</code>
                         * contributes 0 to the sum. This implementation adheres to the contract for
                         * <code>hashCode()</code> as specified for <code>Object()</code>. The returned
                         * value hash code consistently remain the same for multiple invocations
                         * during an execution of a Java application, unless at least one of the pair
                         * values has its hash code changed. That would imply information used for 
                         * equals in the changed value(s) has also changed, which would carry that
                         * change onto this class' <code>equals</code> implementation.
                         *
                         * @return a hash code for this Pair.
                         */
                        @Override
                        public int hashCode() {
                    
                            int hashCode = object1Null ? 0 : object1.hashCode();
                            hashCode += (object2Null ? 0 : object2.hashCode());
                            return hashCode;
                    
                        }
                    
                    }
                    

                    此实现已经过适当的单元测试,并且已经尝试在 Set 和 Map 中使用。

                    请注意,我并没有声称要在公共领域发布此内容。这是我刚刚编写的用于应用程序的代码,所以如果您要使用它,请不要直接复制并弄乱 cmets 和名称。赶上我的漂移?

                    【讨论】:

                    • 其实,查看每一页的底部:“用户贡献在 cc-wiki 下获得许可”
                    • 啊,我没注意到。感谢您的提醒。在这种情况下,请根据该许可使用您认为合适的代码。
                    • 问题是关于一个 C++ 等效对 - 它是有序的。另外我认为,只要有一个对 Pair 对象的引用,并且那些是可变的,在集合中插入 Pairs 可能会导致未定义的行为。
                    【解决方案28】:

                    如果有人想要一个简单易用的版本,我在https://github.com/lfac-pt/Java-Pair 提供了我的版本。此外,非常欢迎改进!

                    【讨论】:

                      【解决方案29】:

                      com.sun.tools.javac.util.Pair 是一对的简单实现。 可以在 jdk1.7.0_51\lib\tools.jar 中找到。

                      除了 org.apache.commons.lang3.tuple.Pair,它不仅仅是一个接口。

                      【讨论】:

                      • 不过,没有人应该使用 JDK 内部 API。
                      【解决方案30】:
                      public class Pair<K, V> {
                      
                          private final K element0;
                          private final V element1;
                      
                          public static <K, V> Pair<K, V> createPair(K key, V value) {
                              return new Pair<K, V>(key, value);
                          }
                      
                          public Pair(K element0, V element1) {
                              this.element0 = element0;
                              this.element1 = element1;
                          }
                      
                          public K getElement0() {
                              return element0;
                          }
                      
                          public V getElement1() {
                              return element1;
                          }
                      
                      }
                      

                      用法:

                      Pair<Integer, String> pair = Pair.createPair(1, "test");
                      pair.getElement0();
                      pair.getElement1();
                      

                      不可变,只有一对!

                      【讨论】:

                      • 哦,哇。另一个?尝试将您的泛型与更复杂的泛型一起使用 - 在某些时候,它将无法推断出适当的类型。另外,以下应该是可能的:Pair&lt;Object, Object&gt; pair = Pair.createPair("abc", "def") 但我认为需要用您的代码编写Pair.createPair((Object)"abc", (Object)"def")
                      • 你可以用这个替换静态方法:@SuppressWarnings("unchecked") public static &lt;K, V, X, Y&gt; Pair&lt;X, Y&gt; createPair(K key, V value) { return new Pair&lt;X, Y&gt;((X) key, (Y) value); } 但我不知道这是否是一个好习惯
                      • 不,这可能只会让事情变得更糟。根据我的经验,至少有一个编译器(尝试 java6、java7、javadoc 和 eclipse java 编译器)会抱怨。在我的实验中,传统的new Pair&lt;Object, Object&gt;("abc", "def") 是最可靠的。
                      猜你喜欢
                      • 1970-01-01
                      • 2010-12-07
                      • 2010-11-22
                      • 1970-01-01
                      • 2011-10-13
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多