丁保国的博客

收集整理工作生活,点点滴滴

  :: :: 博问 :: 闪存 :: :: :: :: 管理 ::
[Jakarta Commons笔记] 开篇 st1":*{behavior:url(#ieooui) }

[Jakarta Commons笔记] 开篇

Java的世界,有很多(成千上万)开源的框架,有成功的,也有不那么成功的,有声名显赫的,也有默默无闻的。在我看来,成功而默默无闻的那些框架值得我们格外的尊敬和关注,Jakarta Commons就是这样的一个框架。如果你至少参与了一个中型规模的Java项目,那么我想有超过一大半的机会你都接触和使用到了Jakarta Commons,不管你自己有没有察觉。就我所知,除了Apache Jakarta其他许多开源框架之外,不少所谓的商业框架其实内部有些模块是借用Commons的,甚至有一些完全就是对Commons的类进行了简单的封装。如果真的没有接触过也不要紧,当你看到它时,你自然会被它的简单而强大所吸引。

 

要提高Java编程水平,一条可以走的路就是学习优秀的开源框架。这又要分两个层面:应用层面源码层面。从应用来说,开源的框架大都可以给你带来生产力和/或代码质量的大幅提升;从源码来说,Java开源框架,尤其是那些大型的优秀的框架,其源码对广大Java爱好者来说都是一笔巨大的财富,你可以从中学到许多课本上学不到的东西:编码习惯、代码组织、注释、文档、如何用Java解决实际问题、特定问题的算法,等等。而这些对于我们的作为软件开发者的实际工作而言,相当有意义。

 

熟悉Jakarta Commons的朋友可能会觉得现在是不是有点过时,因为有很多功能在J2SE 5.0中已经包含了。其实这个问题看你怎么去看,一方面,J2SE 5.0毕竟是刚出现不久的Java版本,实际应用中,很多时候我们需要把代码兼容等级维持在1.3或者1.4,所以很多5.0的功能我们暂时还不能放开手脚去使用;另一方面,鉴于Jakarta在一定程度上反映了一线Java开发人员的实际需求,而目前5.0已经采纳了其中许多特性,我们当然也有理由相信未来的Java版本还会继续参照Jakarta Commons的内容。有这么一套开发源码、免费使用、商业友好的优秀API作为Java自带API的补充,何乐而不为呢?

 

我打算在近期陆续做一些Jakarta Commons的学习笔记放上来,供大家参考。

 

有关Jakarta的最新动态和详细信息,可以参考:

http://jakarta.apache.org/commons/index.html

 

 

[Jakarta Commons笔记] Commons Lang

java.lang这个包的作用类似,Commons Lang这一组API也是提供一些基础的、通用的操作和处理,如自动生成toString()的结果、自动实现hashCode()equals()方法、数组操作、枚举、日期和时间的处理等等。目前这组API的版本是2.1,下载地址如下:

 

http://apache.justdn.org/jakarta/commons/lang/binaries/commons-lang-2.1.zip

http://apache.justdn.org/jakarta/commons/lang/source/commons-lang-2.1-src.zip

 

其中后一个是源代码。

 

这一组API的所有包名都以org.apache.commons.lang开头,共有如下8个包:

 

org.apache.commons.lang

org.apache.commons.lang.builder

org.apache.commons.lang.enum

org.apache.commons.lang.enums

org.apache.commons.lang.exception

org.apache.commons.lang.math

org.apache.commons.lang.mutable

org.apache.commons.lang.time

 

其中的lang.enum已不建议使用,替代它的是紧随其后的lang.enums包。 lang包主要是一些可以高度重用的Util类;lang.builder包包含了一组用于产生每个Java类中都常使用到的toString()hashCode()equals()compareTo()等等方法的构造器;lang.enums包顾名思义用于处理枚举;lang.exception包用于处理Java标准API中的exception,为1.4之前版本提供Nested Exception功能;lang.math包用于处理数字;lang.mutable用于包装值型变量;lang.time包提供处理日期和时间的功能。

 

由于Commons的包和类实在很多,不可能一个一个讲了,在接下来的专题文章中我就只分别过一下langlang.builderlang.mathlang.time这几个包和常见的用法,其他的我们可以在用到时临时参考一下Javadoc。位置就在安装路径的

…"commons-lang-2.1"docs"api"index.html

 

 

[Jakarta Commons笔记] org.apache.commons.lang

我们首先来看org.apache.commons.lang包,这个包提供了一些有用的包含static方法的Util类。除了6Exception类和2个已经deprecated的数字类之外,commons.lang包共包含了17个实用的类:

 

ArrayUtils – 用于对数组的操作,如添加、查找、删除、子数组、倒序、元素类型转换等;

BitField – 用于操作位元,提供了一些方便而安全的方法;

BooleanUtils – 用于操作和转换boolean或者Boolean及相应的数组;

CharEncoding – 包含了Java环境支持的字符编码,提供是否支持某种编码的判断;

CharRange – 用于设定字符范围并做相应检查;

CharSet – 用于设定一组字符作为范围并做相应检查;

CharSetUtils – 用于操作CharSet

CharUtils – 用于操作char值和Character对象;

ClassUtils – 用于对Java类的操作,不使用反射;

ObjectUtils – 用于操作Java对象,提供null安全的访问和其他一些功能;

RandomStringUtils – 用于生成随机的字符串;

SerializationUtils – 用于处理对象序列化,提供比一般Java序列化更高级的处理能力;

StringEscapeUtils – 用于正确处理转义字符,产生正确的JavaJavaScriptHTMLXMLSQL代码;

StringUtils – 处理String的核心类,提供了相当多的功能;

SystemUtils – java.lang.System基础上提供更方便的访问,如用户路径、Java版本、时区、操作系统等判断;

Validate – 提供验证的操作,有点类似assert断言;

WordUtils – 用于处理单词大小写、换行等。

 

接下来我准备用两个例子来分别说明ArrayUtilsStringUtils的常见用法。

[Jakarta Commons笔记] 代码范例 - ArrayUtils

数组是我们经常需要使用到的一种数据结构,但是由于Java本身并没有提供很好的API支持,使得很多操作实际上做起来相当繁琐,以至于我们实际编码中甚至会不惜牺牲性能去使用Collections API,用Collection当然能够很方便的解决我们的问题,但是我们一定要以性能为代价吗?ArrayUtils帮我们解决了处理类似情况的大部分问题。来看一个例子:

 

package sean.study.jakarta.commons.lang;

 

import java.util.Map;

import org.apache.commons.lang.ArrayUtils;

public class ArrayUtilsUsage {

    public static void main(String[] args) {

        // data setup

        int[] intArray1 = { 2, 4, 8, 16 };

        int[][] intArray2 = { { 1, 2 }, { 2, 4 }, { 3, 8 }, { 4, 16 } };

        Object[][] notAMap = {

                { "A", new Double(100) },

                { "B", new Double(80) },

                { "C", new Double(60) },

                { "D", new Double(40) },

                { "E", new Double(20) }

        };

        // printing arrays

        System.out.println("intArray1: " + ArrayUtils.toString(intArray1));

        System.out.println("intArray2: " + ArrayUtils.toString(intArray2));

        System.out.println("notAMap: " + ArrayUtils.toString(notAMap));

 

        // finding items

        System.out.println("intArray1 contains '8'? "

                + ArrayUtils.contains(intArray1, 8));

        System.out.println("intArray1 index of '8'? "

                + ArrayUtils.indexOf(intArray1, 8));

        System.out.println("intArray1 last index of '8'? "

                + ArrayUtils.lastIndexOf(intArray1, 8));

 

        // cloning and resversing

        int[] intArray3 = ArrayUtils.clone(intArray1);

        System.out.println("intArray3: " + ArrayUtils.toString(intArray3));

        ArrayUtils.reverse(intArray3);

        System.out.println("intArray3 reversed: "

                + ArrayUtils.toString(intArray3));

 

        // primitive to Object array

        Integer[] integerArray1 = ArrayUtils.toObject(intArray1);

        System.out.println("integerArray1: "

                + ArrayUtils.toString(integerArray1));

 

        // build Map from two dimensional array

        Map map = ArrayUtils.toMap(notAMap);

        Double res = (Double) map.get("C");

        System.out.println("get 'C' from map: " + res);

 

    }

 

}

 

以下是运行结果:

 

intArray1: {2,4,8,16}

intArray2: {{1,2},{2,4},{3,8},{4,16}}

notAMap: {{A,100.0},{B,80.0},{C,60.0},{D,40.0},{E,20.0}}

intArray1 contains '8'? true

intArray1 index of '8'? 2

intArray1 last index of '8'? 2

intArray3: {2,4,8,16}

intArray3 reversed: {16,8,4,2}

integerArray1: {2,4,8,16}

get 'C' from map: 60.0

 

这段代码说明了我们可以如何方便的利用ArrayUtils类帮我们完成数组的打印、查找、克隆、倒序、以及值型/对象数组之间的转换等操作。如果想了解更多,请参考Javadoc

[Jakarta Commons笔记] 代码范例 - StringUtils

处理文本对Java应用来说应该算是家常便饭了,在1.4出现之前,Java自身提供的API非常有限,如StringStringTokenizerStringBuffer,操作也比较单一。无非就是查找substring、分解、合并等等。到1.4的出现可以说Java的文字处理上了一个台阶,因为它支持regular expression了。这可是个重量级而方便的东东啊,缺点是太复杂,学习起来有一定难度。相较而言,Jakarta Commons提供的StringUtilsWordUtils至今还维持着那种简洁而强大的美,使用起来也很顺手。来看一个例子:

 

package sean.study.jakarta.commons.lang;

 

import org.apache.commons.lang.StringUtils;

 

public class StringUtilsAndWordUtilsUsage {

 

    public static void main(String[] args) {

       

        // data setup

        String str1 = "";

        String str2 = " ";

        String str3 = ""t";

        String str4 = null;

        String str5 = "123";

        String str6 = "ABCDEFG";

        String str7 = "It feels good to use Jakarta Commons."r"n";

       

        // check for empty strings

        System.out.println("==============================");

        System.out.println("Is str1 blank? " + StringUtils.isBlank(str1));

        System.out.println("Is str2 blank? " + StringUtils.isBlank(str2));

        System.out.println("Is str3 blank? " + StringUtils.isBlank(str3));

        System.out.println("Is str4 blank? " + StringUtils.isBlank(str4));

       

        // check for numerics

        System.out.println("==============================");

        System.out.println("Is str5 numeric? " + StringUtils.isNumeric(str5));

        System.out.println("Is str6 numeric? " + StringUtils.isNumeric(str6));

       

        // reverse strings / whole words

        System.out.println("==============================");

        System.out.println("str6: " + str6);

        System.out.println("str6 reversed: " + StringUtils.reverse(str6));

        System.out.println("str7: " + str7);

        String str8 = StringUtils.chomp(str7);

        str8 = StringUtils.reverseDelimited(str8, ' ');

        System.out.println("str7 reversed whole words : "r"n" + str8);

       

        // build header (useful to print log messages that are easy to locate)

        System.out.println("==============================");

        System.out.println("print header:");

        String padding = StringUtils.repeat("=", 50);

        String msg = StringUtils.center(" Customised Header ", 50, "%");

        Object[] raw = new Object[]{padding, msg, padding};

        String header = StringUtils.join(raw, ""r"n");

        System.out.println(header);

 

    }

}

 

输出的结果如下:

 

==============================

Is str1 blank? true

Is str2 blank? true

Is str3 blank? true

Is str4 blank? true

==============================

Is str5 numeric? true

Is str6 numeric? false

==============================

str6: ABCDEFG

str6 reversed: GFEDCBA

str7: It feels good to use Jakarta Commons.

 

str7 reversed whole words :

Commons. Jakarta use to good feels It

==============================

print header:

==================================================

%%%%%%%%%%%%%%% Customised Header %%%%%%%%%%%%%%%%

==================================================

 

从代码中我们可以大致了解到这个StringUtils类简单而强大的处理能力,从检查空串(对null的情况处理很得体),到分割子串,到生成格式化的字符串,使用都很简洁,也很直截了当。

[Jakarta Commons笔记] org.apache.commons.lang.builder

在前面的专题文章中,我们一起过了一遍org.apache.commons.lang包,接下来我们继续看org.apache.commons.lang.builder这个包。在这里面我们可以找到7个类,用于帮助我们实现Java对象的一些基础的共有方法。这7个类分别是:

 

CompareToBuilder – 用于辅助实现Comparable.compareTo(Object)方法;

EqualsBuilder – 用于辅助实现Object.equals()方法;

HashCodeBuilder – 用于辅助实现Object.hashCode()方法;

ToStringBuilder – 用于辅助实现Object.toString()方法;

ReflectionToStringBuilder – 使用反射机制辅助实现Object.toString()方法;

ToStringStyle – 辅助ToStringBuilder控制输出格式;

StandardToStringStyle – 辅助ToStringBuilder控制标准格式。

 

我们知道,在实际应用中,其实经常需要在运行过程中判定对象的知否相等、比较、取hash、和获取对象基本信息(一般是产生log日志)。然而实现这些compareToequalshashCodetoString其实并非那么直截了当,甚至稍有不注意就可能造成难以追踪的bug,而且这些方法手工维护的话,比较繁琐,也容易出错。于是Commons Langbuilder这个包中提供了上述辅助类,为我们简化这些方法的实现和维护。

 

来看一个例子:

 

package sean.study.jakarta.commons.lang;

 

import java.util.Date;

 

import org.apache.commons.lang.builder.CompareToBuilder;

import org.apache.commons.lang.builder.EqualsBuilder;

import org.apache.commons.lang.builder.HashCodeBuilder;

import org.apache.commons.lang.builder.ToStringBuilder;

import org.apache.commons.lang.builder.ToStringStyle;

 

public class BuilderUsage {

 

    public static void main(String[] args) {

       

        Staff staff1 = new Staff(123, "John Smith", new Date());

        Staff staff2 = new Staff(456, "Jane Smith", new Date());

       

        System.out.println("staff1's info: " + staff1);

        System.out.println("staff2's info: " + staff2);

        System.out.println("staff1's hash code: " + staff1.hashCode());

        System.out.println("staff2's hash code: " + staff2.hashCode());

        System.out.println("staff1 equals staff2? " + staff1.equals(staff2));

 

    }

 

}

 

class Staff implements Comparable {

 

    private long staffId;

    private String staffName;

    private Date dateJoined;

 

    public Staff() {

    }

 

    public Staff(long staffId, String staffName, Date dateJoined) {

        this.staffId = staffId;

        this.staffName = staffName;

        this.dateJoined = dateJoined;

    }

 

    public int compareTo(Object o) {

        int res = -1;

        if (o != null && Staff.class.isAssignableFrom(o.getClass())) {

            Staff s = (Staff) o;

            res = new CompareToBuilder()

                    .append(dateJoined, s.getDateJoined())

                    .append(staffName, s.getStaffName()).toComparison();

        }

        return res;

    }

 

    public boolean equals(Object o) {

        boolean res = false;

        if (o != null && Staff.class.isAssignableFrom(o.getClass())) {

            Staff s = (Staff) o;

            res = new EqualsBuilder()

                    .append(staffId, s.getStaffId())

                    .isEquals();

        }

        return res;

    }

 

    public int hashCode() {

        return new HashCodeBuilder(11, 23).append(staffId).toHashCode();

    }

 

    public String toString() {

        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)

                .append("staffId", staffId)

                .append("staffName", staffName)

                .append("dateJoined", dateJoined)

                .toString();

    }

 

    public Date getDateJoined() {

        return dateJoined;

    }

 

    public void setDateJoined(Date dateJoined) {

        this.dateJoined = dateJoined;

    }

 

    public long getStaffId() {

        return staffId;

    }

 

    public void setStaffId(long staffId) {

        this.staffId = staffId;

    }

 

    public String getStaffName() {

        return staffName;

    }

 

    public void setStaffName(String staffName) {

        this.staffName = staffName;

    }

}

 

以下是运行结果:

 

staff1's info: sean.study.jakarta.commons.lang.Staff@190d11[

  staffId=123

  staffName=John Smith

  dateJoined=Sat Jul 30 13:18:45 CST 2005

]

staff2's info: sean.study.jakarta.commons.lang.Staff@1fb8ee3[

  staffId=456

  staffName=Jane Smith

  dateJoined=Sat Jul 30 13:18:45 CST 2005

]

staff1's hash code: 376

staff2's hash code: 709

staff1 equals staff2? false

 

这些builder使用起来都很简单,new一个实例,append需要参与的信息,最后加上toComparisonisEqualstoHashCodetoString这样的结尾即可。相应的,如果你不需要这样级别的控制,也可以使用利用反射机制的版本自动化实现需要的功能,如:

 

    public int compareTo(Object o) {

        return CompareToBuilder.reflectionCompare(this, o);

    }

 

    public boolean equals(Object o) {

        return EqualsBuilder.reflectionEquals(this, o);

    }

 

    public int hashCode() {

        return HashCodeBuilder.reflectionHashCode(this);

    }

 

    public String toString() {

        return ReflectionToStringBuilder.toString(this);

    }

 

尤其当我们在项目中不希望过多的参与到对这些对象方法的维护时,采用Commons提供的利用反射的这些API就成了方便而相对安全的一个方案。

