Commons-Collections最常用类介绍


Commons Collections,又是一个重量级的东西,为 Java 标准的 Collections API提供了相当好的补充。我不知道其他人,就我自己而言,让我用 java.util.Collection 及其子类,加上java.util.Collections 类提供的操作方法,处理一些简单的数据结构问题还可以,稍微复杂一点的就觉得 有点头痛,很多细节的地方需要我插入这样那样的小逻辑,或者感觉它太死板,不够灵活,再或者确实有点晦涩吧。再说了,如果我只是处理一般 的数据结构问题,为什么不自己用数组或者自定义的链表来做,再加上 Jakarta Commons 的Lang 提供的 ArrayUtils、StringUtils 等,已经基本够 了,性能可以保证,那么还要这个 Collections API干嘛。当然,说到这里有些偏激了,Collections 当然有它存在的道理,能够把常用的数据结 构归纳起来,以通用的方式去维护和访问,这应该说是一种进步,但是用起来似乎不够友好。这个时候我就会想,如果 Java 比现在做得更好用些, 或者有一套第三方的 API把我的这些需求抽象出来,实现了,该多好。Commons Collections 就是这样一套 API。 在这里可以找到下载链接:(binary 和src 都有) http://jakarta.apache.org/site/downloads/downloads_commons-collections.cgi 目前Commons Collection 发布的最新版本是3.1。建议下载这个3.1版本,页面上出现的2.1.1是针对2.1不兼容3.0而发布的升级维护版。 我们先来浏览一下它的包结构。一共是12个: org.apache.commons.collections – Commons Collections 自定义的一组公用的接口和工具类 org.apache.commons.collections.bag –实现Bag 接口的一组类 org.apache.commons.collections.bidimap –实现BidiMap 系列接口的一组类 org.apache.commons.collections.buffer –实现Buffer 接口的一组类 org.apache.commons.collections.collection –实现java.util.Collection 接口的一组类 org.apache.commons.collections.comparators –实现java.util.Comparator 接口的一组类 org.apache.commons.collections.functors – Commons Collections 自定义的一组功能类 org.apache.commons.collections.iterators –实现java.util.Iterator 接口的一组类 org.apache.commons.collections.keyvalue –实现集合和键/值映射相关的一组类 org.apache.commons.collections.list –实现java.util.List 接口的一组类 org.apache.commons.collections.map –实现Map 系列接口的一组类 org.apache.commons.collections.set –实现Set 系列接口的一组类 用过Java Collections API的朋友大概或多或少会同意我如下的划分:在 Java 的Collections API中,不狭义的区分语法上的接口和类,把它们 都看作是类的话,大致我们可以发现三种主要的类别: 1-容器类:如 Collection、List、Map 等,用于存放对象和进行简单操作的; 2-操作类:如 Collections、Arrays 等,用于对容器类的实例进行相对复杂操作如排序等; 3-辅助类:如Iterator、Comparator 等,用于辅助操作类以及外部调用代码实现对容器类的操作,所谓辅助,概括而通俗的来讲,就是这些类提供 一种算法,你给它一个对象或者一组对象,或者仅仅是按一定的规则调用它,它给你一个运算后的答案,帮助你正确处理容器对象。比如Iterator 会告诉你容器中下一个对象有没有、是什么,而 Comparator 将对象大小/先后次序的算法逻辑独立出来。 同样,Jakarta Commons Collections 我们细细看来,也能够找出类似的划分: 1-作为容器类的补充,我们可以找到 Bag、Buffer、BidiMap、OrderedMap 等等; 2-作为操作类的补充,我们可以找到 CollectionUtils、IteratorUtils、ListUtils、SetUtils 等等; 3-作为辅助类的补充,我们可以找到 MapIterator、Closure、Predicate、Transformer 等等; 对于这样的一个大包,当然不可能一个类一个类的讲了,找一些常用的和有用的当做接下来讨论的话题吧。大概列个清单: Bag HashBag BagUtils Buffer BlockingBuffer BoundedFifoBuffer PriorityBuffer BufferUtils MultiMap BidiMap CaseInsensitiveMap LazyMap MapUtils TypedCollection CollectionUtils ReverseComparator ComparatorChain NullComparator FixedOrderComparator ComparatorUtils Predicate AndPredicate OrPredicate AllPredicate OnePredicate NonePredicate PredicateUtils Transformer ChainedTransformer SwitchTransformer TransformerUtils Closure ChainedClosure IfClosure WhileClosure ClosureUtils LoopingIterator ArrayListIterator FilterIterator UniqueFilterIterator IteratorUtils 总共9组,在接下来的笔记中我们一起慢慢的看。 先来看 Bag 组。 Bag HashBag BagUtils Bag 是在org.apache.commons.collections 包中定义的接口,它extends java.util.Collection,而它的实现类都被放在下面的 bag 包中。之所以 有这样一组类型,是因为我们有时候需要在 Collection 中存放多个相同对象的拷贝,并且需要很方便的取得该对象拷贝的个数。需要注意的一点是 它虽然 extends Collection,但是如果真把它完全当作 java.util.Collection 来用会遇到语义上的问题,详细信息参考 Javadoc。 HashBag 是Bag 接口的一个标准实现。而 BagUtils 提供一组 static 的方法让调用者获取经过不同装饰后的 Bag 实例。 还是举例子来看: /** Book.java */ package sean.study.commons.collections; import org.apache.commons.lang.builder.ToStringBuilder; import org.apache.commons.lang.builder.ToStringStyle; public class Book { private String name; private String isbn; private double retailPrice; public Book() { } public Book(String name, String isbn, double retailPrice) { this.name = name; this.isbn = isbn; this.retailPrice = retailPrice; } public String toString() { return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) .append("name", name) .append("ISBN", isbn) .append("retailPrice", retailPrice) .toString(); } public String getIsbn() { return isbn; } public void setIsbn(String isbn) { this.isbn = isbn; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getRetailPrice() { return retailPrice; } public void setRetailPrice(double retailPrice) { this.retailPrice = retailPrice; } } /** BagUsage.java */ package sean.study.commons.collections; import org.apache.commons.collections.Bag; import org.apache.commons.collections.BagUtils; import org.apache.commons.collections.bag.HashBag; import org.apache.commons.lang.StringUtils; public class BagUsage { public static void main(String[] args) { demoBagUsage(); } public static void demoBagUsage() { System.out.println(StringUtils.center(" demoBagUsage ", 40, "=")); // data setup Book book1 = new Book("Refactoring Workbook","7-5083-2208-8", 29.8); Book book2 = new Book("J2EE Design Patterns","7-5083-3099-4", 45); Book book3 = new Book("Agile Software Development","7-5083-1503-0", 59); // create a bag Bag myBag = BagUtils.typedBag(new HashBag(), Book.class); myBag.add(book1, 360); myBag.add(book2, 500); myBag.add(book3, 170); // calculations for a bag double price1 = book1.getRetailPrice(); double price2 = book2.getRetailPrice(); double price3 = book3.getRetailPrice(); int book1Count = myBag.getCount(book1); int book2Count = myBag.getCount(book2); int book3Count = myBag.getCount(book3); double totalValue = (price1 * book1Count) + (price2 * book2Count) + (price3 * book3Count); // dispaly results System.out.println("There are " + book1Count + " copies of " + book1.getName() + "."); System.out.println("There are " + book2Count + " copies of " + book2.getName() + "."); System.out.println("There are " + book3Count + " copies of " + book3.getName() + "."); System.out.println("The total value of these books is: " + totalValue); System.out.println(); } } 以下是运行结果: ============= demoBagUsage ============= There are 360 copies of Refactoring Workbook. There are 500 copies of J2EE Design Patterns. There are 170 copies of Agile Software Development. The total value of these books is: 43258.0 需要说明的是,以上的代码仅仅为了演示如何使用 Bag,实际应用不建议像这样硬编码。 来看Buffer 组。 Buffer BlockingBuffer BoundedFifoBuffer PriorityBuffer UnboundedFifoBuffer BufferUtils Buffer 是定义在 org.apache.commons.collections 包下面的接口,用于表示按一定顺序除去成员对象的 collection 如队列等。具体的实现类在 org.apache.commons.collections.buffer 包下可以找到。 BufferUtils 提供很多静态/工具方法装饰现有的 Buffer 实例,如将其装饰成 BlockingBuffer、执行类型检查的 TypedBuffer、或者不可改变的 UnmodifiableBuffer 等等。 最简单直接的 Buffer 实现类是 UnboundedFifoBuffer,提供先进先出的大小可变的队列。而BoundedFifoBuffer 则是对其大小进行了限制,是固定 大小的先进先出队列。BlockingBuffer 要在多线程的环境中才能体现出它的价值,尤其是当我们需要实现某种流水线时这个 BlockingBuffer 很有 用:每个流水线上的组件从上游的 BlockingBuffer 获取数据,处理后放到下一个 BlockingBuffer 中依次传递。BlockingBuffer 的核心特色通俗点 说就是如果你向它要东西,而它暂时还没有的话,你可以一直等待直至拿到为止。PriorityBuffer 则提供比一般的先进先出 Buffer 更强的控制力: 我们可以自定义 Comparator 给它,告诉它怎么判定它的成员的先后顺序,优先级最高的最先走。 为了方便和清晰的需要,我在这里只举一个 BoundedFifoBuffer,包装成 TypedBuffer,看看在具体的代码中通常如何使用 Buffer:(还是沿用上次 的Book 类) package sean.study.commons.collections; import java.util.Iterator; import org.apache.commons.collections.Buffer; import org.apache.commons.collections.BufferUtils; import org.apache.commons.collections.buffer.BoundedFifoBuffer; import org.apache.commons.lang.StringUtils; public class BufferUsage { public static void main(String[] args) { demoBufferUsage(); } public static void demoBufferUsage() { System.out.println(StringUtils.center(" demoBagUsage ", 40, "=")); // data setup Book book1 = new Book("Refactoring Workbook","7-5083-2208-8", 29.8); Book book2 = new Book("J2EE Design Patterns","7-5083-3099-4", 45); Book book3 = new Book("Agile Software Development","7-5083-1503-0", 59); Book book4 = new Book("Professional JSP","7-5053-8005-2", 100); // create a Buffer Buffer buffer = BufferUtils.typedBuffer(new BoundedFifoBuffer(3), Book.class); buffer.add(book1); buffer.add(book2); buffer.add(book3); Book removed = (Book) buffer.remove(); System.out.println("Removed:"); System.out.println(removed); buffer.add(book4); // get items in buffer for (int i = 0; i < 3; i++) { System.out.println(buffer.get()); buffer.remove(); } System.out.println(StringUtils.repeat("=", 40)); } } 以下是运行结果: ============= demoBagUsage ============= Removed: sean.study.commons.collections.Book@e09713[ name=Refactoring Workbook ISBN=7-5083-2208-8 retailPrice=29.8 ] Remaining: sean.study.commons.collections.Book@e09713[ name=J2EE Design Patterns ISBN=7-5083-3099-4 retailPrice=45.0 ] sean.study.commons.collections.Book@47b480[ name=Agile Software Development ISBN=7-5083-1503-0 retailPrice=59.0 ] sean.study.commons.collections.Book@19b49e6[ name=Professional JSP ISBN=7-5053-8005-2 retailPrice=100.0 ] ======================================== 我们可以看到,Buffer 的add 和remove 方法分别添加新成员和删除最先加入的成员。由于我们的 Buffer 定义为只能装3个Book 类的实例,所以不 论我们试图加入其他类型的对象,或者加入超过3个,操作都将失败。如果我们在遍历时使用 get()而不调用 remove(),那么我们将得到3个相同的 拷贝,而这正是我们期望的 FIFO队列的行为。假如你需要遍历并保留数据,可以使用标准的 Iterator 机制 接下来看 Map 组。 BidiMap MultiMap LazyMap MapUtils Commons Collections 在java.util.Map 的基础上扩展了很多接口和类,比较有代表性的是 BidiMap、MultiMap 和LazyMap。跟Bag和Buffer 类似, Commons Collections 也提供了一个 MapUtils。 所谓BidiMap,直译就是双向 Map,可以通过 key 找到value,也可以通过 value 找到key,这在我们日常的代码-名称匹配的时候很方便:因为我们 除了需要通过代码找到名称之外,往往也需要处理用户输入的名称,然后获取其代码。需要注意的是 BidiMap 当中不光 key不能重复,value 也不 可以。 所谓MultiMap,就是说一个 key 不在是简单的指向一个对象,而是一组对象,add()和remove()的时候跟普通的 Map 无异,只是在 get()时返回一 个Collection,利用 MultiMap,我们就可以很方便的往一个 key 上放数量不定的对象,也就实现了一对多。 所谓LazyMap,意思就是这个 Map 中的键/值对一开始并不存在,当被调用到时才创建,这样的解释初听上去是不是有点不可思议?这样的 LazyMap 有用吗?我们这样来理解:我们需要一个 Map,但是由于创建成员的方法很“重”(比如数据库访问),或者我们只有在调用 get()时才知道如何创 建,或者Map 中出现的可能性很多很多,我们无法在 get()之前添加所有可能出现的键/值对,或者任何其它解释得通的原因,我们觉得没有必要去 初始化一个 Map而又希望它可以在必要时自动处理数据生成的话,LazyMap 就变得很有用了。 我们还是通过一个具体的例子来说明: package sean.study.commons.collections; import java.util.Date; import java.util.HashMap; import java.util.Map; import org.apache.commons.collections.BidiMap; import org.apache.commons.collections.Factory; import org.apache.commons.collections.MultiHashMap; import org.apache.commons.collections.MultiMap; import org.apache.commons.collections.bidimap.DualHashBidiMap; import org.apache.commons.collections.map.LazyMap; import org.apache.commons.lang.StringUtils; public class MapUsage { public static void main(String[] args) { demoBidiMap(); demoMultiMap(); demoLazyMap(); } public static void demoBidiMap() { System.out.println(StringUtils.center(" demoBidiMap ", 40, "=")); BidiMap bidiMap = new DualHashBidiMap(); bidiMap.put("BJ","Beijing"); bidiMap.put("SH","Shanghai"); bidiMap.put("GZ","Guangzhou"); bidiMap.put("CD","Chengdu"); System.out.println("Key-Value: BJ = " + bidiMap.get("BJ")); System.out.println("Value-Key: Chengdu = " + bidiMap.getKey("Chengdu")); System.out.println(StringUtils.repeat("=", 40)); } public static void demoMultiMap() { System.out.println(StringUtils.center(" demoMultiMap ", 40, "=")); MultiMap multiMap = new MultiHashMap(); multiMap.put("Sean","C/C++"); multiMap.put("Sean","OO"); multiMap.put("Sean","Java"); multiMap.put("Sean",".NET"); multiMap.remove("Sean","C/C++"); System.out.println("Sean's skill set: " + multiMap.get("Sean")); System.out.println(StringUtils.repeat("=", 40)); } public static void demoLazyMap() { System.out.println(StringUtils.center(" demoLazyMap ", 40, "=")); // borrowed from Commons Collection's Javadoc Factory factory = new Factory() { public Object create() { return new Date(); } }; Map lazy = LazyMap.decorate(new HashMap(), factory); System.out.println(lazy.get("NOW")); System.out.println(StringUtils.repeat("=", 40)); } } 以下是运行结果: ============= demoBidiMap ============== Key-Value: BJ = Beijing Value-Key: Chengdu = CD ======================================== ============= demoMultiMap ============= Sean's skill set: [OO, Java, .NET] ======================================== ============= demoLazyMap ============== Wed Aug 03 12:44:56 CST 2005 ======================================== 简单说一下这个 Factory,它是定义在 org.apache.commons.collections 包下面的一个接口,用于自定义对象的创建过程。这个有点像是后面我们 要讲的 Transformer 的简化版本,但是也更直接也很好用,至少 Commons Collections 通过它向开发人员开放了一个可以方便控制对象创建细节的 接口。 接下来看看 Collection 组。 TypedCollection CollectionUtils 首先就是这个 TypedCollection,它实际上的作用就是提供一个 decorate 方法,我们传进去一个 Collection 和需要的类型甄别信息 java.lang.Class,它给我们创建一个全新的强类型的 Collection。我们其实在 bag、buffer、list、map、set 这些子包中都可以找到分别对应 Bag、 Buffer、List、Map、Set 接口的 TypedXxxx 版本。 方法签名: public static Collection decorate(Collection coll, Class type) 当它执行时,它会判断 coll 是否为 null,同时如果 coll 包含数据,它会对数据进行验证,看是否满足指定的 type 条件。最后它返回一个强类型 的Collection,当我们对这个强类型的 Collection 进行add 操作时,它会帮我们确保添加的是正确的类型。 而这个 CollectionUtils 可能大家都已经想到了,就是提供一组针对 Collection 操作的工具/静态方法。比较有意思的是对 Collection 的转型、合 并、减等操作。 由于这两个类的功能和作用都比较清晰,我就不举例说明了,需要进一步了解的请看 Javadoc。 接下来我们会讲到辅助类,首先看 Comparator 组。 ReverseComparator ComparatorChain NullComparator FixedOrderComparator ComparatorUtils 其实Comparator 这个概念并不是 Commons Collections 引入的,在标准的 Java Collections API中,已经明确定了一个 java.util.Comparator 接口,只是有很多人并不了解,Commons Collections 也只是扩展了这个接口而已。这个 java.util.Comparator 定义如下核心方法: public int compare(Object arg0, Object arg1) 传给它两个对象,它要告诉我们这两个对象哪一个在特定的语义下更“大”,或者两者相等。如果arg0 > arg1,返回大于0的整数;如果arg0 = arg1, 返回0;如果 arg0 < arg2,返回小于0的整数。 我们看看 Commons Collections 给我们提供了哪些 Comparator 的实现类(都在 org.apache.commons.collections.comparators 包下面): BooleanComparator – 用于排序一组 Boolean 对象,指明先 true 还是先 false; ComparableComparator – 用于排序实现了 java.lang.Comparable 接口的对象(我们常用的 Java 类如String、Integer、Date、Double、File、 Character 等等都实现了 Comparable 接口); ComparatorChain – 定义一组 Comparator 链,链中的 Comparator 对象会被依次执行; FixedOrderComparator – 用于定义一个特殊的顺序,对一组对象按照这样的自定义顺序进行排序; NullComparator – 让null 值也可参与比较,可以设定为先 null 或者后 null; ReverseComparator – 将原有的 Comparator 效果反转; TransformingComparator – 将一个 Comparator 装饰为具有 Transformer 效果的 Comparator。 // 有关Transformer 的内容会在以后的笔记中讲到。 以上除了 ComparatorChain 之外,似乎都是实现一些很基本的比较方法,但是当我们用 ComparatorChain 将一组 Comparator 串起来之后,就可以实 现非常灵活的比较操作。那么这些 Comparator 在实际代码中如何使用呢?看例子: /** Issue.java */ package sean.study.commons.collections; import org.apache.commons.lang.builder.ToStringBuilder; import org.apache.commons.lang.builder.ToStringStyle; public class Issue { private long id; private String severity; private String owner; public Issue() { } public Issue(long id, String severity, String owner) { this.id = id; this.severity = severity; this.owner = owner; } public String toString() { return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) .append("id", id) .append("severity", severity) .append("owner", owner) .toString(); } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getOwner() { return owner; } public void setOwner(String owner) { this.owner = owner; } public String getSeverity() { return severity; } public void setSeverity(String severity) { this.severity = severity; } } /** ComparatorUsage.java */ package sean.study.commons.collections; import java.util.Arrays; import java.util.Comparator; import org.apache.commons.beanutils.BeanComparator; import org.apache.commons.collections.comparators.ComparatorChain; import org.apache.commons.collections.comparators.FixedOrderComparator; import org.apache.commons.lang.StringUtils; public class ComparatorUsage { public static void main(String[] args) { demoComparator(); } public static void demoComparator() { System.out.println(StringUtils.center(" demoComparator ", 40, "=")); // data setup Issue[] issues = new Issue[] { new Issue(15102, "Major", "John"), new Issue(15103, "Minor", "Agnes"), new Issue(15104, "Critical", "Bill"), new Issue(15105, "Major", "John"), new Issue(15106, "Major", "John"), new Issue(15107, "Critical", "John"), new Issue(15108, "Major", "Agnes"), new Issue(15109, "Minor", "Julie"), new Issue(15110, "Major", "Mary"), new Issue(15111, "Enhancement", "Bill"), new Issue(15112, "Minor", "Julie"), new Issue(15113, "Major", "Julie") }; // comparators setup String[] severityOrder = {"Critical", "Major", "Minor", "Enhancement"}; Comparator severityComparator = new FixedOrderComparator(severityOrder); ComparatorChain compChain = new ComparatorChain(); compChain.addComparator(new BeanComparator("owner")); compChain.addComparator(new BeanComparator("severity", severityComparator)); compChain.addComparator(new BeanComparator("id")); // sort and display Arrays.sort(issues, compChain); for (int i = 0; i < issues.length; i++) { System.out.println(issues[i]); } System.out.println(StringUtils.repeat("=", 40)); } } 输出结果为: ============ demoComparator ============ Issue[id=15108,severity=Major,owner=Agnes] Issue[id=15103,severity=Minor,owner=Agnes] Issue[id=15104,severity=Critical,owner=Bill] Issue[id=15111,severity=Enhancement,owner=Bill] Issue[id=15107,severity=Critical,owner=John] Issue[id=15102,severity=Major,owner=John] Issue[id=15105,severity=Major,owner=John] Issue[id=15106,severity=Major,owner=John] Issue[id=15113,severity=Major,owner=Julie] Issue[id=15109,severity=Minor,owner=Julie] Issue[id=15112,severity=Minor,owner=Julie] Issue[id=15110,severity=Major,owner=Mary] ======================================== 我们可以看到,ComparatorChain 中的Comparator 被依次执行,先按name,再按我们自定义的 severity 次序,再按id,最终我们得到了重新排列 的数组。 接下来看 Predicate 组 Predicate AndPredicate OrPredicate AllPredicate OnePredicate NonePredicate PredicateUtils Predicate 是Commons Collections 中定义的一个接口,可以在 org.apache.commons.collections 包中找到。其中定义的方法签名如下: public boolean evaluate(Object object) 它以一个 Object 对象为参数,处理后返回一个 boolean 值,检验某个对象是否满足某个条件。其实这个 Predicate 以及上一篇笔记提到的 Comparator 还有我们即将看到的 Transformer 和Closure 等都有些类似 C/C++中的函数指针,它们都只是提供简单而明确定义的函数功能而已。 跟其他组类似,Commons Collections 也提供了一组定义好的 Predicate 类供我们使用,这些类都放在 org.apache.commons.collections.functors 包中。当然,我们也可以自定义 Predicate,只要实现这个 Predicate 接口即可。在 Commons Collections 中我们也可以很方便使用的一组预定义 复合Predicate,我们提供2个或不定数量个 Predicate,然后交给它,它可以帮我们处理额外的逻辑,如AndPredicate 处理两个 Predicate,只有 当两者都返回 true 它才返回 true;AnyPredicate 处理多个 Predicate,当其中一个满足就返回 true,等等。 看看具体的代码中如何使用这些 Predicate 吧: package sean.study.commons.collections; import org.apache.commons.collections.Predicate; import org.apache.commons.collections.PredicateUtils; import org.apache.commons.collections.functors.InstanceofPredicate; import org.apache.commons.collections.functors.NotNullPredicate; import org.apache.commons.lang.BooleanUtils; import org.apache.commons.lang.StringUtils; public class PredicateUsage { public static void main(String[] args) { demoPredicates(); } public static void demoPredicates() { System.out.println(StringUtils.center(" demoPredicates ", 40, "=")); Predicate p1 = new InstanceofPredicate(String.class); Predicate p2 = NotNullPredicate.getInstance(); Predicate p3 = new Predicate() { public boolean evaluate(Object obj) { String str = (String) obj; return StringUtils.isAlphanumeric(str) && str.length() >= 6 && str.length() <= 10; } }; Predicate p4 = PredicateUtils.allPredicate(new Predicate[]{p1, p2, p3}); String input = "ABCD1234"; Object[] raw = new Object[] { "Is '", input, "' a valid input? ", BooleanUtils.toStringYesNo(p4.evaluate(input)), "." }; System.out.println(StringUtils.join(raw)); System.out.println(StringUtils.repeat("=", 40)); } } 输出结果如下: ============ demoPredicates ============ Is 'ABCD1234' a valid input? yes. ======================================== 这里面我首先定义了3个简单的 Predicate,p1判断对象是否为 String 的实例,p2判断是否对象为 null,p3是自定义的,判断是否为数字字母的组 合,并且长度在6~10字符。然后我用 AllPredicate 将它们组合到一起,成为 p4,当p1、p2、p3都满足时,p4的evaluate 方法才返回 true。利用 Predicate 我们可以把判断 true 或false 的逻辑从特定的业务代码分离出来,以后我们重用也好,重新组装也好,都是很方便的。 接下来看 Transformer 组。 Transformer ChainedTransformer SwitchTransformer TransformerUtils 我们有时候需要将某个对象转换成另一个对象供另一组方法调用,而这两类对象的类型有可能并不是出于同一个继承体系的,或者说出了很基本 的 Object 之外没有共同的父类,或者我们根本不关心他们是不是有其他继承关系,甚至就是同一个类的实例只是对我们而言无所谓,我们为了它能够 被后续的调用者有意义的识别和处理,在这样的情形,我们就可以利用 Transformer。除了基本的转型 Transformer 之外,Commons Collections 还 提供了 Transformer 链和带条件的 Transformer,使得我们很方便的组装出有意义的转型逻辑。 假定我们在处理员工聘用时,需要将原来的 Applicant 对象转换为 Employee 对象,而 Applicant 类和Employee 类无论继承关系、字段内容、具体 业务职能等等都不是同一派系的,只是某些字段是相关的,且要求必要的转换,那么这个时候我们使用 Transformer 就可以比较方便的实现这项功 能,并且由于它的实现是灵活的、模块化的,使得今后的维护也变得清晰和易于处理。代码如下: /** Applicant.java */ package sean.study.commons.collections; import org.apache.commons.lang.builder.ToStringBuilder; import org.apache.commons.lang.builder.ToStringStyle; public class Applicant { private String name; private int age; private String applyFor; public Applicant() { } public Applicant(String name, int age, String applyFor) { this.name = name; this.age = age; this.applyFor = applyFor; } public String toString() { return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) .append("name", name) .append("age", age) .append("applyFor", applyFor) .toString(); } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getApplyFor() { return applyFor; } public void setApplyFor(String applyFor) { this.applyFor = applyFor; } public String getName() { return name; } public void setName(String name) { this.name = name; } } /** Employee.java */ package sean.study.commons.collections; import java.util.Date; import org.apache.commons.lang.builder.ToStringBuilder; import org.apache.commons.lang.builder.ToStringStyle; import org.apache.commons.lang.time.DateFormatUtils; public class Employee { private String name; private int age; private Date dateJoined; private String grade; private double salary; public Employee() { } public Employee(String name, int age, Date dateJoined, String grade, double salary) { this.name = name; this.age = age; this.dateJoined = dateJoined; this.grade = grade; this.salary = salary; } public String toString() { return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) .append("name", name) .append("age", age) .append("dateJoined", DateFormatUtils.format(dateJoined, "yyyy-MM-dd")) .append("grade", grade) .append("salary", salary) .toString(); } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Date getDateJoined() { return dateJoined; } public void setDateJoined(Date dateJoined) { this.dateJoined = dateJoined; } public String getGrade() { return grade; } public void setGrade(String grade) { this.grade = grade; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } } /** TransformerUsage.java */ package sean.study.commons.collections; import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.Iterator; import java.util.List; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.Predicate; import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.SwitchTransformer; import org.apache.commons.lang.StringUtils; public class TransformerUsage { public static void main(String[] args) { demoTransformerUsage(); } public static void demoTransformerUsage() { System.out.println(StringUtils.center(" demoTransformerUsage ", 40, "=")); // data setup Applicant[] applicants = new Applicant[] { new Applicant("Tony", 26, "Developer"), new Applicant("Michelle", 24, "Tester"), new Applicant("Jack", 28, "Project Manager") }; List appList = Arrays.asList(applicants); // predicate setup Predicate isDeveloper = new Predicate() { public boolean evaluate(Object obj) { Applicant app = (Applicant) obj; return "Developer".equalsIgnoreCase(app.getApplyFor()); } }; Predicate isTester = new Predicate() { public boolean evaluate(Object obj) { Applicant app = (Applicant) obj; return "Tester".equalsIgnoreCase(app.getApplyFor()); } }; Predicate isPM = new Predicate() { public boolean evaluate(Object obj) { Applicant app = (Applicant) obj; return "Project Manager".equalsIgnoreCase(app.getApplyFor()); } }; Predicate[] checkApplyFor = new Predicate[] { isDeveloper, isTester, isPM }; // transformer setup Transformer developerTransformer = new Transformer() { public Object transform(Object obj) { Applicant app = (Applicant) obj; return new Employee( app.getName(), app.getAge(), new Date(), "E4", 2000 ); } }; Transformer testerTransformer = new Transformer() { public Object transform(Object obj) { Applicant app = (Applicant) obj; return new Employee( app.getName(), app.getAge(), new Date(), "E4", 2000 ); } }; Transformer pmTransformer = new Transformer() { public Object transform(Object obj) { Applicant app = (Applicant) obj; return new Employee( app.getName(), app.getAge(), new Date(), "E5", 3000 ); } }; Transformer[] transformers = new Transformer[] { developerTransformer, testerTransformer, pmTransformer }; // transform Transformer employTransformer = new SwitchTransformer( checkApplyFor, transformers, null ); Collection employed = CollectionUtils.collect(appList, employTransformer); // output System.out.println("Applicants: "); Iterator iter1 = appList.iterator(); while (iter1.hasNext()) { System.out.println(iter1.next()); } System.out.println("Employed: "); Iterator iter2 = employed.iterator(); while (iter2.hasNext()) { System.out.println(iter2.next()); } System.out.println(StringUtils.repeat("=", 40)); } } 以下是运行结果: ========= demoTransformerUsage ========= Applicants: Applicant[name=Tony,age=26,applyFor=Developer] Applicant[name=Michelle,age=24,applyFor=Tester] Applicant[name=Jack,age=28,applyFor=Project Manager] Employed: Employee[name=Tony,age=26,dateJoined=2005-08-05,grade=E4,salary=2000.0] Employee[name=Michelle,age=24,dateJoined=2005-08-05,grade=E4,salary=2000.0] Employee[name=Jack,age=28,dateJoined=2005-08-05,grade=E5,salary=3000.0] ======================================== 我们首先定义一组 Predicate,用于在 SwitchTransformer 中判断采用那个具体的 Transformer,这个具体的 Transformer 也是通过数组同时传递给 SwitchTransformer 的构造方法的。不同的 Predicate 可以有不同的实现,不同的 Transformer 也可以有不同的实现,因为它们之间实际上完全是 相互独立的。这就使我们有效的分离了逻辑和具体业务。 接下来看 Closure 组。 Closure ChainedClosure IfClosure WhileClosure ClosureUtils Closure 这一组接口和类提供一个操作对象的 execute 方法,为我们在处理一系列对象时可以将处理逻辑分离出来。理论上讲,使用 Transformer 也可以达到类似的效果,只要输出对象和输入对象是同一个对象就好,但是Closure 接口定义的 execute 方法返回 void,并且从效果和功能区分上, Closure 可以更好的诠释对象处理或执行的意思。而事实上,ClosureUtils 中也提供了一个 asClosure 方法包装一个现成的 Transformer。 沿用前面的 Emploee 类,我们来给一组员工涨工资: package sean.study.commons.collections; import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.Iterator; import org.apache.commons.collections.Closure; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; public class ClosureUsage { public static void main(String[] args) { demoClosureUsage(); } public static void demoClosureUsage() { System.out.println(StringUtils.center(" demoClosureUsage ", 40, "=")); // data setup Employee[] employees = new Employee[] { new Employee("Tony", 26, new Date(), "E4", 2000), new Employee("Michelle", 24, new Date(), "E4", 2000), new Employee("Jack", 28, new Date(), "E5", 3000) }; Collection empColl = Arrays.asList(employees); printColl("Before salary increase:", empColl); // closure setup Closure salaryIncreaseClosure = new Closure() { public void execute(Object obj) { Employee emp = (Employee) obj; emp.setSalary(emp.getSalary() * 1.20); } }; // salary increase CollectionUtils.forAllDo(empColl, salaryIncreaseClosure); printColl("After salary increase:", empColl); System.out.println(StringUtils.repeat("=", 40)); } public static void printColl(String label, Collection c) { if (StringUtils.isNotBlank(label)) { System.out.println(label); } Iterator iter = c.iterator(); while (iter.hasNext()) { System.out.println(iter.next()); } } } 以下是运行结果: =========== demoClosureUsage =========== Before salary increase: Employee[name=Tony,age=26,dateJoined=2005-08-05,grade=E4,salary=2000.0] Employee[name=Michelle,age=24,dateJoined=2005-08-05,grade=E4,salary=2000.0] Employee[name=Jack,age=28,dateJoined=2005-08-05,grade=E5,salary=3000.0] After salary increase: Employee[name=Tony,age=26,dateJoined=2005-08-05,grade=E4,salary=2400.0] Employee[name=Michelle,age=24,dateJoined=2005-08-05,grade=E4,salary=2400.0] Employee[name=Jack,age=28,dateJoined=2005-08-05,grade=E5,salary=3600.0] ======================================== 我这里举的是一个相对简单的例子,在 Closure 这一组还有一些很方便的类,如 ChainedClosure 可以包装一组 Closure 作为整体执行;IfClosure 在创建时需要提供给它一个Predicate和两个Closure,执行时先做Predicate判定再决定执行哪一个Closure;SwitchClosure跟SwitchTransformer 类似,根据创建时传入的 Predicate 组和Closure 组对应执行;WhileClosure 则根据创建时传入的 Predicate 做判断,如果为 true 则执行 Closure, 直到Predicate 返回false;等等。 具体用法请参考 Javadoc。 来看最后一组 – Iterator。 LoopingIterator ArrayListIterator FilterIterator UniqueFilterIterator IteratorUtils java.util.Iterator 接口定义了标准的 Collection 遍历方法,但是如果不做改变的使用它,我们得到的是从头到尾一次性的遍历。假如我们需要 循环遍历,假如我们需要遍历某一段,假如我们需要遍历满足某些条件的元素,等等等等,我们就不能完全依赖于这个 Iterator 的标准实现了。除 非我们宁可在此基础上在调用的代码中多加一些判断,不过这样的话代码就会显得混乱,时间长了就容易变得难以维护。 Commons Collections 的 这一组 Iterator 为我们带来了便利。 这些Iterator 使用都很一目了然,直接看例子吧: package sean.study.commons.collections; import java.util.Arrays; import java.util.Iterator; import java.util.List; import org.apache.commons.collections.Predicate; import org.apache.commons.collections.iterators.ArrayListIterator; import org.apache.commons.collections.iterators.FilterIterator; import org.apache.commons.collections.iterators.LoopingIterator; import org.apache.commons.lang.StringUtils; public class IteratorUsage { public static void main(String[] args) { demoIteratorUsage(); } public static void demoIteratorUsage() { System.out.println(StringUtils.center(" demoClosureUsage ", 40, "=")); // data setup String[] weekDays = { "Monday","Tuesday","Wednesday", "Thursday","Friday","Saturday","Sunday" }; List weekDayList = Arrays.asList(weekDays); // workdays Iterator iter1 = new ArrayListIterator(weekDays, 0, 5); printColl("Partial:", iter1, 5); // loop Iterator iter2 = new LoopingIterator(weekDayList); printColl("Loop:", iter2, 10); // looping workdays Predicate notWeekendPredicate = new Predicate() { public boolean evaluate(Object obj) { String str = (String) obj; if ("Saturday".equalsIgnoreCase(str)) { return false; } if ("Sunday".equalsIgnoreCase(str)) { return false; } return true; } }; Iterator iter3 = new FilterIterator( new LoopingIterator(weekDayList), notWeekendPredicate ); printColl("No Weekends loop:", iter3, 12); System.out.println(StringUtils.repeat("=", 40)); } public static void printColl(String label, Iterator iter, int maxCount) { if (StringUtils.isNotBlank(label)) { System.out.println(label); } int i = 0; while (iter.hasNext() && i < maxCount) { System.out.println("#" + iter.next() + "#"); i++; } } } 运行结果如下: =========== demoClosureUsage =========== Partial: # Monday # # Tuesday # # Wednesday # # Thursday # # Friday # Loop: # Monday # # Tuesday # # Wednesday # # Thursday # # Friday # # Saturday # # Sunday # # Monday # # Tuesday # # Wednesday # No Weekends loop: # Monday # # Tuesday # # Wednesday # # Thursday # # Friday # # Monday # # Tuesday # # Wednesday # # Thursday # # Friday # # Monday # # Tuesday # ======================================== 有了这些实用的 Iterator 类,我们就可以轻松的实现可配置的遍历行为了。
还剩45页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 20 金币 [ 分享pdf获得金币 ] 2 人已下载

下载pdf

pdf贡献者

hantong

贡献于2010-12-27

下载需要 20 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf