【发布时间】:2009-01-25 01:05:38
【问题描述】:
大约一年前我发现了CollectionUtils 类,其中一些方法(如收集和转换)看起来真的很酷,但是,我还没有找到它在语法上更简洁的用途和\或用简单的循环编写逻辑更容易。
有没有发现这些方法(转换、predicatedCollection、collect 等)的独特\有用的用途,例如将转换器或谓词作为参数的方法?
【问题讨论】:
标签: java apache oop collections
大约一年前我发现了CollectionUtils 类,其中一些方法(如收集和转换)看起来真的很酷,但是,我还没有找到它在语法上更简洁的用途和\或用简单的循环编写逻辑更容易。
有没有发现这些方法(转换、predicatedCollection、collect 等)的独特\有用的用途,例如将转换器或谓词作为参数的方法?
【问题讨论】:
标签: java apache oop collections
我认为关键问题是设计/编码的灵活性。
如果您只有一个用例(例如,选择满足某些特定条件的集合成员),那么手动编写一个相对简单的循环是可行的。另一方面...
假设可能的条件集变得很大,或者甚至可以在运行时即时组合(甚至是动态的,基于用户输入/交互)。或者假设有一些非常复杂的条件可以用运算符(例如A和B,C而不是D等)组合成更多的情况。
假设在做出选择后,还有一些其他的处理需要对结果集合进行。
现在考虑一下代码的结构,它可能是由暴力的内联方法编写上述内容:一个包含复杂决策过程的外部循环,用于确定要执行哪些测试,与代码混合在一起对集合中“幸存的”成员做一件或多件事情。此类代码往往(尤其是随着时间的推移会随着维护而变得)难以理解且难以在不引入缺陷的情况下进行修改。
所以关键是要追求一个战略,其中每个方面:
可以独立编码和测试,然后根据需要组合在一起。
【讨论】:
collect() 在您有对象的可能替代表示时很有用。
例如,最近我正在处理一段代码,它需要匹配来自两个不同来源的对象列表。这些对象属于不同的类,因为它们在代码中的不同点使用,但出于我的目的,它们具有相同的相关概念(即它们都有一个 ID,都有一个属性路径,都有一个“级联”标志等) .
我发现定义这些属性的简单中间表示(作为内部类)、为两个具体对象类定义转换器要容易得多(同样非常简单,因为它只是使用相关的访问器方法来获取属性) ,然后使用collect() 将我传入的对象转换为中间表示。一旦它们在那里,我就可以使用标准的 Collections 方法来比较和操作这两个集合。
作为一个(半)具体的例子,假设我需要一种方法来检查表示层中的对象集是否是缓存在数据层中的对象的子集。使用上面概述的方法,可以这样做:
public boolean isColumnSubset(PresSpec pres, CachedDataSpec dataSpec)
{
final List<IntermediateRepresentation> presObjects = CollectionUtils.collect(pres.getObjects(), PRES_TRANSFORMER);
final List<IntermediateRepresentation> dataObjects = CollectionUtils.collect(dataSpec.getCached(), DATA_TRANSFORMER);
return dataObjects.containsAll(presObjects);
}
对我来说,这更易读,最后一行传达了该方法的真正意义在做什么,而不是循环:
public boolean isColumnSubset(PresSpec pres, CachedDataSpec dataSpec)
{
for (PresSpecificObject presObj : pres.getObjects())
{
boolean matched = false;
for (CachedDataObject dataObj : dataSpec.getCached())
{
if (areObjectsEquivalent(presObj, dataObj)) // or do the tests inline but a method is cleaner
{
matched = true;
break;
}
}
if (matched == false)
{
return false;
}
}
// Every column must have matched
return true;
}
这两个可能效率差不多,但就可读性而言,我想说第一个更容易立即理解。尽管它总体上是更多的代码行(由于定义了一个内部类和两个转换器),但将遍历实现与实际的“真假”逻辑分开使得后者更加清晰。另外,如果您有任何 KLOC 指标,它也不能成为珠子。 ;-)
【讨论】:
虽然我原则上同意 Uri,但没有闭包或函数字面量或其他任何东西,Java 对实际使用收集、转换等方法施加了相当高的语法成本。在很多情况下,实际的代码行是与编写简单循环的情况相同或更大。 addAll、removeAll 以及它们所有不将函数对象作为参数的朋友都是必不可少的。
所有这些也适用于同样优秀的Google Collections API。
很遗憾 Sun 有能力在 Java 7 中修复这些问题,但它appears they won't。胆小鬼。
【讨论】:
我不确定我是否同意你的说法......
一个简单的循环增加了复杂性,并且比一个具有合理名称的调用更不“可读和明显”。
重构布道者会声称您的目标通常应该是创建调用其他操作的扁平和简短的函数。虽然 addAll、find 等方法很容易自己实现,但避免使用它们会要求读者掌握比单个单词更复杂的东西,并且可能导致代码复制。
恕我直言,CollectionUtils 实际上提供了比标准 Java 集合库更简洁的操作。
【讨论】:
虽然我们不使用 CollctionUtils,但我们已经实现了一些我们自己和我们经常使用的类似实用程序
空(集合 c)
测试集合、字符串等是否为空
有(...)
只返回 !empty(...)
mapToProperty(Collection c, String property, Class newType)
这使用反射调用“属性”将 T1 的集合映射到 T2 的集合
implode(Collection c, String sep)
带有 c 元素的分隔字符串
【讨论】: