Java编程的利器:Pair和Triple无缝办理多值返回问题,助力编写高效代码 ...

一给  金牌会员 | 2024-5-12 21:36:56 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 866|帖子 866|积分 2598

在实际编码中,常常会遇到一个方法需要返回多个值的情况,你编写一个方法,需要同时返回某个操作的结果和一些相关的附加信息。利用传统的方式,你大概需要创建一个包含这些信息的自定义类或者利用集合(如 Map)来存储这些值。然而,这往往使得代码变得臃肿,而且对于调用方来说,明白和提取这些值大概会显得有些繁琐。
这时利用org.apache.commons.lang3.tuple下的Pair 或 Triple 及其子类是一种非常便捷的办理方案。这些类提供了一种清楚、简朴的方式来组织和传递多个相关联的值,使得代码更加直观和易于明白。
利用 Pair 或 Triple 就能轻松办理这个问题。你可以在一个方法中返回一个 Pair 或 Triple 对象,其中包含你想要传递的多个值。如许,你可以清楚地表示这些值之间的关系,而且调用方可以轻松地访问和利用这些值,而无需繁琐的解包过程。
在接下来的部分,我们将深入研究怎样在这类场景中利用 Pair 和 Triple 及其子类,以及它们怎样简化我们在编码中常遇到的多值返回问题。
引入依靠:
  1. <dependency>
  2.     <groupId>org.apache.commons</groupId>
  3.     <artifactId>commons-lang3</artifactId>
  4.     <version>3.12.0</version>
  5. </dependency>
复制代码
Pair 类介绍

Pair 类是org.apache.commons.lang3库提供的一个简朴的键值对容器,用于表示两个相关联的值。其主要作用是将两个值组织在一起,提供一种便捷的方式进行传递和处理。
首先我们来看一下Pair的源码:
  1. /**
  2. * 抽象类,表示简单的键值对。
  3. * 实现了 Map.Entry 接口,支持在各种集合中使用。
  4. * 实现了 Comparable 接口,用于比较两个 Pair 对象的大小。
  5. * 可序列化,支持对象的序列化和反序列化。
  6. *
  7. * @param <L> 左值的类型
  8. * @param <R> 右值的类型
  9. */
  10. public abstract class Pair<L, R> implements Map.Entry<L, R>, Comparable<Pair<L, R>>, Serializable {
  11.     private static final long serialVersionUID = 4954918890077093841L;
  12.     // 空数组,用于表示空的 Pair 对象数组
  13.     public static final Pair<?, ?>[] EMPTY_ARRAY = new PairAdapter[0];
  14.     /**
  15.      * 返回一个空的 Pair 数组。
  16.      *
  17.      * @return 空的 Pair 数组
  18.      */
  19.     public static <L, R> Pair<L, R>[] emptyArray() {
  20.         return (Pair[]) EMPTY_ARRAY;
  21.     }
  22.     /**
  23.      * 静态工厂方法,创建一个新的 ImmutablePair 对象,表示给定的左右值的键值对。
  24.      *
  25.      * @param left  左值
  26.      * @param right 右值
  27.      * @param <L>   左值的类型
  28.      * @param <R>   右值的类型
  29.      * @return ImmutablePair 对象
  30.      */
  31.     public static <L, R> Pair<L, R> of(L left, R right) {
  32.         return ImmutablePair.of(left, right);
  33.     }
  34.     /**
  35.      * 静态工厂方法,创建一个新的 ImmutablePair 对象,表示给定 Map.Entry 对象的键值对。
  36.      *
  37.      * @param pair Map.Entry 对象
  38.      * @param <L>  左值的类型
  39.      * @param <R>  右值的类型
  40.      * @return ImmutablePair 对象
  41.      */
  42.     public static <L, R> Pair<L, R> of(Map.Entry<L, R> pair) {
  43.         return ImmutablePair.of(pair);
  44.     }
  45.     /**
  46.      * 返回左值,实现了 Map.Entry 接口。
  47.      *
  48.      * @return 左值
  49.      */
  50.     public final L getKey() {
  51.         return this.getLeft();
  52.     }
  53.     /**
  54.      * 抽象方法,由子类实现,用于获取左值。
  55.      *
  56.      * @return 左值
  57.      */
  58.     public abstract L getLeft();
  59.     /**
  60.      * 抽象方法,由子类实现,用于获取右值。
  61.      *
  62.      * @return 右值
  63.      */
  64.     public abstract R getRight();
  65.     /**
  66.      * 返回右值,实现了 Map.Entry 接口。
  67.      *
  68.      * @return 右值
  69.      */
  70.     public R getValue() {
  71.         return this.getRight();
  72.     }
  73. }
