在實(shí)際編碼中,經(jīng)常會遇到一個(gè)方法需要返回多個(gè)值的情況纽门,你編寫一個(gè)方法薛耻,需要同時(shí)返回某個(gè)操作的結(jié)果和一些相關(guān)的附加信息基跑。使用傳統(tǒng)的方式澜薄,你可能需要?jiǎng)?chuàng)建一個(gè)包含這些信息的自定義類或者使用集合(如 Map
)來存儲這些值。然而,這往往使得代碼變得臃腫吃媒,而且對于調(diào)用方來說,理解和提取這些值可能會顯得有些繁瑣死陆。
這時(shí)使用org.apache.commons.lang3.tuple
下的Pair
或 Triple
及其子類是一種非常便捷的解決方案肴焊。這些類提供了一種清晰、簡單的方式來組織和傳遞多個(gè)相關(guān)聯(lián)的值倒淫,使得代碼更加直觀和易于理解伙菊。
使用 Pair
或 Triple
就能輕松解決這個(gè)問題。你可以在一個(gè)方法中返回一個(gè) Pair
或 Triple
對象敌土,其中包含你想要傳遞的多個(gè)值镜硕。這樣,你可以清晰地表示這些值之間的關(guān)系返干,而且調(diào)用方可以輕松地訪問和使用這些值兴枯,而無需繁瑣的解包過程。
在接下來的部分矩欠,我們將深入研究如何在這類場景中使用 Pair
和 Triple
及其子類财剖,以及它們?nèi)绾魏喕覀冊诰幋a中常遇到的多值返回問題。
引入依賴:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
Pair 類介紹
Pair
類是org.apache.commons.lang3
庫提供的一個(gè)簡單的鍵值對容器癌淮,用于表示兩個(gè)相關(guān)聯(lián)的值躺坟。其主要作用是將兩個(gè)值組織在一起,提供一種便捷的方式進(jìn)行傳遞和處理乳蓄。
首先我們來看一下Pair
的源碼:
/**
* 抽象類咪橙,表示簡單的鍵值對。
* 實(shí)現(xiàn)了 Map.Entry 接口虚倒,支持在各種集合中使用美侦。
* 實(shí)現(xiàn)了 Comparable 接口,用于比較兩個(gè) Pair 對象的大小魂奥。
* 可序列化菠剩,支持對象的序列化和反序列化。
*
* @param <L> 左值的類型
* @param <R> 右值的類型
*/
public abstract class Pair<L, R> implements Map.Entry<L, R>, Comparable<Pair<L, R>>, Serializable {
private static final long serialVersionUID = 4954918890077093841L;
// 空數(shù)組耻煤,用于表示空的 Pair 對象數(shù)組
public static final Pair<?, ?>[] EMPTY_ARRAY = new PairAdapter[0];
/**
* 返回一個(gè)空的 Pair 數(shù)組具壮。
*
* @return 空的 Pair 數(shù)組
*/
public static <L, R> Pair<L, R>[] emptyArray() {
return (Pair[]) EMPTY_ARRAY;
}
/**
* 靜態(tài)工廠方法,創(chuàng)建一個(gè)新的 ImmutablePair 對象哈蝇,表示給定的左右值的鍵值對嘴办。
*
* @param left 左值
* @param right 右值
* @param <L> 左值的類型
* @param <R> 右值的類型
* @return ImmutablePair 對象
*/
public static <L, R> Pair<L, R> of(L left, R right) {
return ImmutablePair.of(left, right);
}
/**
* 靜態(tài)工廠方法,創(chuàng)建一個(gè)新的 ImmutablePair 對象买鸽,表示給定 Map.Entry 對象的鍵值對涧郊。
*
* @param pair Map.Entry 對象
* @param <L> 左值的類型
* @param <R> 右值的類型
* @return ImmutablePair 對象
*/
public static <L, R> Pair<L, R> of(Map.Entry<L, R> pair) {
return ImmutablePair.of(pair);
}
/**
* 返回左值,實(shí)現(xiàn)了 Map.Entry 接口眼五。
*
* @return 左值
*/
public final L getKey() {
return this.getLeft();
}
/**
* 抽象方法妆艘,由子類實(shí)現(xiàn)彤灶,用于獲取左值。
*
* @return 左值
*/
public abstract L getLeft();
/**
* 抽象方法批旺,由子類實(shí)現(xiàn)幌陕,用于獲取右值。
*
* @return 右值
*/
public abstract R getRight();
/**
* 返回右值汽煮,實(shí)現(xiàn)了 Map.Entry 接口搏熄。
*
* @return 右值
*/
public R getValue() {
return this.getRight();
}
}
Pair
類是一個(gè)抽象類,它有兩個(gè)子類ImmutablePair
和MutablePair
暇赤。接下來我們介紹一下這兩個(gè)子類心例,也是我們要使用的兩個(gè)類。
MutablePair
MutablePair
是一個(gè)可變的鞋囊。它允許在創(chuàng)建后動(dòng)態(tài)修改鍵和值止后,提供了更大的靈活性。但是它是線程不安全的溜腐。
我們可以根據(jù)它提供的幾個(gè)靜態(tài)方法或者它的構(gòu)造器去構(gòu)造一個(gè)MutablePair
:
// 靜態(tài)工廠方法译株,返回一個(gè)空的 MutablePair
public static <L, R> MutablePair<L, R>[] emptyArray();
// 靜態(tài)工廠方法,傳入給定的左右值的鍵值對挺益,創(chuàng)建并返回一個(gè)MutablePair 對象
public static <L, R> MutablePair<L, R> of(L left, R right);
// 靜態(tài)工廠方法歉糜,傳入給定 Map.Entry 對象的鍵值對,創(chuàng)建并返回一個(gè)新的MutablePair 對象
public static <L, R> MutablePair<L, R> of(Map.Entry<L, R> pair);
// 無參構(gòu)造器
public MutablePair()
// 指定左右值的鍵值對的構(gòu)造器
public MutablePair(L left, R right)
我們可以根據(jù)它的幾個(gè)方法修改鍵和值:
// 修改左值
public void setLeft(L left);
// 修改右值
public void setRight(R right);
// 修改新的右值望众,并返回之前的右值匪补。
public R setValue(R value);
我們可以根據(jù)它的幾個(gè)方法獲取鍵和值:
// 獲取左值
public L getLeft();
// Pair中的方法 獲取左值
public final L getKey();
// 獲取右值
public R getRight();
// Pair中的方法 獲取右值
public R getValue();
當(dāng)然我們看到它的
left
以及right
都是public
的。所以我們也可以直接取值黍檩,不用使用它的get
方法。
ImmutablePair
ImmutablePair
是Pair
的一個(gè)不可變的子類始锚。它在創(chuàng)建完成之后刽酱,不允許改變鍵和值。它是線程安全的瞧捌。
我們可以看一下它如何進(jìn)行構(gòu)造的:
// 靜態(tài)工廠方法棵里,返回一個(gè)空的 ImmutablePair 數(shù)組。
public static <L, R> ImmutablePair<L, R>[] emptyArray();
// 靜態(tài)工廠方法姐呐,返回一個(gè)包含 null 左值和 null 右值的 ImmutablePair 對象殿怜,表示空值。
public static <L, R> ImmutablePair<L, R> nullPair();
// 靜態(tài)工廠方法曙砂,返回一個(gè)包含指定左值和 null 右值的 ImmutablePair 對象头谜。
public static <L, R> Pair<L, R> left(L left);
// 靜態(tài)工廠方法,返回一個(gè)包含 null 左值和指定右值的 ImmutablePair 對象鸠澈。
public static <L, R> Pair<L, R> right(R right);
// 靜態(tài)工廠方法柱告,創(chuàng)建并返回一個(gè)新的 ImmutablePair 對象截驮,表示給定的左右值的鍵值對。
public static <L, R> ImmutablePair<L, R> of(L left, R right);
// 靜態(tài)工廠方法际度,創(chuàng)建并返回一個(gè)新的 ImmutablePair 對象葵袭,表示給定 Map.Entry 對象的鍵值對。
public static <L, R> ImmutablePair<L, R> of(Map.Entry<L, R> pair);
// 有參構(gòu)造器 傳入給定的左右值的鍵值對乖菱。
public ImmutablePair(L left, R right);
我們可以根據(jù)它的幾個(gè)方法獲取鍵和值:
// 獲取左值
public L getLeft();
// Pair中的方法 獲取左值
public final L getKey();
// 獲取右值
public R getRight();
// Pair中的方法 獲取右值
public R getValue();
當(dāng)然我們看到它的
left
以及right
都是public
的坡锡。所以我們也可以直接取值,不用使用它的get
方法窒所。
那我們再看一下為什么ImmutablePair
是不可變的鹉勒,并且是線程安全的。
首先我們看一下它的左值以及右值都是final
的墩新,不可更改的贸弥。并且調(diào)用它的setValue
會拋出UnsupportedOperationException
。
public final L left;
public final R right;
public R setValue(R value) {
throw new UnsupportedOperationException();
}
類中的 left
和 right
成員變量被聲明為 final
海渊,這意味著它們在對象創(chuàng)建后不能被修改绵疲,確保了線程安全性。ImmutablePair
被設(shè)計(jì)為不可變的鍵值對類臣疑,即一旦創(chuàng)建盔憨,其內(nèi)容不可更改。這確保了在多線程環(huán)境中讯沈,不會有并發(fā)修改的問題郁岩。
使用示例
/**
* 返回MutablePair
* @param userDO
* @return
*/
private static MutablePair<String, Integer> handleUserInfo1(UserDO userDO){
return MutablePair.of(userDO.getUserId(), userDO.getAge());
}
/**
* 返回ImmutablePair
* @param userDO
* @return
*/
private static ImmutablePair<String, Integer> handleUserInfo2(UserDO userDO){
return ImmutablePair.of(userDO.getUserId(), userDO.getAge());
}
public static void main(String[] args) {
UserDO userDO = new UserDO();
userDO.setUserId("coderacademy");
userDO.setAge(35);
MutablePair<String, Integer> mutablePair = handleUserInfo1(userDO);
System.out.println(mutablePair.getLeft()+" MutablePair修改前:"+ mutablePair.right);
mutablePair.setRight(40);
System.out.println(mutablePair.getLeft()+" MutablePair修改后:"+ mutablePair.right);
ImmutablePair<String, Integer> immutablePair = handleUserInfo2(userDO);
System.out.println(mutablePair.getLeft()+" ImmutablePair修改前:"+ mutablePair.right);
immutablePair.setValue(50);
System.out.println(mutablePair.getLeft()+" ImmutablePair修改后:"+ mutablePair.right);
}
執(zhí)行結(jié)果,我們發(fā)現(xiàn)ImmutablePair
在修改value時(shí)報(bào)錯(cuò):
Pair
類及其子類 ImmutablePair
和 MutablePair
是用于表示鍵值對的實(shí)用工具類缺狠。ImmutablePair
是不可變的问慎、線程安全的,適用于安全共享挤茄;MutablePair
允許動(dòng)態(tài)修改值如叼,但不具備線程安全性,適用于單線程環(huán)境穷劈。它們在方法返回多個(gè)值時(shí)提供了簡便的解決方案笼恰,提高了代碼的靈活性。
Triple介紹
Triple
是一個(gè)用于表示三元組的抽象類歇终。三元組是由三個(gè)元素組成的有序集合社证,其中每個(gè)元素都有特定的位置,分別稱為左值(Left)评凝、中間值(Middle)和右值(Right)追葡。Triple
類提供了一種便捷的方式來組織和處理這種具有固定順序的數(shù)據(jù)。可以在不創(chuàng)建專門類的情況下輕松返回三個(gè)值辽俗。通過 Triple
疾渣,開發(fā)者可以更方便地處理包含三個(gè)元素的數(shù)據(jù),減少了創(chuàng)建和維護(hù)多個(gè)變量的復(fù)雜性崖飘,使代碼更加簡潔榴捡。
我們來看一下Triple
的源碼:
/**
* 表示包含三個(gè)元素的三元組的抽象類 Triple。
*
* 該類是一個(gè)抽象實(shí)現(xiàn)朱浴,定義了基本的 API吊圾,將元素分別稱為 'left'、'middle' 和 'right'翰蠢。
*
* 子類的實(shí)現(xiàn)可以是可變的或不可變的项乒。對存儲的對象類型沒有限制。
* Triple 對象的可變性取決于其中存儲的對象是否是可變的梁沧。如果存儲的是可變對象檀何,那么 Triple 本身也就變得可變,因?yàn)榇鎯Φ膶ο鬆顟B(tài)可以被修改廷支。
* 如果存儲的是不可變對象频鉴,那么Triple 對象在創(chuàng)建后就保持不可變。
*
*/
public abstract class Triple<L, M, R> implements Comparable<Triple<L, M, R>>, Serializable {
/**
* 一個(gè)空數(shù)組恋拍。
*/
public static final Triple<?, ?, ?>[] EMPTY_ARRAY = new TripleAdapter[0];
/**
* 返回可分配而無需編譯器警告的空數(shù)組單例垛孔。
*
*/
@SuppressWarnings("unchecked")
public static <L, M, R> Triple<L, M, R>[] emptyArray() {
return (Triple<L, M, R>[]) EMPTY_ARRAY;
}
/**
* 獲取由三個(gè)對象組成的不可變?nèi)M,推斷出泛型類型施敢。
*
* 此工廠方法允許使用推斷類型來創(chuàng)建三元組以獲取泛型類型周荐。
*
* @param left 左元素,可以為 null
* @param middle 中間元素僵娃,可以為 null
* @param right 右元素概作,可以為 null
* @return 由三個(gè)參數(shù)形成的三元組,非 null
*/
public static <L, M, R> Triple<L, M, R> of(final L left, final M middle, final R right) {
return new ImmutableTriple<>(left, middle, right);
}
/**
* 獲取此三元組的左元素默怨。
*
* @return 左元素讯榕,可以為 null
*/
public abstract L getLeft();
/**
* 獲取此三元組的中間元素。
*
* @return 中間元素先壕,可以為 null
*/
public abstract M getMiddle();
/**
* 獲取此三元組的右元素瘩扼。
*
* @return 右元素谆甜,可以為 null
*/
public abstract R getRight();
}
Triple
是一個(gè)抽象類垃僚,它有兩個(gè)子類:可變MutableTriple
以及不可變 ImmutableTriple
。
MutableTriple
MutableTriple
是可變的规辱,原因在于它提供了公共的設(shè)置(set)方法谆棺,允許在創(chuàng)建后修改其內(nèi)部值。具體來說,MutableTriple
提供了 setLeft
改淑、setMiddle
和 setRight
方法碍岔,使得在對象創(chuàng)建后可以修改左、中朵夏、右元素的值蔼啦。
/**
* 表示由三個(gè) {@code Object} 元素組成的可變?nèi)M。
*
* 非線程安全
*
*/
public class MutableTriple<L, M, R> extends Triple<L, M, R> {
/**
* 通過推斷泛型類型獲取三個(gè)對象的可變?nèi)M仰猖。
*
* 該工廠允許通過推斷泛型類型創(chuàng)建三元組捏肢。
*
*/
public static <L, M, R> MutableTriple<L, M, R> of(final L left, final M middle, final R right) {
return new MutableTriple<>(left, middle, right);
}
/** 左對象 */
public L left;
/** 中間對象 */
public M middle;
/** 右對象 */
public R right;
/**
* 創(chuàng)建一個(gè)新的三元組實(shí)例,包含三個(gè) null 值饥侵。
*/
public MutableTriple() {
}
/**
* 創(chuàng)建一個(gè)新的三元組實(shí)例鸵赫。
*
* @param left 左值,可以為 null
* @param middle 中間值躏升,可以為 null
* @param right 右值辩棒,可以為 null
*/
public MutableTriple(final L left, final M middle, final R right) {
this.left = left;
this.middle = middle;
this.right = right;
}
/**
* 設(shè)置三元組的左元素。
*/
public void setLeft(final L left) {
this.left = left;
}
/**
* 設(shè)置三元組的中間元素膨疏。
*/
public void setMiddle(final M middle) {
this.middle = middle;
}
/**
* 設(shè)置三元組的右元素一睁。
*/
public void setRight(final R right) {
this.right = right;
}
}
MutableTriple
被明確標(biāo)記為非線程安全。
ImmutableTriple
ImmutableTriple
是一個(gè)不可變的三元組類成肘,由三個(gè)泛型元素(left卖局、middle、right)組成双霍。不可變意味著一旦創(chuàng)建砚偶,其狀態(tài)無法修改。該類被設(shè)計(jì)為線程安全的洒闸,但需要注意染坯,如果存儲在三元組中的對象是可變的,那么三元組本身實(shí)際上就不再是不可變的丘逸。
/**
* 一個(gè)由三個(gè)元素組成的不可變?nèi)M单鹿。
*
* ImmutableTriple 是一個(gè)最終類,被設(shè)計(jì)成不可變的深纲,即在實(shí)例化后其狀態(tài)不可更改仲锄。
* 如果存儲在三元組中的三個(gè)對象都是線程安全的,則該類是線程安全的湃鹊。類的最終性防止了子類化儒喊,確保不會添加不希望的行為。
*
* 線程安全的 如果三個(gè)對象都是線程安全的
*
*/
public final class ImmutableTriple<L, M, R> extends Triple<L, M, R> {
/**
* 返回可以在不觸發(fā)編譯器警告的情況下分配的空數(shù)組單例币呵。
* @return 可以在不觸發(fā)編譯器警告的情況下分配的空數(shù)組單例怀愧。
*/
@SuppressWarnings("unchecked")
public static <L, M, R> ImmutableTriple<L, M, R>[] emptyArray() {
return (ImmutableTriple<L, M, R>[]) EMPTY_ARRAY;
}
/**
* 返回一個(gè)由 null 組成的不可變?nèi)M。
*
* @return 一個(gè)由 null 組成的不可變?nèi)M。
*/
public static <L, M, R> ImmutableTriple<L, M, R> nullTriple() {
return NULL;
}
/**
* 通過推斷泛型類型獲得由三個(gè)對象組成的不可變?nèi)M芯义。
*
* 此工廠允許使用推斷創(chuàng)建三元組以獲得泛型類型哈垢。
*
* @return 由三個(gè)參數(shù)形成的不可變?nèi)M,不為 null
*/
public static <L, M, R> ImmutableTriple<L, M, R> of(final L left, final M middle, final R right) {
return new ImmutableTriple<>(left, middle, right);
}
/** 左對象 */
public final L left;
/** 中間對象 */
public final M middle;
/** 右對象 */
public final R right;
/**
* 構(gòu)造方法 創(chuàng)建一個(gè)新的三元組實(shí)例扛拨。
*
*/
public ImmutableTriple(final L left, final M middle, final R right) {
this.left = left;
this.middle = middle;
this.right = right;
}
}
ImmutableTriple
被聲明為 final
耘分,表示不可繼承,確保不可變性绑警。確保不會有子類添加或修改行為陶贼。然后類中的屬性 left
、middle
待秃、right
被聲明為 final
拜秧,表示它們在實(shí)例化后無法被修改。類中沒有提供修改元素的公共方法章郁。ImmutableTriple
主張不可變性枉氮,不提供修改實(shí)例狀態(tài)的方法。當(dāng)然如果存儲在三元組中的對象是可變的暖庄,則整個(gè)三元組就變得可變聊替。這是因?yàn)殡m然 ImmutableTriple
本身是不可變的,但如果存儲的對象是可變的培廓,它們的狀態(tài)可能會發(fā)生變化惹悄。
類聲明中使用 #ThreadSafe#
標(biāo)記,表示在存儲的三個(gè)對象都是線程安全的情況下肩钠,ImmutableTriple
是線程安全的泣港。
示例
/**
* 返回可變Truple
* @param userDO
* @return
*/
private static MutableTriple<String, Integer, UserDO> handleUserInfo1(UserDO userDO){
return MutableTriple.of(userDO.getUserId(), userDO.getSex(), userDO);
}
/**
* 返回不可變Triple
* @param userDO
* @return
*/
private static ImmutableTriple<String, Integer, UserDO> handleUserInfo2(UserDO userDO){
return ImmutableTriple.of(userDO.getUserId(), userDO.getSex(), userDO);
}
public static void main(String[] args) {
UserDO userDO = new UserDO();
userDO.setUserId("coderacademy");
userDO.setUserName("碼農(nóng)Academy");
userDO.setSex(1);
MutableTriple<String, Integer, UserDO> mutableTriple = handleUserInfo1(userDO);
System.out.println("mutableTriple改變前的值:" + mutableTriple);
mutableTriple.setMiddle(2);
System.out.println("mutableTriple改變后的值:" + mutableTriple);
ImmutableTriple<String, Integer, UserDO> immutableTriple = handleUserInfo2(userDO);
System.out.println("ImmutableTriple改變前的值:" + immutableTriple);
UserDO userFromTriple = immutableTriple.right;
userFromTriple.setSex(2);
System.out.println("ImmutableTriple改Right鍵值對象的值:" + immutableTriple);
// 因ImmutableTriple 不可變,無法通過set方法修改鍵值价匠。
}
總結(jié)
使用 Pair 和 Triple 類可以簡化代碼当纱、提高可讀性,使關(guān)聯(lián)數(shù)據(jù)更清晰踩窖,保持類型安全坡氯,增強(qiáng)代碼清晰度,提高擴(kuò)展性洋腮,并提供豐富的功能箫柳,從而使開發(fā)人員更高效地處理相關(guān)數(shù)據(jù),編寫更簡潔可讀的代碼啥供,提升代碼質(zhì)量和開發(fā)效率悯恍。
本文已收錄于我的個(gè)人博客:[碼農(nóng)Academy的博客,專注分享Java技術(shù)干貨滤灯,包括Java基礎(chǔ)坪稽、SpringBoot、SpringCloud鳞骤、Mysql窒百、Redis、Elasticsearch豫尽、中間件篙梢、架構(gòu)設(shè)計(jì)、面試題美旧、程序員攻略等渤滞。] (https://www.coderacademy.online/)