元組在計算機領域有着特殊的意義,這個名字聽起來似乎有些陌生, 平時在寫代碼也基本沒什么應用場景, 然而, 出人意料的是, 元組跟程序設計密切相關, 可能有的同學不知道, 關系數據庫中的「紀錄」的另一個學術性的名稱就是「元組」, 一條記錄就是一個元組, 一個表就是一個關系, 紀錄組成表, 元組生成關系, 這就是關系數據庫的核心理念。
元組是關系數據庫不可脫離的部份, 但是在程序設計中, 元組並不顯得那么不可或缺。 有一些編程語言本身就自帶元組的語法, 比如說python、F#、haskell、scala等,另一些更為流行的編程語言卻不帶元組語法, 如Java、JavaScript、c++、c#等。
元組並不像數組、對象那樣是不可缺少的編程元素,但是, 使用它卻能對編寫代碼帶來很多的便利,尤其是當一個函數需要返回多個值的情況下。對於這種情況, 普遍的做法是定義一個對象,把函數需要返回的值作為對象的屬性設置,然后把函數的返回值類型設為這個對象的類型, 函數直接返回這個對象就相當於返回多個值了。或者可以讓這個函數返回一個map數據結構,具體的數據存在這個map里面。 然而, 這兩種做法各有缺陷, 第一種方法雖然可靠, 然而代碼會顯的異常臃腫。需求本身很簡單, 只要讓函數返回多個值 , 然而用這種方法卻需要事先定義好一個類型, 然后再實例化,再設置實例屬性, 最后返回, 這樣做的編碼效率也未免太低了些。 第二種方法雖然快捷,卻不夠安全, 在函數的內部或許知道map里存儲着什么樣的值, 然而在函數外部, 卻只知道這個函數的返回值是一個map,至於map里面存有哪些值,是什么類型都是一無所知的, 在多人開發的項目中這種弊端尤其明顯 ,可悲的是這種做法在一些動態類型的語言中是首選的方案,這也是動態類型語言被吐槽安全性、可讀性差的根本原因之一。 因此, 解決這類問題最好的方案就是使用元組。
在語法本身支持元組的語言中, 元組是用括號表示的,如(int,bool,string)就是一個三元組類型, 它的值可以是(1,true,"abc")。 需要注意是的每一個元組類型都是唯一的, (int,bool),(bool,int),(string,double)雖然都是二元組, 然而它們卻是不同的元組, 假如把這里的某一個元組作為函數的返回值, 在可讀性和安全性方面雖然不如前面講的第一種使用自定義類型的方案, 然而卻比第二種使用map的方案要好的多, 至少使用元組能知道函數會返回幾個值, 這些值又分別是什么類型, 而且它還有第二種使用map的方案編碼簡單快捷的優勢。
另人遺憾的是, 像java、c++、c#之類行業內主流的編程語言都不內置元組這一項特性,要使用元組必須自行實現,所幸現在這些編程語言都支持泛型, 實現非內置元組也變的異常簡單, 但是畢竟是非語言內置的語法元素,使用起來肯定不如原生元組來的便捷。
下面介紹一個第三方的Java元組庫類庫,名稱叫做Javatuples,有自己的官方主頁, github star數也有幾百,在Java元組庫領域差不多起着壟斷的地位了。
Javatuples定義的元組最大長度為10, 其實我覺得10元組的元素數量已經是太多了, 基本上沒有什么可讀性可言了。 元組類的定義如下
Unit<A> (1 element) Pair<A,B> (2 elements) Triplet<A,B,C> (3 elements) Quartet<A,B,C,D> (4 elements) Quintet<A,B,C,D,E> (5 elements) Sextet<A,B,C,D,E,F> (6 elements) Septet<A,B,C,D,E,F,G> (7 elements) Octet<A,B,C,D,E,F,G,H> (8 elements) Ennead<A,B,C,D,E,F,G,H,I> (9 elements) Decade<A,B,C,D,E,F,G,H,I,J> (10 elements)
這些原型類都是泛型類, 所以尖括號中的字母可以使用任意類型來代替。 下面是一個三元組的代碼示例
舉一反三,其余的元組類型也是同樣的使用方式。
因為Java的元組並非語言自身支持, 因此代碼看起來Java味十足, 顯得不那么優雅。然而, 寫代碼時除了書寫便捷能提高效率以外,更要注重的是代碼本身所表達的意途, 在這里就是利用元組本身的含意(作用)來對代碼進行增強, 至於是用什么方式書寫其實只是次要的。
最后, 要注意的是泛型尖括號里面的類型不能是Java的基本類型, 如果要使用基本類型那也必須是經過裝箱的基礎類型,如int形必須轉成Integer,bool型對應Boolean類型。