java 元祖类型Tuple

提出问题

很多时候,方法需要返回 2个值 或 多个值

比如返回 姓名、年龄、性别,除了使用 数组 Array、集合(List、Set、Map)这些容器类型之外,在Java中我们就必须创建一个Class来作为返回类型。

但很多时候只需要 2 至 5 个值,这个时候非要定义一个class就很烦琐了

解决

此时可以使用 元组 Tuple

元祖 Tuple

和列表List一样,都用于数据存储,包含 多个数据

但是和列表 不同的是:列表只能存储 相同 的数据类型(不用泛型就存成 Object )

而元组可以存储 不同的数据类型,比如同时存储 int、string、list

很多语言都提供 元组 Tuple ,比如 .NET Framework 最多支持7个元素的元组;Scala最多支持22个元素的元组;C++(STL)的Tuple;

JDK原生不支持元祖,但 github的 javatuples支持元祖:
https://github.com/javatuples/javatuples

限制

数量限制

元祖一般有最大值,不能无限添加,个人认为,数量 2 - 3 个 最为合理,超过 4个,不易理解,还是建议用封装类

比如:用在方法返回值,可以返回 2 -3 个值,容易说明和理解

不能修改

从 数据结构 来讲,元祖中的元素 不能修改

代码实现

下面是从 hbase 中反编译的 Tuple 源码,略有改动

Tuple2

可以存2个元素

import java.io.Serializable;

public class Tuple2<T1, T2> implements Serializable {

    private static final long serialVersionUID = -3986244606585552569L;
    protected T1 first = null;
    protected T2 second = null;

    /**
     * Default constructor.
     */
    public Tuple2() {
    }

    /**
     * Constructor
     *
     * @param a operand
     * @param b operand
     */
    public Tuple2(T1 a, T2 b) {
        this.first = a;
        this.second = b;
    }

    /**
     * Constructs a new pair, inferring the type via the passed arguments
     *
     * @param <T1> type for first
     * @param <T2> type for second
     * @param a    first element
     * @param b    second element
     * @return a new pair containing the passed arguments
     */
    public static <T1, T2> Tuple2<T1, T2> create(T1 a, T2 b) {
        return new Tuple2<>(a, b);
    }

    /**
     * Replace the first element of the pair.
     *
     * @param a operand
     */
    public void setFirst(T1 a) {
        this.first = a;
    }

    /**
     * Replace the second element of the pair.
     *
     * @param b operand
     */
    public void setSecond(T2 b) {
        this.second = b;
    }

    /**
     * Return the first element stored in the pair.
     *
     * @return T1
     */
    public T1 getFirst() {
        return first;
    }

    /**
     * Return the second element stored in the pair.
     *
     * @return T2
     */
    public T2 getSecond() {
        return second;
    }

    private static boolean equals(Object x, Object y) {
        return (x == null && y == null) || (x != null && x.equals(y));
    }

    @Override
    @SuppressWarnings("unchecked")
    public boolean equals(Object other) {
        return other instanceof Tuple2 && equals(first, ((Tuple2) other).first) &&
                equals(second, ((Tuple2) other).second);
    }

    @Override
    public int hashCode() {
        if (first == null)
            return (second == null) ? 0 : second.hashCode() + 1;
        else if (second == null)
            return first.hashCode() + 2;
        else
            return first.hashCode() * 17 + second.hashCode();
    }

    @Override
    public String toString() {
        return "{" + getFirst() + "," + getSecond() + "}";
    }

}

Tuple3

public class Tuple3<A, B, C> {
    private A first;
    private B second;
    private C third;

    // default constructor
    public Tuple3() {

    }

    public Tuple3(A first, B second, C third) {
        this.first = first;
        this.second = second;
        this.third = third;
    }

    // ctor cannot infer types w/o warning but a method can.
    public static <A, B, C> Tuple3<A, B, C> create(A first, B second, C third) {
        return new Tuple3<>(first, second, third);
    }

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

        return (hashFirst >> 1) ^ hashSecond ^ (hashThird << 1);
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof Tuple3)) {
            return false;
        }

        Tuple3<?, ?, ?> otherTriple = (Tuple3<?, ?, ?>) obj;

        if (first != otherTriple.first && (first != null && !(first.equals(otherTriple.first))))
            return false;
        if (second != otherTriple.second && (second != null && !(second.equals(otherTriple.second))))
            return false;
        if (third != otherTriple.third && (third != null && !(third.equals(otherTriple.third))))
            return false;

        return true;
    }

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

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

    public C getThird() {
        return third;
    }

    public void setThird(C third) {
        this.third = third;
    }
}