[Jakarta Commons笔记] org.apache.commons.lang.math

Jakarta Commons中,专门处理数学计算的类分别可以在两个地方找到:一是Commons Langorg.apache.commons.lang.math包中,二是在Commons Math这个单独的子项目中。由于后者主要是处理复数、矩阵等,相对使用比较少,在我的笔记中就只简单讲讲Commons Lang中的math包。对后者感兴趣的可以看看

http://jakarta.apache.org/commons/math/

 

org.apache.commons.lang.math包中共有10个类,这些类可以归纳成四组:

1- 处理分数的Fraction类;

2- 处理数值的NumberUtils类;

3- 处理数值范围的RangeNumberRangeIntRangeLongRangeFloatRangeDoubleRange类;

4- 处理随机数的JVMRandomRandomUtils类。

 

下面我举个例子分别说明上述四组的使用方法:

 

package sean.study.jakarta.commons.lang;

 

import org.apache.commons.lang.ArrayUtils;

import org.apache.commons.lang.BooleanUtils;

import org.apache.commons.lang.StringUtils;

import org.apache.commons.lang.math.DoubleRange;

import org.apache.commons.lang.math.Fraction;

import org.apache.commons.lang.math.NumberUtils;

import org.apache.commons.lang.math.RandomUtils;

import org.apache.commons.lang.math.Range;

 

public class LangMathUsage {

 

    public static void main(String[] args) {

        demoFraction();

        demoNumberUtils();

        demoNumberRange();

        demoRandomUtils();

    }

 

    public static void demoFraction() {

        System.out.println(StringUtils.center(" demoFraction ", 30, "="));

        Fraction myFraction = Fraction.getFraction(144, 90);

        // Fraction myFraction = Fraction.getFraction("1 54/90");

        System.out.println("144/90 as fraction: " + myFraction);

        System.out.println("144/90 to proper: " + myFraction.toProperString());

        System.out.println("144/90 as double: " + myFraction.doubleValue());

        System.out.println("144/90 reduced: " + myFraction.reduce());

        System.out.println("144/90 reduced proper: "

                + myFraction.reduce().toProperString());

        System.out.println();

    }

 

    public static void demoNumberUtils() {

        System.out.println(StringUtils.center(" demoNumberUtils ", 30, "="));

        System.out.println("Is 0x3F a number? "

                + StringUtils.capitalize(BooleanUtils.toStringYesNo(NumberUtils

                        .isNumber("0x3F"))) + ".");

        double[] array = { 1.0, 3.4, 0.8, 7.1, 4.6 };

        double max = NumberUtils.max(array);

        double min = NumberUtils.min(array);

        String arrayStr = ArrayUtils.toString(array);

        System.out.println("Max of " + arrayStr + " is: " + max);

        System.out.println("Min of " + arrayStr + " is: " + min);

        System.out.println();

    }

 

    public static void demoNumberRange() {

        System.out.println(StringUtils.center(" demoNumberRange ", 30, "="));

        Range normalScoreRange = new DoubleRange(90, 120);

        double score1 = 102.5;

        double score2 = 79.9;

        System.out.println("Normal score range is: " + normalScoreRange);

        System.out.println("Is "

                + score1

                + " a normal score? "

                + StringUtils

                        .capitalize(BooleanUtils.toStringYesNo(normalScoreRange

                                .containsDouble(score1))) + ".");

        System.out.println("Is "

                + score2

                + " a normal score? "

                + StringUtils

                        .capitalize(BooleanUtils.toStringYesNo(normalScoreRange

                                .containsDouble(score2))) + ".");

        System.out.println();

    }

 

    public static void demoRandomUtils() {

        System.out.println(StringUtils.center(" demoRandomUtils ", 30, "="));

        for (int i = 0; i < 5; i++) {

            System.out.println(RandomUtils.nextInt(100));

        }

        System.out.println();

    }

 

}

 

以下是运行结果:

 

======== demoFraction ========

144/90 as fraction: 144/90

144/90 to proper: 1 54/90

144/90 as double: 1.6

144/90 reduced: 8/5

144/90 reduced proper: 1 3/5

 

====== demoNumberUtils =======

Is 0x3F a number? Yes.

Max of {1.0,3.4,0.8,7.1,4.6} is: 7.1

Min of {1.0,3.4,0.8,7.1,4.6} is: 0.8

 

====== demoNumberRange =======

Normal score range is: Range[90.0,120.0]

Is 102.5 a normal score? Yes.

Is 79.9 a normal score? No.

 

====== demoRandomUtils =======

75

63

40

21

92

 

以上就是Commons Langmath包通常的用法。提一下NumberUtils.inNumber(String)方法,它会正确判断出给定的字符串是否是合法的数值,而我在前面的笔记中提到的StringUtils.isNumeric(String)只能判断一个字符串是否是由纯数字字符组成。Commons Langmath包的各个类都还有很多实用的方法,远不止我在例子中用到的这些,如果你感兴趣,参照一下Commons LangJavadoc吧。

[Jakarta Commons笔记] org.apache.commons.lang.time

好了,来看我在Common Lang中最后要讲的一个包:org.apache.commons.lang.time。这个包里面包含了如下5个类:

 

DateFormatUtils – 提供格式化日期和时间的功能及相关常量;

DateUtils – CalendarDate的基础上提供更方便的访问;

DurationFormatUtils – 提供格式化时间跨度的功能及相关常量;

FastDateFormat – java.text.SimpleDateFormat提供一个的线程安全的替代类;

StopWatch – 是一个方便的计时器。

 

我们还是在一个例子中来看上述各个类的用法吧:

 

package sean.study.jakarta.commons.lang;

 

import java.util.Calendar;

import java.util.Date;

 

import org.apache.commons.lang.StringUtils;

import org.apache.commons.lang.time.DateFormatUtils;

import org.apache.commons.lang.time.DateUtils;

import org.apache.commons.lang.time.FastDateFormat;

import org.apache.commons.lang.time.StopWatch;

 

public class DateTimeUsage {

 

    public static void main(String[] args) {

        demoDateUtils();

        demoStopWatch();

    }

   

    public static void demoDateUtils() {

        System.out.println(StringUtils.center(" demoDateUtils ", 30, "="));

        Date date = new Date();

        String isoDateTime = DateFormatUtils.ISO_DATETIME_FORMAT.format(date);

        String isoTime = DateFormatUtils.ISO_TIME_NO_T_FORMAT.format(date);

        FastDateFormat fdf = FastDateFormat.getInstance("yyyy-MM");

        String customDateTime = fdf.format(date);

        System.out.println("ISO_DATETIME_FORMAT: " + isoDateTime);

        System.out.println("ISO_TIME_NO_T_FORMAT: " + isoTime);

        System.out.println("Custom FastDateFormat: " + customDateTime);

        System.out.println("Default format: " + date);

        System.out.println("Round HOUR: " + DateUtils.round(date, Calendar.HOUR));

        System.out.println("Truncate HOUR: " + DateUtils.truncate(date, Calendar.HOUR));

        System.out.println();

    }

   

    public static void demoStopWatch() {

        System.out.println(StringUtils.center(" demoStopWatch ", 30, "="));

        StopWatch sw = new StopWatch();

        sw.start();

        operationA();

        sw.stop();

        System.out.println("operationA used " + sw.getTime() + " milliseconds.");

        System.out.println();

    }

   

    public static void operationA() {

        try {

            Thread.sleep(999);

        }

        catch (InterruptedException e) {

            // do nothing

        }

    }

 

}

 

以下是运行结果:

 

======= demoDateUtils ========

ISO_DATETIME_FORMAT: 2005-08-01T12:41:51

ISO_TIME_NO_T_FORMAT: 12:41:51

Custom FastDateFormat: 2005-08

Default format: Mon Aug 01 12:41:51 CST 2005

Round HOUR: Mon Aug 01 13:00:00 CST 2005

Truncate HOUR: Mon Aug 01 12:00:00 CST 2005

 

======= demoStopWatch ========

operationA used 1000 milliseconds.

 

具体的调用细节和完整的API请参阅Commons LangJavadoc

[Jakarta Commons笔记] Commons BeanUtils

Jakarta Commons项目提供了相当丰富的API,我们之前了解到的Commons Lang只是众多API的比较核心的一小部分而已。Commons下面还有相当数量的子项目,用于解决各种各样不同方向的实际问题,BeanUtils就是其中的一个,用于处理JavaBeans。它利用Java的反射机制,从动态的生成对beangettersetter的调用代码,到模拟创建一个动态的bean,等等。这个包看似简单,却是很多开源项目的基石:如在著名的StrutsSpring Framework中,我们都能找到BeanUtils的影子。大家猜猜看,有哪位名人是BeanUtils的作者之一?没错,就是Struts的创始人Craig McClanahan

 

BeanUtils最核心的好处在于:我们在编码时,并不需要知道我们处理的JavaBeans具体是什么类型,有哪些属性,这些信息是可以动态获取的,甚至我们都可以不必去关心事实上是否存在这样一个具体的JavaBean类。我们只需要知道有一个JavaBean的实例,我们需要从中取得某个属性,设定某个属性的值,或者仅仅是需要一个属性表。要做到这些,依靠Sun提供的JavaBean规范似乎找不到一个很直接的方式,除非硬编码,将getXxxx()setXxxx()直接写进我们的程序。但是这样就大大增加了代码的复杂度、耦合性和维护成本。还好Commons BeanUtils对这个问题提供了一种优雅的解决方案。

 

我们有两种途径获取Commons BeanUtilsbinary

1- StrutsSpring或者任何依赖BeanUtils的开源产品的发行包中找到相应的jar文件;

2- http://www.apache.org/dist/jakarta/commons/beanutils/binaries/下载。

 

Commons BeanUtils的源码下载地址:

http://www.apache.org/dist/jakarta/commons/beanutils/source/

 

Commons BeanUtils一共包括如下5个包:

 

org.apache.commons.beanutils – 核心包,定义一组Utils类和需要用到的接口规范

org.apache.commons.beanutils.converters – 转换String到需要类型的类,实现Converter接口

org.apache.commons.beanutils.locale – beanutilslocale敏感版本

org.apache.commons.beanutils.locale.converters – converterslocale敏感版本

org.apache.commons.collections – beanutils使用到的Collection

 

其中需要我们特别关注的是这个org.apache.commons.beanutils包,其他包都是起辅助作用的。接下来我们就仔细看一看这个包都有些什么东东:

 

[4个接口]

 

Converter

该接口定义了如下方法:

public java.lang.Object convert(java.lang.Class type, java.lang.Object value);

只要实现了这个Converter接口并注册到ConvertUtils类即可被我们的BeanUtils包所使用,它的主要目的是提供将给定的Object实例转换为目标类型的算法。我们可以在beanutils.converters包中找到相当多的已经实现的转换器。

 

DynaBean

该接口定义的是一个动态的JavaBean,它的属性类型、名称和值都是可以动态改变的。

 

DynaClass

该接口定义的是针对实现了DynaBean接口的类的java.lang.Class对象,提供如getName()newInstance()等方法。

 

MutableDynaClass

该接口是对DynaClass的扩展,使得动态bean的属性可以动态增加或删除。

 

[24个类]

 

BasicDynaBean

DynaBean接口的最精简实现

 

BasicDynaClass

DynaClass接口的最精简实现

 

BeanUtils

提供通过反射机制填写JavaBeans属性的工具/静态方法

 

BeanUtilsBean

BeanUtils类的实例化实现,区别于BeanUtils的静态方法方式,使得自定义的配置得以保持

 

ConstructorUtils

MethodUtils类似,不过专注于构造方法

 

ContextClassLoaderLocal

针对每个classloader的唯一标识

 

ConvertingWrapDynaBean

包含了标准JavaBean实例的DynaBean实现,使得我们可以使用DynaBeanAPI来访问起属性,同时提供设定属性时的类型转换,继承自并区别于WrapDynaBean

 

ConvertUtils

提供工具/静态方法,用于将String对象及其数组转换为指定的类型的对象及其数组。

 

ConvertUtilsBean

ConvertUtils类的实例化实现,区别于ConvertUtils的静态方法方式,使得自定义的配置得以保持

 

DynaProperty

用于描述DynaBean的属性

 

JDBCDynaClass

DynaClassJDBC实现提供公用的逻辑

 

LazyDynaBean

懒载入DynaBean,自动往DynaClass添加属性并提供懒载入List和懒载入Map的功能

 

LazyDynaClass

实现MutableDynaClass接口的类

 

LazyDynaMap

Map实例提供一个轻量级的DynaBean包装

 

MappedPropertyDescriptor

用于描述映射的属性

 

MethodUtils

包含了针对一般意义上的方法而非特定属性的反射工具/静态方法

 

MethodUtils.MethodDescriptor

描述通过反射查找某个方法所使用的键值

 

PropertyUtils

提供利用Java反射API调用具体对象的gettersetter的工具/静态方法

 

PropertyUtilsBean

PropertyUtils类的实例化实现,区别于PropertyUtils的静态方法方式,使得自定义的配置得以保持

 

ResultSetDynaClass

包装java.sql.ResultSet中的java.sql.Row实例的DynaBean所对应的DynaClass实现

 

ResultSetIterator

针对ResultSetDynaClassjava.util.Iterator实现

 

RowSetDynaClass

DynaClass的一种实现,用于在内存中创建一组表示SQL查询结果的DynaBeans,区别于ResultSetDynaClass,它不需要保持ResultSet打开

 

WrapDynaBean

DynaBean的一种实现,包含一个标准的JavaBean实例,以便我们可以使用DynaBeanAPI去访问它的属性,区别于ConvertingWrapDynaBean,它不做专门的类型转换

 

WrapDynaClass

DynaClass的一种实现,针对那些包装标准JavaBean实例的DynaBeans

 

[3Exception]

 

(略)

 

看到这么多东西是不是有点头晕?不要慌,看几个例子就明白了。只要把握好BeanUtils本身要完成的事,就不难理解这些类存在的道理。我们不妨把BeanUtils的基础应用分解成:访问JavaBean的属性、设定JavaBean的属性、以及创建和使用DynaBeans。这样来看BeanUtils,你就会觉得简单清晰得多。

 

// 例子将在下一节单独放出。

[Jakarta Commons笔记] 代码范例 - BeanUtils

假定我们有如下两个标准的JavaBean

 

/** Address.java */

 

package sean.study.commons.beanutils;

 

public class Address {

 

    private String zipCode;

    private String addr;

    private String city;

    private String country;

 

    public Address() {

    }

 

    public Address(String zipCode, String addr, String city, String country) {

        this.zipCode = zipCode;

        this.addr = addr;

        this.city = city;

        this.country = country;

    }

 

    public String getAddr() {

        return addr;

    }

 

    public void setAddr(String addr) {

        this.addr = addr;

    }

 

    public String getCity() {

        return city;

    }

 

    public void setCity(String city) {

        this.city = city;

    }

 

    public String getCountry() {

        return country;

    }

 

    public void setCountry(String country) {

        this.country = country;

    }

 

    public String getZipCode() {

        return zipCode;

    }

 

    public void setZipCode(String zipCode) {

        this.zipCode = zipCode;

    }

 

}

 

/** Customer.java */

 

package sean.study.commons.beanutils;

 

public class Customer {

 

    private long id;

    private String name;

    private Address[] addresses;

 

    public Customer() {

    }

 

    public Customer(long id, String name, Address[] addresses) {

        this.id = id;

        this.name = name;

        this.addresses = addresses;

    }

 

    public Address[] getAddresses() {

        return addresses;

    }

 

    public void setAddresses(Address[] addresses) {

        this.addresses = addresses;

    }

 

    public long getId() {

        return id;

    }

 

    public void setId(long id) {

        this.id = id;

    }

 

    public String getName() {

        return name;

    }

 

    public void setName(String name) {

        this.name = name;

    }

 

}

 

 

我们来看看通常我们是怎样利用Commons BeanUtils来完成一些基本的JavaBeanDynaBean操作:

 

package sean.study.commons.beanutils;

 

import org.apache.commons.beanutils.BasicDynaBean;

import org.apache.commons.beanutils.BasicDynaClass;

import org.apache.commons.beanutils.DynaBean;

import org.apache.commons.beanutils.DynaProperty;

import org.apache.commons.beanutils.PropertyUtils;

import org.apache.commons.lang.StringUtils;

 

public class BeanUtilsUsage {

 

    public static void main(String[] args) throws Exception {

        demoNormalJavaBeans();

        demoDynaBeans();

    }

 

    public static void demoNormalJavaBeans() throws Exception {

 

        System.out.println(StringUtils.center(" demoNormalJavaBeans ", 40, "="));

       

        // data setup

        Address addr1 = new Address("CA1234", "xxx", "Los Angeles", "USA");

        Address addr2 = new Address("100000", "xxx", "Beijing", "China");

        Address[] addrs = new Address[2];

        addrs[0] = addr1;

        addrs[1] = addr2;

        Customer cust = new Customer(123, "John Smith", addrs);

       

        // accessing the city of first address

        String cityPattern = "addresses[0].city";

        String name = (String) PropertyUtils.getSimpleProperty(cust, "name");

        String city = (String) PropertyUtils.getProperty(cust, cityPattern);

        Object[] rawOutput1 = new Object[] { "The city of customer ", name,

                "'s first address is ", city, "." };

        System.out.println(StringUtils.join(rawOutput1));

       

        // setting the zipcode of customer's second address

        String zipPattern = "addresses[1].zipCode";

        if (PropertyUtils.isWriteable(cust, zipPattern)) {

            System.out.println("Setting zipcode ...");

            PropertyUtils.setProperty(cust, zipPattern, "200000");

        }

        String zip = (String) PropertyUtils.getProperty(cust, zipPattern);

        Object[] rawOutput2 = new Object[] { "The zipcode of customer ", name,

                "'s second address is now ", zip, "." };

        System.out.println(StringUtils.join(rawOutput2));

       

        System.out.println();

    }

 