复制代码
Pair类是一个抽象类,它有两个子类ImmutablePair和MutablePair。接下来我们介绍一下这两个子类,也是我们要利用的两个类。
MutablePair

MutablePair是一个可变的。它允许在创建后动态修改键和值,提供了更大的机动性。但是它是线程不安全的。
我们可以根据它提供的几个静态方法或者它的构造器去构造一个MutablePair:
  1. // 静态工厂方法,返回一个空的 MutablePair
  2. public static <L, R> MutablePair<L, R>[] emptyArray();
  3. // 静态工厂方法,传入给定的左右值的键值对,创建并返回一个MutablePair 对象
  4. public static <L, R> MutablePair<L, R> of(L left, R right);
  5. // 静态工厂方法,传入给定 Map.Entry 对象的键值对,创建并返回一个新的MutablePair 对象
  6. public static <L, R> MutablePair<L, R> of(Map.Entry<L, R> pair);
  7. // 无参构造器
  8. public MutablePair()
  9. // 指定左右值的键值对的构造器
  10. public MutablePair(L left, R right)
复制代码
我们可以根据它的几个方法修改键和值:
  1. // 修改左值
  2. public void setLeft(L left);
  3. // 修改右值
  4. public void setRight(R right);
  5. // 修改新的右值,并返回之前的右值。
  6. public R setValue(R value);
复制代码
我们可以根据它的几个方法获取键和值:
  1. // 获取左值
  2. public L getLeft();
  3. // Pair中的方法  获取左值
  4. public final L getKey();
  5. // 获取右值
  6. public R getRight();
  7. // Pair中的方法  获取右值
  8. public R getValue();
复制代码
固然我们看到它的left以及right都是public的。所以我们也可以直接取值,不消利用它的get方法。
ImmutablePair

ImmutablePair是Pair的一个不可变的子类。它在创建完成之后,不允许改变键和值。它是线程安全的。
我们可以看一下它怎样进行构造的:
  1. // 静态工厂方法,返回一个空的 ImmutablePair 数组。
  2. public static <L, R> ImmutablePair<L, R>[] emptyArray();
  3. // 静态工厂方法,返回一个包含 null 左值和 null 右值的 ImmutablePair 对象,表示空值。
  4. public static <L, R> ImmutablePair<L, R> nullPair();
  5. // 静态工厂方法,返回一个包含指定左值和 null 右值的 ImmutablePair 对象。
  6. public static <L, R> Pair<L, R> left(L left);
  7. // 静态工厂方法,返回一个包含 null 左值和指定右值的 ImmutablePair 对象。
  8. public static <L, R> Pair<L, R> right(R right);
  9. // 静态工厂方法,创建并返回一个新的 ImmutablePair 对象,表示给定的左右值的键值对。
  10. public static <L, R> ImmutablePair<L, R> of(L left, R right);
  11. // 静态工厂方法,创建并返回一个新的 ImmutablePair 对象,表示给定 Map.Entry 对象的键值对。
  12. public static <L, R> ImmutablePair<L, R> of(Map.Entry<L, R> pair);
  13. // 有参构造器 传入给定的左右值的键值对。
  14. public ImmutablePair(L left, R right);
复制代码
我们可以根据它的几个方法获取键和值:
  1. // 获取左值
  2. public L getLeft();
  3. // Pair中的方法  获取左值
  4. public final L getKey();
  5. // 获取右值
  6. public R getRight();
  7. // Pair中的方法  获取右值
  8. public R getValue();
复制代码
固然我们看到它的left以及right都是public的。所以我们也可以直接取值,不消利用它的get方法。
那我们再看一下为什么ImmutablePair是不可变的,并且是线程安全的。
首先我们看一下它的左值以及右值都是final的,不可更改的。并且调用它的setValue会抛出UnsupportedOperationException。
  1. public final L left;  
  2. public final R right;
  3. public R setValue(R value) {  
  4.     throw new UnsupportedOperationException();  
  5. }
