What is the equivalent of the C++ Pair in Java?

前端 未结 30 1063
一个人的身影
一个人的身影 2020-11-22 02:29

Is there a good reason why there is no Pair in Java? What would be the equivalent of this C++ construct? I would rather avoid reimplementing my own.<

相关标签:
30条回答
  • 2020-11-22 02:43

    The biggest problem is probably that one can't ensure immutability on A and B (see How to ensure that type parameters are immutable) so hashCode() may give inconsistent results for the same Pair after is inserted in a collection for instance (this would give undefined behavior, see Defining equals in terms of mutable fields). For a particular (non generic) Pair class the programmer may ensure immutability by carefully choosing A and B to be immutable.

    Anyway, clearing generic's warnings from @PeterLawrey's answer (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 + ')';
        }
    }
    

    Additions/corrections much welcome :) In particular I am not quite sure about my use of Pair<?, ?>.

    For more info on why this syntax see Ensure that objects implement Comparable and for a detailed explanation How to implement a generic max(Comparable a, Comparable b) function in Java?

    0 讨论(0)
  • 2020-11-22 02:46

    Brian Goetz, Paul Sandoz and Stuart Marks explain why during QA session at Devoxx'14.

    Having generic pair class in standard library will turn into technical debt once value types introduced.

    See also: Does Java SE 8 have Pairs or Tuples?

    0 讨论(0)
  • 2020-11-22 02:50

    The shortest pair that I could come up with is the following, using Lombok:

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

    It has all the benefits of the answer from @arturh (except the comparability), it has hashCode, equals, toString and a static “constructor”.

    0 讨论(0)
  • 2020-11-22 02:50

    I noticed all the Pair implementations being strewn around here attribute meaning to the order of the two values. When I think of a pair, I think of a combination of two items in which the order of the two is of no importance. Here's my implementation of an unordered pair, with hashCode and equals overrides to ensure the desired behaviour in collections. Also cloneable.

    /**
     * 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;
    
        }
    
    }
    

    This implementation has been properly unit tested and the use in a Set and Map has been tried out.

    Notice I'm not claiming to release this in the public domain. This is code I've just written for use in an application, so if you're going to use it, please refrain from making a direct copy and mess about with the comments and names a bit. Catch my drift?

    0 讨论(0)
  • 2020-11-22 02:51

    another terse lombok implementation

    import lombok.Value;
    
    @Value(staticConstructor = "of")
    public class Pair<F, S> {
        private final F first;
        private final S second;
    }
    
    0 讨论(0)
  • 2020-11-22 02:52

    Apache Commons Lang 3.0+ has a few Pair classes: http://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/tuple/package-summary.html

    0 讨论(0)
提交回复
热议问题