    public static void demoDynaBeans() throws Exception {

 

        System.out.println(StringUtils.center(" demoDynaBeans ", 40, "="));

       

        // creating a DynaBean

        DynaProperty[] dynaBeanProperties = new DynaProperty[] {

                new DynaProperty("name", String.class),

                new DynaProperty("inPrice", Double.class), 

                new DynaProperty("outPrice", Double.class),

        };

        BasicDynaClass cargoClass = new BasicDynaClass("Cargo", BasicDynaBean.class, dynaBeanProperties);

        DynaBean cargo = cargoClass.newInstance();

       

        // accessing a DynaBean

        cargo.set("name", "Instant Noodles");

        cargo.set("inPrice", new Double(21.3));

        cargo.set("outPrice", new Double(23.8));

        System.out.println("name: " + cargo.get("name"));

        System.out.println("inPrice: " + cargo.get("inPrice"));

        System.out.println("outPrice: " + cargo.get("outPrice"));

 

        System.out.println();

    }

 

}

 

上述代码运行结果如下:

 

========= demoNormalJavaBeans ==========

The city of customer John Smith's first address is Los Angeles.

Setting zipcode ...

The zipcode of customer John Smith's second address is now 200000.

 

============ demoDynaBeans =============

name: Instant Noodles

inPrice: 21.3

outPrice: 23.8

 

以上代码简单说明了一下BeanUtils常见的基本用法,还有很多高阶或者更具体的应用及原理,这里无法一一讲到,而且有很多笔者也不熟悉、不了解,对BeanUtils的讲解就到此吧。如果你有兴趣,或者还不是很清楚为什么像这样动态的或者说松散的访问JavaBean是有必要的,可以把Struts的源码拿下来研究一下,看看FormBean以及DynaActionForm这些是如何被动态创建的,一定会有收获。

[Jakarta Commons笔记] Commons Collections

Commons Collections,又是一个重量级的东西,为Java标准的Collections API提供了相当好的补充。我不知道其他人,就我自己而言,让我用java.util.Collection及其子类,加上java.util.Collections类 提供的操作方法,处理一些简单的数据结构问题还可以,稍微复杂一点的就觉得有点头痛,很多细节的地方需要我插入这样那样的小逻辑,或者感觉它太死板,不够灵活,再或者确实有点晦涩吧。再说了,如果我只是处理一般的数据结构问题,为什么不自己用数组或者自定义的链表来做,再加上Jakarta CommonsLang提供的ArrayUtilsStringUtils等,已经基本够了,性能可以保证,那么还要这个Collections API干嘛。当然,说到这里有些偏激了,Collections当然有它存在的道理,能够把常用的数据结构归纳起来,以通用的方式去维护和访问,这应该说是一种进步,但是用起来似乎不够友好。这个时候我就会想,如果Java比现在做得更好用些,或者有一套第三方的API把我的这些需求抽象出来,实现了,该多好。Commons Collections就是这样一套API

 

在这里可以找到下载链接:(binarysrc都有)

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的朋友大概或多或少会同意我如下的划分:在JavaCollections API中,不狭义的区分语法上的接口和类,把它们都看作是类的话,大致我们可以发现三种主要的类别:

 

1- 容器类:如CollectionListMap等,用于存放对象和进行简单操作的;

2- 操作类:如CollectionsArrays等,用于对容器类的实例进行相对复杂操作如排序等;

3- 辅助类:如IteratorComparator等,用于辅助操作类以及外部调用代码实现对容器类的操作,所谓辅助,概括而通俗的来讲,就是这些类提供一种算法,你给它一个对象或者一组对象,或者仅仅是按一定的规则调用它,它给你一个运算后的答案,帮助你正确处理容器对象。比如Iterator会告诉你容器中下一个对象有没有、是什么,而Comparator将对象大小/先后次序的算法逻辑独立出来。

 

同样,Jakarta Commons Collections我们细细看来,也能够找出类似的划分:

 

1- 作为容器类的补充,我们可以找到BagBufferBidiMapOrderedMap等等;

2- 作为操作类的补充,我们可以找到CollectionUtilsIteratorUtilsListUtilsSetUtils等等;

3- 作为辅助类的补充,我们可以找到MapIteratorClosurePredicateTransformer等等;

 

对于这样的一个大包,当然不可能一个类一个类的讲了,找一些常用的和有用的当做接下来讨论的话题吧。大概列个清单:

 

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组,在接下来的笔记中我们一起慢慢的看。

[Jakarta Commons笔记] Commons Collections - Bag

首先来看Bag组。

 

Bag

HashBag

BagUtils

 

Bag是在org.apache.commons.collections包中定义的接口,它extends java.util.Collection,而它的实现类都被放在下面的bag包中。之所以有这样一组类型,是因为我们有时候需要在Collection中存放多个相同对象的拷贝,并且需要很方便的取得该对象拷贝的个数。需要注意的一点是它虽然extends Collection,但是如果真把它完全当作java.util.Collection来用会遇到语义上的问题,详细信息参考Javadoc

 

HashBagBag接口的一个标准实现。而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,实际应用不建议像这样硬编码。

[Jakarta Commons笔记] Commons Collections - Buffer

来看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

]

========================================

 