复制代码
类中的 left 和 right 成员变量被声明为 final,这意味着它们在对象创建后不能被修改,确保了线程安全性。ImmutablePair 被设计为不可变的键值对类,即一旦创建,其内容不可更改。这确保了在多线程环境中,不会有并发修改的问题。
利用示例

  1. /**  
  2. * 返回MutablePair  
  3. * @param userDO  
  4. * @return  
  5. */  
  6. private static MutablePair<String, Integer> handleUserInfo1(UserDO userDO){  
  7.     return MutablePair.of(userDO.getUserId(), userDO.getAge());  
  8. }  
  9.   
  10. /**  
  11. * 返回ImmutablePair  
  12. * @param userDO  
  13. * @return  
  14. */  
  15. private static ImmutablePair<String, Integer> handleUserInfo2(UserDO userDO){  
  16.     return ImmutablePair.of(userDO.getUserId(), userDO.getAge());  
  17. }  
  18.   
  19. public static void main(String[] args) {  
  20.     UserDO userDO = new UserDO();  
  21.     userDO.setUserId("coderacademy");  
  22.     userDO.setAge(35);  
  23.   
  24.     MutablePair<String, Integer> mutablePair = handleUserInfo1(userDO);  
  25.     System.out.println(mutablePair.getLeft()+" MutablePair修改前:"+ mutablePair.right);  
  26.     mutablePair.setRight(40);  
  27.     System.out.println(mutablePair.getLeft()+" MutablePair修改后:"+ mutablePair.right);  
  28.   
  29.     ImmutablePair<String, Integer> immutablePair = handleUserInfo2(userDO);  
  30.     System.out.println(mutablePair.getLeft()+" ImmutablePair修改前:"+ mutablePair.right);  
  31.     immutablePair.setValue(50);  
  32.     System.out.println(mutablePair.getLeft()+" ImmutablePair修改后:"+ mutablePair.right);  
  33. }
复制代码
实行结果,我们发现ImmutablePair在修改value时报错:

Pair 类及其子类 ImmutablePair 和 MutablePair 是用于表示键值对的实用工具类。ImmutablePair 是不可变的、线程安全的,适用于安全共享;MutablePair 允许动态修改值,但不具备线程安全性,适用于单线程环境。它们在方法返回多个值时提供了简便的办理方案,提高了代码的机动性。
Triple介绍

Triple 是一个用于表示三元组的抽象类。三元组是由三个元素组成的有序集合,其中每个元素都有特定的位置,分别称为左值(Left)、中间值(Middle)和右值(Right)。Triple 类提供了一种便捷的方式来组织和处理这种具有固定顺序的数据。可以在不创建专门类的情况下轻松返回三个值。通过 Triple,开发者可以更方便地处理包含三个元素的数据,减少了创建和维护多个变量的复杂性,使代码更加简洁。
我们来看一下Triple的源码:
  1. /**
  2. * 表示包含三个元素的三元组的抽象类 Triple。
  3. *
  4. * 该类是一个抽象实现,定义了基本的 API,将元素分别称为 'left'、'middle' 和 'right'。
  5. *
  6. * 子类的实现可以是可变的或不可变的。对存储的对象类型没有限制。
  7. * Triple 对象的可变性取决于其中存储的对象是否是可变的。如果存储的是可变对象,那么 Triple 本身也就变得可变,因为存储的对象状态可以被修改。
  8. * 如果存储的是不可变对象,那么Triple 对象在创建后就保持不可变。
  9. *
  10. */
  11. public abstract class Triple<L, M, R> implements Comparable<Triple<L, M, R>>, Serializable {
  12.     /**
  13.      * 一个空数组。
  14.      */
  15.     public static final Triple<?, ?, ?>[] EMPTY_ARRAY = new TripleAdapter[0];
  16.     /**
  17.      * 返回可分配而无需编译器警告的空数组单例。
  18.      *
  19.      */
  20.     @SuppressWarnings("unchecked")
  21.     public static <L, M, R> Triple<L, M, R>[] emptyArray() {
  22.         return (Triple<L, M, R>[]) EMPTY_ARRAY;
  23.     }
  24.     /**
  25.      * 获取由三个对象组成的不可变三元组,推断出泛型类型。
  26.      *
  27.      * 此工厂方法允许使用推断类型来创建三元组以获取泛型类型。
  28.      *
  29.      * @param left   左元素,可以为 null
  30.      * @param middle 中间元素,可以为 null
  31.      * @param right  右元素,可以为 null
  32.      * @return 由三个参数形成的三元组,非 null
  33.      */
  34.     public static <L, M, R> Triple<L, M, R> of(final L left, final M middle, final R right) {
  35.         return new ImmutableTriple<>(left, middle, right);
  36.     }
  37.     /**
  38.      * 获取此三元组的左元素。
  39.      *
  40.      * @return 左元素,可以为 null
  41.      */
  42.     public abstract L getLeft();
  43.     /**
  44.      * 获取此三元组的中间元素。
  45.      *
  46.      * @return 中间元素,可以为 null
  47.      */
  48.     public abstract M getMiddle();
  49.     /**
  50.      * 获取此三元组的右元素。
  51.      *
  52.      * @return 右元素,可以为 null
  53.      */
  54.     public abstract R getRight();
  55. }
复制代码
Triple是一个抽象类,它有两个子类:可变MutableTriple 以及不可变 ImmutableTriple。
MutableTriple

MutableTriple 是可变的,原因在于它提供了公共的设置(set)方法,允许在创建后修改其内部值。具体来说,MutableTriple 提供了 setLeft、setMiddle 和 setRight 方法,使得在对象创建后可以修改左、中、右元素的值。
  1. /**
  2. * 表示由三个 {@code Object} 元素组成的可变三元组。
  3. *
  4. * 非线程安全
  5. *
  6. */
  7. public class MutableTriple<L, M, R> extends Triple<L, M, R> {
  8.     /**
  9.      * 通过推断泛型类型获取三个对象的可变三元组。
  10.      *
  11.      * 该工厂允许通过推断泛型类型创建三元组。
  12.      *
  13.      */
  14.     public static <L, M, R> MutableTriple<L, M, R> of(final L left, final M middle, final R right) {
  15.         return new MutableTriple<>(left, middle, right);
  16.     }
  17.     /** 左对象 */
  18.     public L left;
  19.     /** 中间对象 */
  20.     public M middle;
  21.     /** 右对象 */
  22.     public R right;
  23.     /**
  24.      * 创建一个新的三元组实例,包含三个 null 值。
  25.      */
  26.     public MutableTriple() {
  27.     }
  28.     /**
  29.      * 创建一个新的三元组实例。
  30.      *
  31.      * @param left   左值,可以为 null
  32.      * @param middle 中间值,可以为 null
  33.      * @param right  右值,可以为 null
  34.      */
  35.     public MutableTriple(final L left, final M middle, final R right) {
  36.         this.left = left;
  37.         this.middle = middle;
  38.         this.right = right;
  39.     }
  40.     /**
  41.      * 设置三元组的左元素。
  42.      */
  43.     public void setLeft(final L left) {
  44.         this.left = left;
  45.     }
  46.     /**
  47.      * 设置三元组的中间元素。
  48.      */
  49.     public void setMiddle(final M middle) {
  50.         this.middle = middle;
  51.     }
  52.     /**
  53.      * 设置三元组的右元素。
  54.      */
  55.     public void setRight(final R right) {
  56.         this.right = right;
  57.     }
  58. }
复制代码
MutableTriple 被明白标志为非线程安全。
ImmutableTriple

