我工作的代码库有一个名为Pair的对象,其中A和B是Pair中第一个和第二个值的类型。我发现这个对象是冒犯性的,因为它被用来代替具有明确命名成员的对象。所以我发现了这个:
List<Pair<Integer, Integer>> productIds = blah();
// snip many lines and method calls
void doSomething(Pair<Integer, Integer> id) {
Integer productId = id.first();
Integer quantity = id.second();
}
而不是
class ProductsOrdered {
int productId;
int quantityOrdered;
// accessor methods, etc
}
List<ProductsOrderded> productsOrdered = blah();
代码库中对的许多其他用途同样难闻。
我用谷歌搜索了元组,它们似乎经常被误解或以可疑的方式使用。对他们的使用是否存在令人信服的论据?我可以理解不想创建庞大的类层次结构,但是如果没有使用元组,那么类层次结构会爆炸吗?
答案 0 :(得分:36)
首先,一个元组是快速而简单的:每次你想把两件事放在一起时,不是写一个类,而是有一个模板可以帮你完成。
其次,它们是通用的。例如,在C ++中,std :: map使用std :: pair of key和value。因此可以使用ANY对,而不必为两种类型的每个排列使用访问器方法创建某种包装类。
最后,它们对于返回多个值非常有用。实际上没有理由专门为函数的多个返回值创建一个类,如果它们不相关,则不应将它们视为一个对象。
公平地说,你粘贴的代码很难使用。
答案 1 :(得分:16)
在Python中一直使用元组,它们被集成到语言中并且非常有用(它们允许为初学者提供多个返回值)。
有时,你真的只需要配对并创造一个真实,诚实的上帝,上课是矫枉过正的。另一方面,当你真的应该使用一个类时使用元组就像反向一样。
答案 2 :(得分:10)
代码示例有几个不同的气味:
框架中已有一个元组; KeyValuePair
结构。 Dictionary
类使用它来存储对,但您可以在任何适合的位置使用它。 (并不是说它适合这种情况......)
如果你有一个对列表,那么使用KeyValuePair
结构比使用相同目的的类更好,因为它会减少内存分配。
具有属性的类清楚地显示了值的含义,而Pair<int,int>
类没有告诉您有关值的含义(只是它们可能以某种方式相关)。为了使代码合理地自我解释,你必须给列表一个非常具有说服力的名称,如productIdAndQuantityPairs
...
答案 3 :(得分:8)
对于它的价值,OP中的代码是一团糟,不是因为它使用元组,而是因为元组中的值太弱了。比较以下内容:
List<Pair<Integer, Integer>> products_weak = blah1();
List<Pair<Product, Integer>> products_strong = blah2();
我生气太多,如果我的开发团队进行了绕过标识,而不是围绕类实例,因为一个整数能代表什么。
话虽如此,当您正确使用它们时,元组非常有用:
然而,C#中的元组使我的眼睛流水。许多语言,如OCaml,Python,Haskell,F#等,都有一个特殊的,简洁的语法来定义元组。例如,在F#中,Map module定义了一个构造函数,如下所示:
val of_list : ('key * 'a) list -> Map<'key,'a>
我可以使用以下方法创建地图实例:
(* val values : (int * string) list *)
let values =
[1, "US";
2, "Canada";
3, "UK";
4, "Australia";
5, "Slovenia"]
(* val dict : Map<int, string> *)
let dict = Map.of_list values
C#中的等价代码是荒谬的:
var values = new Tuple<int, string>[] {
new Tuple<int, string>(1, "US"),
new Tuple<int, string>(2, "Canada"),
new Tuple<int, string>(3, "UK"),
new Tuple<int, string>(4, "Australia"),
new Tuple<int, string>(5, "Slovenia")
}
var dict = new Dictionary<int, string>(values);
我不相信元组原则有什么问题,但C#的语法太麻烦,无法充分利用它们。
答案 4 :(得分:4)
这是代码重用。而不是编写与我们制作的最后5个类似于类的类完全相同的结构的另一个类,你制作一个元组类,并在需要元组时使用它。
如果该类的唯一意义是“存储一对值”,那么我会说使用元组是一个明显的想法。如果你开始实现多个相同的类只是为了让你可以重命名这两个成员,我会说这是代码味道(就像我讨厌这个术语一样)。
答案 5 :(得分:2)
这只是原型质量的代码,很可能被粉碎在一起并且从未被重构过。不修复它只是懒惰。
元组的真正用途是用于通用功能,它实际上并不关心组件是什么,而只是在元组级别运行。
答案 6 :(得分:2)
Scala具有元组值类型,从2元组(Pairs)到具有20多个元素的元组。见First Steps to Scala(第9步):
val pair = (99, "Luftballons")
println(pair._1)
println(pair._2)
如果您需要将某些相对 ad hoc 目的的值捆绑在一起,则元组非常有用。例如,如果您有一个需要返回两个相当不相关的对象的函数,而不是创建一个新类来保存这两个对象,则从函数返回一个Pair。
我完全赞同其他海报可以滥用元组。如果元组具有对应用程序很重要的任何语义,则应使用适当的类。
答案 7 :(得分:1)
明显的例子是坐标对(或三重)。标签无关紧要;使用X和Y(和Z)只是一种惯例。使它们变得均匀可以清楚地表明它们可以用同样的方式处理。
答案 8 :(得分:1)
已经提到了很多东西,但我认为还应该提一下,有些编程风格与OOP不同,而且这些元组非常有用。
像Haskell这样的函数式编程语言根本就没有类。
答案 9 :(得分:0)
如果你正在做一个Schwartzian transform按特定键排序(重复计算代价很高),或类似的东西,那么一个类似乎有点矫枉过正:
val transformed = data map {x => (x.expensiveOperation, x)}
val sortedTransformed = transformed sort {(x, y) => x._1 < y._1}
val sorted = sortedTransformed map {case (_, x) => x}
拥有class DataAndKey
或其他任何东西在这里看起来有点多余。
你的例子不是一个元组的好例子,但我同意。