【问题标题】:Is Google Guava "harder" to use than Apache Collections? [closed]Google Guava 比 Apache Collections 更“难”使用吗? [关闭]
【发布时间】:2011-04-26 05:49:24
【问题描述】:

我正在考虑让我的团队(技能水平参差不齐)使用 Google Guava。在 Guava 之前,我会使用 Apache Collections(或其通用版本)。

与 Apache Collections 相比,Guava 在某些方面似乎更强大,但对于经验不足的程序员来说可能不太容易使用。这是我认为可以说明这一点的一个领域。

我继承的代码包含大量循环列表,这些列表本质上是异构值的映射,探测它们的值,进行空值检查,然后做一些琐碎的事情:

boolean foo( final List< MapLike > stuff, final String target ) {
  final String upperCaseTarget = target.toUpperCase(0;

  for( MapLike m : stuff ) {
     final Maplike n = (MapLike) m.get( "hard coded string" );
     if( n != null ) {
         final String s = n.get( "another hard code string" );
         if( s != null && s.toUpperCase().equals( upperCaseTarget ) ) {
            return true ;
         }
     }
   return false ;
}

我最初的想法是使用 Apache Collections Transformers:

boolean foo( final List< MapLike > stuff, final String target ) {
   Collection< String> sa = (Collection< String >) CollectionUtils.collect( stuff, 
     TransformerUtils.chainedTransformer( new Transformer[] { 
        AppUtils.propertyTransformer("hard coded string"),
        AppUtils.propertyTransformer("another hard coded string"),
        AppUtils.upperCaseTransformer()
         } ) );

    return sa.contains( target.toUpperCase() ) ;        

}

使用番石榴,我可能有两种方式:

boolean foo( final List< MapLike > stuff, final String target ) {
   Collection< String > sa = Collections2.transform( stuff,
       Functions.compose( AppUtils.upperCaseFunction(), 
       Functions.compose( AppUtils.propertyFunction("another hard coded string"), 
                          AppUtils.propertyFunction("hard coded string") ) ) );

    return sa.contains( target.toUpperCase() ) ;    
    // or
    // Iterables.contains( sa, target.toUpperCase() );
    // which actually doesn't buy me much

}

与 Apache Collections 相比,Functions.compose( g, f ) 颠倒了“直观”的顺序:函数从右到左应用,而不是 TransformerUtils.chainedTransformer 的“明显”从左到右应用。

一个更微妙的问题是,当 Guava 返回实时视图时,在实时视图上调用 contains 可能会多次应用(组合)函数,所以我真正应该这样做做的是:

   return ImmutableSet.copy( sa ).contains( target.toUpperCase() ) ;

但我的转换集中可能有空值,所以我不能这样做。当然,我可以将其转储到 java.util.Collection 中。

但这对我的(经验较少的)团队来说并不明显,即使我解释了它,也可能会在编码的热度中错过。我希望 Iterables.contains() 可能会“做正确的事”并知道一些魔术实例来区分实时视图代理和普通的旧集合,但事实并非如此。这使得番石榴可能更难使用。

也许我在我的实用程序类中编写了类似静态方法的东西来处理这个问题?

// List always uses linear search? So no value in copying?
// or perhaps I should copy it into a set?
boolean contains( final List list, final Object target ) {
  return list.contains( target ) ;
}

// Set doesn't use linear search, so copy?
boolean contains( final Set set, final Object target ) {
  //return ImmutableSet.copy( set ).contains( target ) ;
  // whoops, I might have nulls
  return Sets.newHashSet( set ).contains( target ) ;
}

或者也许只是超过一定大小的副本?

// Set doesn't use linear search, so copy?
boolean contains( final Set set, final Object target ) {
  final Set search = set.size() > 16 : Sets.newHashSet( set ) : set ;
  return search.contains( target ) ;
}

我想我是在问,“为什么在 Guava 中没有一个‘更简单的’transform”,我想答案是,“好吧,总是将它返回的内容转储到一个新的集合中,或者写您自己的转换可以做到这一点”。

但如果我需要这样做,可能不是 Guava 库的其他客户端吗?也许在 Guava 中有更好的方法,我不知道?

【问题讨论】:

  • "即使在我解释之后,也很可能会在编码的热潮中错过。" - 你们没有定期的代码检查吗?
  • 好吧,我继承的团队没有。我现在已经制定了。但我更愿意利用审查时间来解决更大的问题。另外,如果我的团队认为“新方法太难了”,我不会接受。而且我更喜欢一个想要以正确方式做事的团队,而不是一个仅仅因为害怕警告而以正确方式做事的团队。
  • compose() 参数顺序:我对走哪条路有一定程度的焦虑,所以我做了研究,发现两种方式的先例大致相同。
  • 我见过很少有库比 apache commons 库“更难”(这将是一个 eufemism)。除了必须说的话,我真的很喜欢番石榴。是对 Java 集合类的完美补充。
  • ListUtils.union 是一个令人困惑的 Apache Commons 方法的好例子。它使用两个列表的元素创建一个新列表,但是您需要检查 javadoc 以确定重复项会发生什么(很难不考虑使用 union 之类的名称的 SQL)。

标签: java collections guava


【解决方案1】:

我会说 Guava 绝对不会比 Apache Collections 更难使用。实际上,我会说这要容易得多。

Guava 的一大优势在于它不会暴露太多新的对象类型……它喜欢将它使用的大多数实际实现类型整齐地隐藏在只暴露接口的静态工厂方法后面.以各种Predicates 为例。在 Apache Collections 中,您拥有顶级公共实现类,例如:

NullPredicate
NotNullPredicate
NotPredicate
AllPredicate
AndPredicate
AnyPredicate
OrPredicate

再加一吨。

在 Guava 中,这些都被整齐地打包在一个顶级类中,Predicates

Predicates.isNull()
Predicates.notNull()
Predicates.not(...)
Predicates.and(...)
Predicates.or(...)

它们都没有公开它们的实现类,因为您不需要知道它!虽然 Apache Collections 确实有一个等效的 PredicateUtils,但它公开了 Predicates 的类型这一事实使其更难使用。正如我所看到的,Apache Collections 只是一堆不必要的可见类和不太有用的部分,它们增加了混乱,使获取和使用有用的部分变得更加困难。当您查看这两个库公开的类和接口的数量时,差异就很明显了:

  • Apache Collections 公开了 309 种类型。
  • Guava,包括它的所有包(不仅仅是 Collections)只公开了 191 种类型。

此外,Guava 更加谨慎地只包含真正有用的实用程序和类,它严格遵守它实现的接口的契约等等,我认为它是一个更高质量、更易于使用的库。

解决您的一些具体问题:

我实际上认为 Guava 为Functions.compose 选择的顺序直观(尽管我认为这是一个相当主观的论点)。请注意,在您使用 Guava 进行组合的示例中,应用函数的顺序从声明的末尾读取 分配最终结果的位置。您的示例的另一个问题是它不是类型安全的,因为原始示例涉及将 get 方法的结果转换为另一种类型。 Guava 的 compose 相对于 Apache Commons 示例中的 Transformers 数组的一个优势是 compose 可以对函数进行类型安全的组合,确保(在编译时)您正在应用的一系列函数将正常工作。 Apache 版本在这方面是完全不安全的。

浏览量优于副本:

第二,关于Collections2.transform的直播“问题”。坦率地说,你在这一点上是完全错误的。使用实时视图而不是将原始Collection 的所有元素复制到新的Collection 中实际上效率更高!以下是当您调用Collections2.transform 然后在返回的Collection 上调用contains 时会发生的情况:

  • 创建了包装原始视图Collection...原始视图和Function 都简单地分配给其中的字段。
  • 检索到Collection 的迭代器。
  • 对于Iterator 中的每个元素,将应用Function,获取该元素的转换值。
  • 当找到您正在检查的对象的转换值equals 的第一个元素时,contains 将返回。您只需迭代(并应用Function)直到找到匹配项! Function 每个元素最多应用一次!

Apache Collections 版本的作用如下:

  • 创建一个新的ArrayList 来存储转换后的值。
  • 获取原始Collection 的迭代器。
  • 对于原始Collection 的迭代器中的每个元素,应用函数并将结果添加到新的Collection。这是针对原始Collection每个元素 完成的,即使将Transformer 应用于第一个元素的结果与我们正在寻找的对象匹配!
  • 然后contains 将遍历新的Collection 中的每个元素以寻找结果。

以下是使用这两个库的大小为 N 的 Collection 的最佳和最坏情况。最好的情况是第一个元素 equals 的转换值是您使用 contains 查找的对象,最坏的情况是您使用 contains 查找的值在转换后的集合中不存在.

  • 番石榴
    • 最佳情况:迭代 1 个元素,应用 Function 1 次,存储 0 个额外元素。
    • 最坏情况:迭代N个元素,应用Function N次,存储0个额外元素。
  • 阿帕奇
    • 最佳情况:迭代 N + 1 个元素,应用 Transformer N 次,存储 N 个额外元素(转换后的集合)。
    • 最坏情况:迭代 2N 个元素,应用 Transformer N 次,存储 N 个额外元素(转换后的集合)。

我希望从上面可以明显看出,总的来说,视图是一件非常的好事!另外,任何时候都可以很容易地将视图复制到非视图集合中,这将具有与 Apache 版本相同的性能。但是,在您给出的任何示例中,它肯定没有有用。

作为最后的小注,Iterables.contains 的存在只是为了让您检查您不知道是 CollectionIterable 是否包含值。如果您给它的Iterable 实际上是Collection,它很好地只是在Collection 上调用contains(),以便您获得更好的性能(比如说,如果它是Set)。

【讨论】:

  • 对于作文顺序,我觉得有一个比较客观的答案,比如en.wikipedia.org/wiki/Function_composition。我认为它必须读作“在 f() 的结果上应用 g()。
  • @Sylvain:是的,这是有道理的。文档也将组合描述为“g(f(a))”。
  • 我想知道,您是如何获得这些关于 Guava 如何进行合成的信息的。 (和 apache 确实转换)。顺便说一句,惊人的答案。如果可以的话,我会添加更多的赞成票。
  • @doc_180:Guava 和 apache 集合都是开源项目,因此查看它们的源代码会告诉你它们是如何工作的。 Guava 的 Collection2.transform 的 Javadoc 还告诉您它返回一个实时视图,这意味着我描述的行为。
  • 对于单个contains()检查,查看方式肯定更好。如果您想检查许多对象的包含情况,创建一个哈希集副本可能会更好(取决于元素的数量,并假设有一个好的哈希函数)。
【解决方案2】:

作为 Guava 的开发者之一,我显然是有偏见的,但这里有几个 cmets。

易用性是 Guava 设计背后的主要目标之一。总是有改进的余地,我们渴望听到任何建议或疑虑。设计决策背后通常有一个基本原理,尽管每个人都可能会发现他们个人不同意的事情。

在实时视图方面,ColinD 描述了某些用例存在的性能优势。此外,有时您希望更改视图以更改原始集合,反之亦然。

现在,在某些情况下,复制集合可以提供更好的性能,但只需一行代码即可。虽然 Guava 可以包含 transformAndCopy() 方法,但我们省略了单行方法,除了 Maps.newHashMap() 等极其常见的情况。存在的方法越多,就越难找到所需的方法。

【讨论】:

    猜你喜欢
    • 2010-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-13
    • 2011-01-23
    • 1970-01-01
    相关资源
    最近更新 更多