我们可以看到,Bufferaddremove方法分别添加新成员和删除最先加入的成员。由于我们的Buffer定义为只能装3Book类的实例,所以不论我们试图加入其他类型的对象,或者加入超过3个,操作都将失败。如果我们在遍历时使用get()而不调用remove(),那么我们将得到3个相同的拷贝,而这正是我们期望的FIFO队列的行为。假如你需要遍历并保留数据,可以使用标准的Iterator机制。

[Jakarta Commons笔记] Commons Collections - Map

接下来看Map组。

 

BidiMap

MultiMap

LazyMap

MapUtils

 

Commons Collectionsjava.util.Map的基础上扩展了很多接口和类,比较有代表性的是BidiMapMultiMapLazyMap。跟BagBuffer类似,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通过它向开发人员开放了一个可以方便控制对象创建细节的接口。

[Jakarta Commons笔记] Commons Collections - Collection

接下来看看Collection组。

 

TypedCollection

CollectionUtils

 

首先就是这个TypedCollection,它实际上的作用就是提供一个decorate方法,我们传进去一个Collection和需要的类型甄别信息java.lang.Class,它给我们创建一个全新的强类型的Collection。我们其实在bagbufferlistmapset这些子包中都可以找到分别对应BagBufferListMapSet接口的TypedXxxx版本。

 

方法签名:

public static Collection decorate(Collection coll, Class type)

 

当它执行时,它会判断coll是否为null,同时如果coll包含数据,它会对数据进行验证,看是否满足指定的type条件。最后它返回一个强类型的Collection,当我们对这个强类型的Collection进行add操作时,它会帮我们确保添加的是正确的类型。

 

而这个CollectionUtils可能大家都已经想到了,就是提供一组针对Collection操作的工具/静态方法。比较有意思的是对Collection的转型、合并、减等操作。

 

由于这两个类的功能和作用都比较清晰,我就不举例说明了,需要进一步了解的请看Javadoc

[Jakarta Commons笔记] Commons Collections - Comparator

接下来我们会讲到辅助类,首先看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类如StringIntegerDateDoubleFileCharacter等等都实现了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,最终我们得到了重新排列的数组。

[Jakarta Commons笔记] Commons Collections - Predicate

接下来看Predicate

 

Predicate

AndPredicate

OrPredicate

AllPredicate

OnePredicate

NonePredicate

PredicateUtils

 

PredicateCommons Collections中定义的一个接口,可以在org.apache.commons.collections包中找到。其中定义的方法签名如下:

 

public boolean evaluate(Object object)

 

它以一个Object对象为参数,处理后返回一个boolean值,检验某个对象是否满足某个条件。其实这个Predicate以及上一篇笔记提到的Comparator还有我们即将看到的TransformerClosure等都有些类似C/C++中的函数指针,它们都只是提供简单而明确定义的函数功能而已。

 

跟其他组类似,Commons Collections也提供了一组定义好的Predicate类供我们使用,这些类都放在org.apache.commons.collections.functors包中。当然,我们也可以自定义Predicate,只要实现这个Predicate接口即可。在Commons Collections中我们也可以很方便使用的一组预定义复合Predicate,我们提供2个或不定数量个Predicate,然后交给它,它可以帮我们处理额外的逻辑,如AndPredicate处理两个Predicate,只有当两者都返回true它才返回trueAnyPredicate处理多个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个简单的Predicatep1判断对象是否为String的实例,p2判断是否对象为nullp3是自定义的,判断是否为数字字母的组合,并且长度在6~10字符。然后我用AllPredicate将它们组合到一起,成为p4,当p1p2p3都满足时,p4evaluate方法才返回true。利用Predicate我们可以把判断truefalse的逻辑从特定的业务代码分离出来,以后我们重用也好,重新组装也好,都是很方便的。

[Jakarta Commons笔记] Commons Collections - Transformer

接下来看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也可以有不同的实现,因为它们之间实际上完全是相互独立的。这就使我们有效的分离了逻辑和具体业务。

[Jakarta Commons笔记] Commons Collections - Closure

接下来看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判定再决定执行哪一个ClosureSwitchClosureSwitchTransformer类似,根据创建时传入的Predicate组和Closure组对应执行;WhileClosure则根据创建时传入的Predicate做判断,如果为true则执行Closure,直到Predicate返回false;等等。

 

具体用法请参考Javadoc

[Jakarta Commons笔记] Commons Collections - Iterator

来看最后一组 – 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类,我们就可以轻松的实现可配置的遍历行为了。

[Jakarta Commons笔记] 结语

在前面的随笔中,我们一起过了一遍Jakarta Commons这个类库中非常重要的三个子项目:Commons LangCommons BeanUtilsCommons Collections,这些工具包在无数开源或商业框架中都可以找到,可以说应用范围非常广。

 

当然,Jakarta Commons提供的API远不止我们提到的这些,除了上述三个核心项目之外,还有读取和映射XML文档的Digester/Betwixt、处理命令行参数的CLI、提供常用编码算法的Codec、用于读取多种格式的配置文件的Configuration、发送电子邮件的Email、上传文件的FileUpload、模拟HTTP客户端的HttpClient、处理日志的Logging等等等等,实在是相当的丰富。

 

Jakarta Commons官网上可以找到完整的组件列表:

http://jakarta.apache.org/commons/

 

今后如果发现特别有价值需要跟大家分享的,我还会贴出来,只是不会像这样有条理和规律了。希望我的这一组笔记对大家了解和认识Jakarta Commons有所帮助,也希望这些优秀的类库及其源码能够给大家带来工作效率和编程功底上的提升。

 

我整理了一份清单,列出了所有这一系列随笔的链接,方便大家查找和阅读:

http://www.blogjava.net/sean/articles/Jakarta_Commons_Notes.html

 

posted on 2009-02-04 08:52  丁保国  阅读(2320)  评论(1编辑  收藏  举报