技术开发 频道

Google pk Apache:谁的集合库更酷



【IT168 专稿】

集合是编程语言中的一个非常重要的特性,在Apache的Common程序集中为我们提供了一套更为强大的集合库。而最近火暴的Google也推出了自己的集合库,这套集合库不仅增强了我们感兴趣的功能,而且还使整个Java集合框架得到了空前的增强。
当我第一次使用Google的集合库时就有些困惑。这套Google集合库是为增加JDK的Java集合库框架而设计的,它的开发人员计划使这套库成为JDK7的一部分。但在这之前,Apache组织已经在Common中加入了集合类库,正是由于这一点,使我不明白的是一向以创新精神著称的Google为什么要做Apache已经做过的事。不过,最后我看了下关于对这套库的创始人的采访才了解Google的最终动机是为了使这套库提供对泛型的支持,而Apache的这套集合库目前并不支持泛型,而且Apache的这套库还有一些类明显违反了Java集合框架的规范。
为了验证Apache的这套集库是否真的如Google所说的那样,我特意查看了一下Apache的这套库,结果发现它确实不支持JDK5的泛型功能(这是一个非常明显的缺陷),但在SourceForge网站上的确声称支持泛型。
    至于那个违反Java集合框架规范的地方,我发现只有在Apache集合类中的Bag接口方法违反了这个规范,这是由于Bag接口本身的特性所决定的。如在Bag接口的add方法中,在Java集合框架的规范中规定add方法必须总是返回true,但当同类型的对象在Bag中存在时,add方法并不返回true。在这种情况下,在这种情况下,它仅仅增加计数,并返回false。Apache集合类还有一个一致性问题,我发现这套库的源程序和二进制程序有些不一致,src文件夹已经有很长时间没有更新了。
    基于以上的原因,我决定更深一步地研究Google的这套开源集合类库,并发现在其中更有价值的东西。在本文中首先描述了Google集合库的包和类(只是简单的提一下),然后会将Apache和Google的这两套集合类库进行一个比较,看看其中的差别。
    Google的这套集合库共分为两个包:
1. com.google.common.base:这个包由一些常用的类组成,在些类甚至可以在没有Java集合框架来的情况下使用。
2. com.google.common.collect:这个包所提供的类将大大增强原有的Java集合框架。

   
下面分别介绍一下这两个包,并将其和Apache的集合库做一下比较:

一. The com.google.common.base Package
    在这部分将讨论一下在com.google.common.base中的这个通用类包。如果我们直接使用过在Java集合框架包java.lang.ref中的任何SoftReference、WeakReference或PlantomReference类,以及使用ReferenceQueue写过代码,我们会为使用Google的这套库而兴奋不已。下面是Google的三个和java.lang.ref包对应的类:

1. FinalizablePhantomReference
2   FinalizableSoftReference
3. FinalizableWeakReference

    上述的三个类扩展了它们各自在Java集合类中对应的类,这些都都可以处理ReferenceQueue,并回调在这些类中定义的finalizeReferent()方法。因为如果当一个对象被垃圾回收器回收后,必须做一些清理工作时,finalizeReferent()方法将是最好的选择。

    例如,假设我们正在使用ImageChunks类来将一个图象二进制数据保存在一个对象中,然后当JVM运行变得缓慢时,GC将清楚这个对象类型。我们可以试着继承这个FinalizableSoftReference类,然后覆盖FinalizeReferent方法。这个方法允许我们在GC清除对象时写一些清除操作的代码。FinalizableSoftReference的最大优势就是我们不需要直接处理ReferenceQueue。
    要注意的是,这个包并不能取代ReferenceQueue,因为它并不是为增强类而设计的。

2. Function接口
    Function接口中的方法将用来进行数据的类型转换,如将String类型转换为Integer类型。Fuction将被使用在com.google.common.collect包中的Maps和Comparators中来执行转换任务。下面是几个非常典型的Function的应用:

(1)  Maps.uniqueIndex(...)使我们在支持Map实例的数据类型之间进行转换。这个谅无立锥之地 产生一个包含key和被转换的值的Map实例。

(2) Comparators.fromFunction(Function<F,T> function)方法可以让我们建立一个Comparator,个Comparator可以将两个值进行比较。Comparator在这个方法中被创建,这些对象将以自然顺序进行比较。

(3) 在Function中的forMap方法可以方便地在一个要进行转换地Map中查询。这个方法在一个Map实例中调用,并返回一个Function。当这个方法被调用时,将使用这个方法的输入参数作为关键字,在Map中查找数据。
    我们可以发现这个Function接口被使用在List、Iterators、Iterator和Functions类中。和Google的这个Function接口相对应的Apache集合类的接口是org.apache.commons.collections.Transformer,它被使用在以下几个类中:

(1). TransformedBag
(2). TransformedBuffer
(3). TransformedList
(4). TransformedSet
(5). TransformedMap

3. Nullable
    Nullable注释类型是一个非常棒的东西。对于很多商业应用程序的开发人员有一个很常见的误会,就是它们的应用程序不能抛出运行时错误。而Nullable允许运行时错误被接受,并以非常简捷的方式来将这个异常和方法绑定。也就是如果一个方法不能接收一个空参数,开发人员可以使用Nullable来执行一个是否为空的检查,以及在方法的第一行抛出一个错误。
 