ImmutableTriple 是一个不可变的三元组类,由三个泛型元素(left、middle、right)组成。不可变意味着一旦创建,其状态无法修改。该类被设计为线程安全的,但需要注意,如果存储在三元组中的对象是可变的,那么三元组本身实际上就不再是不可变的。
  1. /**
  2. * 一个由三个元素组成的不可变三元组。
  3. *
  4. * ImmutableTriple 是一个最终类,被设计成不可变的,即在实例化后其状态不可更改。
  5. * 如果存储在三元组中的三个对象都是线程安全的,则该类是线程安全的。类的最终性防止了子类化,确保不会添加不希望的行为。
  6. *
  7. * 线程安全的 如果三个对象都是线程安全的
  8. *
  9. */
  10. public final class ImmutableTriple<L, M, R> extends Triple<L, M, R> {
  11.     /**
  12.      * 返回可以在不触发编译器警告的情况下分配的空数组单例。
  13.      * @return 可以在不触发编译器警告的情况下分配的空数组单例。
  14.      */
  15.     @SuppressWarnings("unchecked")
  16.     public static <L, M, R> ImmutableTriple<L, M, R>[] emptyArray() {
  17.         return (ImmutableTriple<L, M, R>[]) EMPTY_ARRAY;
  18.     }
  19.     /**
  20.      * 返回一个由 null 组成的不可变三元组。
  21.      *
  22.      * @return 一个由 null 组成的不可变三元组。
  23.      */
  24.     public static <L, M, R> ImmutableTriple<L, M, R> nullTriple() {
  25.         return NULL;
  26.     }
  27.     /**
  28.      * 通过推断泛型类型获得由三个对象组成的不可变三元组。
  29.      *
  30.      * 此工厂允许使用推断创建三元组以获得泛型类型。
  31.      *
  32.      * @return 由三个参数形成的不可变三元组,不为 null
  33.      */
  34.     public static <L, M, R> ImmutableTriple<L, M, R> of(final L left, final M middle, final R right) {
  35.         return new ImmutableTriple<>(left, middle, right);
  36.     }
  37.     /** 左对象 */
  38.     public final L left;
  39.     /** 中间对象 */
  40.     public final M middle;
  41.     /** 右对象 */
  42.     public final R right;
  43.     /**
  44.      * 构造方法 创建一个新的三元组实例。
  45.      *
  46.      */
  47.     public ImmutableTriple(final L left, final M middle, final R right) {
  48.         this.left = left;
  49.         this.middle = middle;
  50.         this.right = right;
  51.     }
  52. }
复制代码
ImmutableTriple 被声明为 final,表示不可继承,确保不可变性。确保不会有子类添加或修改行为。然后类中的属性 left、middle、right 被声明为 final,表示它们在实例化后无法被修改。类中没有提供修改元素的公共方法。ImmutableTriple 主张不可变性,不提供修改实例状态的方法。固然如果存储在三元组中的对象是可变的,则整个三元组就变得可变。这是因为固然 ImmutableTriple 本身是不可变的,但如果存储的对象是可变的,它们的状态大概会发生变化。
类声明中利用 #ThreadSafe# 标志,表示在存储的三个对象都是线程安全的情况下,ImmutableTriple 是线程安全的。
示例

  1. /**  
  2. * 返回可变Truple  
  3. * @param userDO  
  4. * @return  
  5. */  
  6. private static MutableTriple<String, Integer, UserDO> handleUserInfo1(UserDO userDO){  
  7.         return MutableTriple.of(userDO.getUserId(), userDO.getSex(), userDO);  
  8. }  
  9.   
  10. /**  
  11. * 返回不可变Triple  
  12. * @param userDO  
  13. * @return  
  14. */  
  15. private static ImmutableTriple<String, Integer, UserDO> handleUserInfo2(UserDO userDO){  
  16.         return ImmutableTriple.of(userDO.getUserId(), userDO.getSex(), userDO);  
  17. }  
  18.   
  19.   
  20. public static void main(String[] args) {  
  21.         UserDO userDO = new UserDO();  
  22.         userDO.setUserId("coderacademy");  
  23.         userDO.setUserName("码农Academy");  
  24.         userDO.setSex(1);  
  25.           
  26.         MutableTriple<String, Integer, UserDO> mutableTriple = handleUserInfo1(userDO);  
  27.         System.out.println("mutableTriple改变前的值:" + mutableTriple);  
  28.         mutableTriple.setMiddle(2);  
  29.         System.out.println("mutableTriple改变后的值:" + mutableTriple);  
  30.           
  31.         ImmutableTriple<String, Integer, UserDO> immutableTriple = handleUserInfo2(userDO);  
  32.         System.out.println("ImmutableTriple改变前的值:" + immutableTriple);  
  33.         UserDO userFromTriple = immutableTriple.right;  
  34.         userFromTriple.setSex(2);  
  35.         System.out.println("ImmutableTriple改Right键值对象的值:" + immutableTriple);  
  36.         // 因ImmutableTriple 不可变,无法通过set方法修改键值。
  37. }
复制代码
总结

利用 Pair 和 Triple 类可以简化代码、提高可读性,使关联数据更清楚,保持范例安全,增强代码清楚度,提高扩展性,并提供丰富的功能,从而使开发人员更高效地处理相关数据,编写更简洁可读的代码,提升代码质量和开发服从。
本文已收录于我的个人博客:码农Academy的博客,专注分享Java技能干货,包括Java基础、Spring Boot、Spring Cloud、Mysql、Redis、Elasticsearch、中间件、架构设计、面试题、程序员攻略等

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!  更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

一给

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表