Java中C++ Pair <L,R>的等价物是什么?

Dav*_*nds 655 java tuples std-pair

有没有一个很好的理由为什么Pair<L,R>Java中没有?这个C++构造的等价物是什么?我宁愿避免重新实现自己.

似乎1.6提供类似的东西(AbstractMap.SimpleEntry<K,V>),但这看起来很复杂.

Luc*_*lle 392

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

更好的做法是为每个应用程序编写一个非常简单的类,就像Mike提出的Pair类一样.Map.Entry是一个在其名称中带有其含义的对的示例.

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

  • 格拉兹纳正在分裂头发.我们很高兴将单个值作为基元或内置类返回,而不将其封装在类中.如果我们要返回一个十几个元素的元组,没有人会不同意它应该有自己的类.中间的某处是(模糊)分界线.我认为我们的蜥蜴大脑可以轻松应对Pairs. (132认同)
  • 我同意伊恩的观点.Java允许你返回int; 每次使用时,它都不会强制您为int创建别名.对不是很不一样. (22认同)
  • 如果我们可以将一对直接解包到您的局部变量,或者将它转发给一个带有两个参数的方法,那么Pair将是一个有用的类.因为我们不能像这样解压缩它,创建一个有意义的类并将值保持在一起看起来并不太糟糕.并且,如果你真的想要一对,尽管有限制,总有对象[2] +演员:-) (5认同)
  • @Dragas 当我需要一对值时,那不是 Java ……真的吗? (2认同)

And*_*rey 150

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

  • +1用于模拟Java的冗长.-1实际上没有回答问题. (120认同)
  • 这不回答"为什么"这个问题.(除非你认为'这是java'的答案) (55认同)
  • 第一句话是回答"为什么?"的问题.这是Java,就是这样. (32认同)
  • 如果您指向包含Pair类的Apache Commong Lang,那么Java-mockery就可以了. (17认同)
  • 或者你可以使用`SimpleImmutableEntry` (6认同)
  • 如果您从电影300中大喊那位愤怒的斯巴达人,那就更好了。“这……是……JAVA!” (2认同)
  • 我同意。“因为这是 Java”是一个很好的答案。请记住,Java 语言有意剥夺了某些让普通程序员感到困惑的(咳咳 c++)特性。这就是 Java 不允许您重新定义运算符的原因。它也不允许多重继承。底线是,如果一些愚蠢的程序员可能会滥用它,那么 Java 将很难做到。 (2认同)

art*_*urh 102

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 您可能想要删除setter,并进行第一和第二次final,从而使该对不可变.(如果有人在将组件用作哈希键后更改了组件,则会发生奇怪的事情). (134认同)
  • 返回"("+"first.toString()+","+ second.toString()+")"in toString()方法可能会抛出NullPointerExceptions.这样更好:返回"("+ + first +","+ second +")"; (21认同)
  • 抱歉随机的nooby问题,但为什么你在构造函数中调用super()? (7认同)
  • 另外,要么将对标记为"final",要么将第一行等于'if(other!= null && this.getClass()== other.getClass())' (6认同)
  • @Ibrahim:在这种情况下,它是多余的---如果你把`super()`拿出来,行为就完全一样了.通常情况下,如果它是可选的,我会把它丢掉,就像在这里一样. (6认同)

Mic*_*fel 53

使用Lombok,我能想到的最短的一对是:

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

它具有的所有优点的答案从@arturh(可比性除外),它有hashCode,equals,toString和静态的"构造".


Pet*_*rey 31

另一种实现Pair的方法.

  • 公共不可变字段,即简单的数据结构.
  • 可比.
  • 简单的哈希和等于.
  • 简单的工厂,所以你不必提供类型.例如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 + ')';
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

  • 我喜欢静态工厂方法`of`.它提醒**Google Guava**不可变的收藏品. (10认同)
  • 你在某些时候将`o1`转换为`Comparable`,即使没有任何东西表明它会实际实现该接口.如果这是一个要求,`FIRST`类型参数应该是`FIRST extends Comparable <?>`. (7认同)
  • 31是hashCode的坏常量.例如,如果使用由Pair <Integer,Integer>键入的HashMap用于2D地图,则会发生许多冲突.例如(a*65497)^ b将更适合. (3认同)

cyb*_*ion 27

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

javatuples为您提供从1到10个元素的元组类:

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)
Run Code Online (Sandbox Code Playgroud)

  • 有趣,但至少有5个课程比我想象的要多. (6认同)
  • @Boann:好的,我保持纠正.我曾经使用`Pair`并且可以设想每50年使用一次`Triplet`.现在我使用Lombok并在每次需要一对时创建一个小的4行类.所以"10太多"是确切的. (6认同)
  • 我们需要一个`Bottom(0元素)`类吗?:) (4认同)
  • @maaartinus至少比我使用的多10个. (3认同)
  • 哇,这太丑了.我知道他们试图让它变得明确,但是像C#中那样超载params的元组本来会更好. (2认同)

cle*_*tus 12

这取决于你想用它做什么.这样做的典型原因是迭代地图,您只需执行此操作(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());
}
Run Code Online (Sandbox Code Playgroud)

  • "这样做的典型原因是迭代地图".真? (31认同)

she*_*pya 12

android提供了Pair类(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);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 将其更改为自2011年开始使用Java(1.7)以来的Objects.equals(...)。 (2认同)

Mr_*_*s_D 8

最大的问题可能是无法确保A和B的不变性(请参阅如何确保类型参数是不可变的)因此hashCode()可能会插入集合之后为同一对提供不一致的结果(这会产生未定义的行为) ,请参阅根据可变字段定义等于).对于特定(非泛型)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 + ')';
    }
}
Run Code Online (Sandbox Code Playgroud)

非常欢迎添加/更正:)特别是我对我的使用不太确定Pair<?, ?>.

有关此语法原因的详细信息,请参阅确保对象实现Comparable以及详细说明如何max(Comparable a, Comparable b)在Java中实现泛型函数?


RAJ*_*MAR 6

好消息JavaFX具有关键价值。

只需将javafx添加为依赖项并导入javafx.util.Pair

并像中那样简单地使用c++

Pair <Key, Value> 
Run Code Online (Sandbox Code Playgroud)

例如

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

pr.get(key);// will return corresponding value
Run Code Online (Sandbox Code Playgroud)

  • 坏消息是并不是每个人都使用 JavaFX (2认同)

小智 5

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

没有边界的可比较的示例:

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));
Run Code Online (Sandbox Code Playgroud)

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));
Run Code Online (Sandbox Code Playgroud)

这很好,但是这次你可能不会在Pair中使用非可比类型作为类型参数.在某些实用程序类中,可能会使用大量的Comparators for Pair,但C++人员可能无法获得它.另一种方法是在类型层次结构中编写许多类,在类型参数上使用不同的边界,但是有太多可能的边界及其组合......


Pet*_*etz 5

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

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

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

一如既往,它需要熟练的判断.

对于第二个问题,我建议使用Apache Commons库中的Pair类.那些可能被视为Java的扩展标准库:

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

您可能还想看看Apache Commons的EqualsBuilder,HashCodeBuilderToStringBuilder,它们简化了为业务对象编写值类的过程.


Ear*_*ine 5

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

interface Pair<L, R> {
    public L getL();
    public R getR();
}
Run Code Online (Sandbox Code Playgroud)

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

... //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; }
}
Run Code Online (Sandbox Code Playgroud)

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

编辑:我应该在这里指出的一件事是上面的定义明确地关联了类型参数名称和方法名称。这是对那些认为 aPair缺乏语义信息的人的回答。实际上,该方法的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; }   
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

return Pairs.makePair(new Integer(100), "123");
Run Code Online (Sandbox Code Playgroud)


Car*_*ght 5

JavaFX(与Java 8捆绑在一起)具有Pair <A,B>类


小智 5

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


Sat*_*san 5

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