4. 对象和预处理类(Objects、Preconditions)
    Objects和Preconditions可以在没有Java标准集合类的情况下使用:
 (1). Objects类包含了一系列有用的方法来辅助调用toString、hashCode和其他Object类的方法。在它的内部使用了数组来表示对适当类型的调用。
 (2) Preconditions类是一个非常有用的类,它用来核对传递到一个方法的参数,并抛出一个适当的异常。

5. Predicates
    Predicates可以对扫描一个集合进行建模(如,当我们想扫描所有的雇员对象集合时,并通过salary属性来过滤他们)。使用Predicates可以为我们节省很多写重复代码(这些代码主要是对集合列表中的对象的核对)的时间。

二、com.google.common.collect Package

    这部分将介绍一下Google的这个用于增强Java集合框架的包:com.google.common.collect。

1. BiMap和ClassToInstance类
 
    在集合库中的BiMap接口和Apache的公用集合中的BidiMap接口类似。它允许我们将五个key映射到一个值上,并形成一个整体。因此,key和value应该是唯一的。
    通过对这两个库的比较后发现,Google的HashBiMap和Apache的DaulTreeBidiMap在插入和查询的时间非常类似。然后,TreeBidiMap花了更长时间来插入数据,,但它更节省内存,这主要是因为它并不使用双重映射来表示key和value。
    如果我们的Key和value使用Enum类型,那么我们可以使用com.google.common.collect包中的EnumBiMap类直接建立一个bi-directional映射。但我想这么做的人很少,毕竟将Enum类型映射到Enum类型的意义不大。
    在Google的这套库中并没有和Apache的ClassToInstance 相对应的类,这个类可以用来将一个对象实例映射到它的class类型。
 
2. Comparators
    在Google和Apache的库中都有Comparators,这个接口可以使我们建立一个Comparator来和集合一起使用。如果我们必须在应用程序中大量使用Comparator,可详细看一下google提供文档。这些文档包含了Comparator的所有方法和使用。
    如果我们有两个comparators,并且想综合利用它们获得更佳的结果,可以很方便地使用Comparators.compound。这个方法返回了一个Comparator,这个Comparator将调用其他的comparator,直到发现了非空的结果。
    在org.apache.commons.collections.comparators包中包含了7个预定义的comparators方法。其中一个是org.apache.commons.collections.ComparatorUtils,这个类和com.google.common.collect.Comparators非常类似。和Apache的库不同,Google的这个Comparator将所有的功能定义在了一个类中:Comparators。这个类包含原始比较方法,还有在比较之前转换数据的功能。
 
3. Maps, Lists,和Sets    这三个类:Maps、Lists和Sets分别允许我们很方便地建立一个map、list和set对象,在建立的过程中无需调用构造方法。如我们可以使用下面的代码来替换Map m = new HashMap()。

    Map m = Maps.newHashMap();

    除了建立集合对象外,这些工厂类还有很多实用的方法,主要的方法如下:

1. Maps.immutableMap(...)方法可以让我们建立一个不变的Map,用于保存一个固定的key和value的对集。我们不再需要写4或5行代码来创建一些硬编码的key和value的映射对。
2. List.newArrayList(Iterator itr)方法建立了一个ArrayList对象,并将参数itr和ArrayList对象组合在一起。

在Apache的类库中也可以找到类似的类,如MapUtils、ListUtils和SetUtils。
这套库还包含了其他一些实用的类,如PrimitiveArrays和ObjectArrays,来简化简单类型的使用。PrimitiveArrays包含了有用的方法将List转换为简单类型。这将大大减少我们程序中的重复代码。


三、Google的集合库真的完美吗?
    Google的集合库虽然很强大,但是在其中也有不足的地方,如以下的功能在Apache的集合库有,但在Google的集合库中就没有:
1. org.apache.commons.collections.keyvalue.MultiKey:这个类允许我们使用多个值建立一个key。
2. org.apache.commons.collection.buffer:这个类为从集合中删除一个对象定义了一个契约。根据Buffer接口的文档,“从集合中删除对象的顺序可以基于插入的顺序(如FIFO队列或是LIFO栈),访问顺序(如一个LRU缓冲),任何的Comparator的顺序(如一个优先队列)或是任何其他明确定义的顺序”。
3. org.apache.commons.collections.functors:这个类并没有什么特殊的功能,只是当Predicate evaluate方法返回true时调用。

    除了上述的三个在Google的集合库中未发现的功能外,还有FixedSizeList、LazyList以及一些其他特殊的list类型类,如FixedSizeMap、Flat3Map、LazyMap、LRUMap以及ListOrderedSet。

四、我们到底该选哪个集合库
    虽然Google已经在它的GMail、Google Reader和Blogger中大量使用了这套集合库,但它现在仍然未正式发行(版本号为0.5)。从这个版本号来看,Google的集合库仍然是Alpha版本。目前它的功能只有85%经过了测试。
    现在我们已经知道Google的集合库和Apache的公共集合库的区别了。如果我们不是必须使用JDK5的泛型功能,那么Apache的集合库将是最好的选择,因为它要比Google的这套库更成熟。但在我各人看来,我更喜欢Google的集合库中提供新特性的类和包,这些东西使更容易学习和使用。如果Apache的集合库可以跟上时代的步伐,继续加入JDK5以及JDK的后续版本功能,那么Apache的集合库也许会更耀眼。
 
0
相关文章