java中Pair


在这篇文章中,我们讨论了一个非常有用的编程概念,配对(Pair)。配对提供了一种方便方式来处理简单的键值关联,当我们想从方法返回两个值时特别有用。

在核心Java库中可以使用配对(Pair)的实现。除此之外,某些第三方库,比如Apache Commons和Vavr,已经在各自的api中公开了这个功能。

核心java配对实现
Pair类
Pair类在javafx.util 包中,类构造函数有两个参数,键及对应值:

Pair<Integer, String> pair = new Pair<>(1, "One");
Integer key = pair.getKey();
String value = pair.getValue();
  • 示例描述使用Pair类实现简单Integer到String的映射。示例中getKey方法返回key对象,getValue方法返回对应值对象。

AbstractMap.SimpleEntry 和 AbstractMap.SimpleImmutableEntry
SimpleEntry定义在抽象类AbstractMap里面,其构造方法与Pair类似:

AbstractMap.SimpleEntry<Integer, String> entry 
  = new AbstractMap.SimpleEntry<>(1, "one");
Integer key = entry.getKey();
String value = entry.getValue();
  • 其键和值可以通过标准的getter和setter方法获得。
  • 另外AbstractMap 类还包含一个嵌套类,表示不可变配对:SimpleImmutableEntry 类。
AbstractMap.SimpleImmutableEntry<Integer, String> entry
  = new AbstractMap.SimpleImmutableEntry<>(1, "one");
  • 应用方式与可变的配对一样,除了配置的值不能修改,尝试修改会抛出UnsupportedOperationException异常。

Apache Commons
在Apache Commons库中,org.apache.commons.lang3.tuple 包中提供Pair抽象类,不能被直接实例化。
其有两个子类,分别代表可变与不可变配对:ImmutablePair 和 MutablePair。两者都实现了访问key/value以及setter和getter方法:

ImmutablePair<Integer, String> pair = new ImmutablePair<>(2, "Two");
Integer key = pair.getKey();
String value = pair.getValue();

尝试在ImmutablePair 执行setValue方法,会抛出UnsupportedOperationException异常。但在可变配对上执行完全正常:

Pair<Integer, String> pair = new MutablePair<>(3, "Three");
pair.setValue("New Three");

Vavr库
Vavr库中不可变的Tuple2类提供配对功能:

Tuple2<Integer, String> pair = new Tuple2<>(4, "Four");
Integer key = pair._1();
String value = pair._2();

在这个实现中,创建对象后不能修改,所以更新方法返回改变后的新实例:

tuplePair = pair.update2("New Four");

源码

javafx.util
public class Pair<K,V> implements Serializable{

    private K key;

    public K getKey() { return key; }

    private V value;

    public V getValue() { return value; }

    public Pair(@NamedArg("key") K key, @NamedArg("value") V value) {
        this.key = key;
        this.value = value;
    }

    @Override
    public String toString() {
        return key + "=" + value;
    }

    @Override
    public int hashCode() {
        return key.hashCode() * 13 + (value == null ? 0 : value.hashCode());
    }

     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
         if (o instanceof Pair) {
             Pair pair = (Pair) o;
             if (key != null ? !key.equals(pair.key) : pair.key != null) return false;
             if (value != null ? !value.equals(pair.value) : pair.value != null) return false;
             return true;
         }
         return false;
     }
 }
org.apache.commons.lang3.tuple
package org.apache.commons.lang3.tuple;
import java.io.Serializable;
import java.util.Map;
import java.util.Objects;

import org.apache.commons.lang3.builder.CompareToBuilder;
/**
* 抽象类实现Map.Entry接口  
*/
public abstract class Pair<L, R> implements Map.Entry<L, R>, Comparable<Pair<L, R>>, Serializable {

    private static final long serialVersionUID = 4954918890077093841L;
  /**
   * 返回一个不可变的ImmutablePair对象,即:左元素不可变、右元素不可变,Map的值也不可变
   */
    public static <L, R> Pair<L, R> of(final L left, final R right) {
        return new ImmutablePair<>(left, right);
    }
   /**
   * 获取左元素
   */
    public abstract L getLeft();
   /**
   * 获取右元素
   */
    public abstract R getRight();
   /**
   * 从Map中获取key
   */
    @Override
    public final L getKey() {
        return getLeft();
    }
   /**
   * 获取Map中value
   */
    @Override
    public R getValue() {
        return getRight();
    }
   /**
   * 比较基于左元素和右元素的配对
   */
    @Override
    public int compareTo(final Pair<L, R> other) {
      return new CompareToBuilder().append(getLeft(), other.getLeft())
              .append(getRight(), other.getRight()).toComparison();
    }
  /**
   * 比较两个Pair对应的值是否相等,基于左右两个元素
   */
    @Override
    public boolean equals(final Object obj) {
        if (obj == this) {
            return true;
        }
        if (obj instanceof Map.Entry<?, ?>) {
            final Map.Entry<?, ?> other = (Map.Entry<?, ?>) obj;
            return Objects.equals(getKey(), other.getKey())
                    && Objects.equals(getValue(), other.getValue());
        }
        return false;
    }
   /**
   * 返回合适的哈希代码
   */
    @Override
    public int hashCode() {
        // see Map.Entry API specification
        return (getKey() == null ? 0 : getKey().hashCode()) ^
                (getValue() == null ? 0 : getValue().hashCode());
    }

  /**
   * 返回Pair的左右元素的字符串比奥斯
   */
    @Override
    public String toString() {
        return "(" + getLeft() + ',' + getRight() + ')';
    }

  /**
   * 格式化字符串
   */
    public String toString(final String format) {
        return String.format(format, getLeft(), getRight());
    }

}
ImmutablePair不可变的左右元素对
package org.apache.commons.lang3.tuple;

public final class ImmutablePair<L, R> extends Pair<L, R> {
    /**
     * 返回不可变的Pair对象,左右元素都为null
     */
    @SuppressWarnings("rawtypes")
    private static final ImmutablePair NULL = ImmutablePair.of(null, null);

    private static final long serialVersionUID = 4954918890077093841L;
    /**
     * 静态方法,返回元素对为null的不可变Pair对象
     */
    @SuppressWarnings("unchecked")
    public static <L, R> ImmutablePair<L, R> nullPair() {
        return NULL;
    }

    /**
     * 返回Pair的左元素
     */
    public final L left;
    /**
     * 返回Pair的右元素
     */
    public final R right;
    /**
     * 返回不可变Pair元素对对象
     */
    public static <L, R> ImmutablePair<L, R> of(final L left, final R right) {
        return new ImmutablePair<>(left, right);
    }

    /**
     * 构造函数,传递左元素、右元素
     */
    public ImmutablePair(final L left, final R right) {
        super();
        this.left = left;
        this.right = right;
    }

    /**
     * 获取左元素
     */
    @Override
    public L getLeft() {
        return left;
    }

    /**
     * 获取右元素
     */
    @Override
    public R getRight() {
        return right;
    }
    /**
     * 设置元素,但是会报异常
     */
    @Override
    public R setValue(final R value) {
        throw new UnsupportedOperationException();
    }

}
MutablePair可以改变值得Pair左右元素对
package org.apache.commons.lang3.tuple;

public class MutablePair<L, R> extends Pair<L, R> {


    private static final long serialVersionUID = 4954918890077093841L;

    /**
     *  左元素对象
     */
    public L left;
    /**
     * 右元素对象
     */
    public R right;

    /**
     * 返回一个可变的Pair左右元素对象
     */
    public static <L, R> MutablePair<L, R> of(final L left, final R right) {
        return new MutablePair<>(left, right);
    }

    /**
     * 
     */
    public MutablePair() {
        super();
    }
    /**
     * 构造函数需要传递一个左元素一个右元素
     */
    public MutablePair(final L left, final R right) {
        super();
        this.left = left;
        this.right = right;
    }
    /**
     * 获取左元素
     */
    @Override
    public L getLeft() {
        return left;
    }

    /**
     * 设置左元素对象
     */
    public void setLeft(final L left) {
        this.left = left;
    }

    /**
     * 获取右元素对象
     */
    @Override
    public R getRight() {
        return right;
    }
    /**
     * 设置右元素对象
     */
    public void setRight(final R right) {
        this.right = right;
    }
    /**
     * 设置右元素对象,并返回未设置之前的右元素值
     */
    @Override
    public R setValue(final R value) {
        final R result = getRight();
        setRight(value);
        return result;
    }

}

静态方法使用示例

MutablePair p = MutablePair.<String, String>of("s", "s");
ImmutablePair p1 = ImmutablePair.<String, String>of("s", "sd");