精彩的人生

好好工作,好好生活

BlogJava 首页 新随笔 联系 聚合 管理
  147 Posts :: 0 Stories :: 250 Comments :: 0 Trackbacks

#

java.util 中的集合类包含 Java 中某些最常用的类。 最常用的集合类是 List 和 Map。 List 的具体实现包括 ArrayList 和 Vector,它们是可变大小的列表,比较适合构建、存储和操作任何类型对象的元素列表。 List 适用于按数值索引访问元素的情形。

Map 提供了一个更通用的元素存储方法。 Map 集合类用于存储元素对(称作“键”和“值”),其中每个键映射到一个值。 从概念上而言,您可以将 List 看作是具有数值键的 Map。 而实际上,除了 List 和 Map 都在定义 java.util 中外,两者并没有直接的联系。本文将着重介绍核心 Java 发行套件中附带的 Map,同时还将介绍如何采用或实现更适用于您应用程序特定数据的专用 Map。

了解 Map 接口和方法

Java 核心类中有很多预定义的 Map 类。 在介绍具体实现之前,我们先介绍一下 Map 接口本身,以便了解所有实现的共同点。 Map 接口定义了四种类型的方法,每个 Map 都包含这些方法。 下面,我们从两个普通的方法(表 1)开始对这些方法加以介绍。

表 1: 覆盖的方法。 我们将这 Object 的这两个方法覆盖,以正确比较 Map 对象的等价性。 equals(Object o) 比较指定对象与此 Map 的等价性
hashCode() 返回此 Map 的哈希码



Map 构建

Map 定义了几个用于插入和删除元素的变换方法(表 2)。

表 2: Map 更新方法: 可以更改 Map 内容。 clear() 从 Map 中删除所有映射
remove(Object key) 从 Map 中删除键和关联的值
put(Object key, Object value) 将指定值与指定键相关联
clear() 从 Map 中删除所有映射
putAll(Map t) 将指定 Map 中的所有映射复制到此 map



尽管您可能注意到,纵然假设忽略构建一个需要传递给 putAll() 的 Map 的开销,使用 putAll() 通常也并不比使用大量的 put() 调用更有效率,但 putAll() 的存在一点也不稀奇。 这是因为,putAll() 除了迭代 put() 所执行的将每个键值对添加到 Map 的算法以外,还需要迭代所传递的 Map 的元素。 但应注意,putAll() 在添加所有元素之前可以正确调整 Map 的大小,因此如果您未亲自调整 Map 的大小(我们将对此进行简单介绍),则 putAll() 可能比预期的更有效。

查看 Map

迭代 Map 中的元素不存在直接了当的方法。 如果要查询某个 Map 以了解其哪些元素满足特定查询,或如果要迭代其所有元素(无论原因如何),则您首先需要获取该 Map 的“视图”。 有三种可能的视图(参见表 3)

所有键值对 — 参见 entrySet()
所有键 — 参见 keySet()
所有值 — 参见 values()

前两个视图均返回 Set 对象,第三个视图返回 Collection 对象。 就这两种情况而言,问题到这里并没有结束,这是因为您无法直接迭代 Collection 对象或 Set 对象。要进行迭代,您必须获得一个 Iterator 对象。 因此,要迭代 Map 的元素,必须进行比较烦琐的编码


Iterator keyValuePairs = aMap.entrySet().iterator();
Iterator keys = aMap.keySet().iterator();
Iterator values = aMap.values().iterator();


值得注意的是,这些对象(Set、Collection 和 Iterator)实际上是基础 Map 的视图,而不是包含所有元素的副本。 这使它们的使用效率很高。 另一方面,Collection 或 Set 对象的 toArray() 方法却创建包含 Map 所有元素的数组对象,因此除了确实需要使用数组中元素的情形外,其效率并不高。

我运行了一个小测试(随附文件中的 Test1),该测试使用了 HashMap,并使用以下两种方法对迭代 Map 元素的开销进行了比较:


int mapsize = aMap.size();

Iterator keyValuePairs1 = aMap.entrySet().iterator();
for (int i = 0; i < mapsize; i++)
{
Map.Entry entry = (Map.Entry) keyValuePairs1.next();
Object key = entry.getKey();
Object value = entry.getValue();
...
}

Object[] keyValuePairs2 = aMap.entrySet().toArray();
for (int i = 0; i < rem; i++) {
{
Map.Entry entry = (Map.Entry) keyValuePairs2[i];
Object key = entry.getKey();


Object value = entry.getValue();
...
}


此测试使用了两种测量方法: 一种是测量迭代元素的时间,另一种测量使用 toArray 调用创建数组的其他开销。 第一种方法(忽略创建数组所需的时间)表明,使用已从 toArray 调用中创建的数组迭代元素的速度要比使用 Iterator 的速度大约快 30%-60%。 但如果将使用 toArray 方法创建数组的开销包含在内,则使用 Iterator 实际上要快 10%-20%。 因此,如果由于某种原因要创建一个集合元素的数组而非迭代这些元素,则应使用该数组迭代元素。 但如果您不需要此中间数组,则不要创建它,而是使用 Iterator 迭代元素。

表 3: 返回视图的 Map 方法: 使用这些方法返回的对象,您可以遍历 Map 的元素,还可以删除 Map 中的元素。 entrySet() 返回 Map 中所包含映射的 Set 视图。 Set 中的每个元素都是一个 Map.Entry 对象,可以使用 getKey() 和 getValue() 方法(还有一个 setValue() 方法)访问后者的键元素和值元素
keySet() 返回 Map 中所包含键的 Set 视图。 删除 Set 中的元素还将删除 Map 中相应的映射(键和值)
values() 返回 map 中所包含值的 Collection 视图。 删除 Collection 中的元素还将删除 Map 中相应的映射(键和值)



访问元素

表 4 中列出了 Map 访问方法。Map 通常适合按键(而非按值)进行访问。 Map 定义中没有规定这肯定是真的,但通常您可以期望这是真的。 例如,您可以期望 containsKey() 方法与 get() 方法一样快。 另一方面,containsValue() 方法很可能需要扫描 Map 中的值,因此它的速度可能比较慢。

表 4: Map 访问和测试方法: 这些方法检索有关 Map 内容的信息但不更改 Map 内容。 get(Object key) 返回与指定键关联的值
containsKey(Object key) 如果 Map 包含指定键的映射,则返回 true
containsValue(Object value) 如果此 Map 将一个或多个键映射到指定值,则返回 true
isEmpty() 如果 Map 不包含键-值映射,则返回 true
size() 返回 Map 中的键-值映射的数目



对使用 containsKey() 和 containsValue() 遍历 HashMap 中所有元素所需时间的测试表明,containsValue() 所需的时间要长很多。 实际上要长几个数量级! (参见图 1 和图 2,以及随附文件中的 Test2)。 因此,如果 containsValue() 是应用程序中的性能问题,它将很快显现出来,并可以通过监测您的应用程序轻松地将其识别。 这种情况下,我相信您能够想出一个有效的替换方法来实现 containsValue() 提供的等效功能。 但如果想不出办法,则一个可行的解决方案是再创建一个 Map,并将第一个 Map 的所有值作为键。 这样,第一个 Map 上的 containsValue() 将成为第二个 Map 上更有效的 containsKey()。


图 1: 使用 JDeveloper 创建并运行 Map 测试类




图 2: 在 JDeveloper 中使用执行监测器进行的性能监测查出应用程序中的瓶颈



核心 Map

Java 自带了各种 Map 类。 这些 Map 类可归为三种类型:


通用 Map,用于在应用程序中管理映射,通常在 java.util 程序包中实现
HashMap
Hashtable
Properties
LinkedHashMap
IdentityHashMap
TreeMap
WeakHashMap
ConcurrentHashMap
专用 Map,您通常不必亲自创建此类 Map,而是通过某些其他类对其进行访问
java.util.jar.Attributes
javax.print.attribute.standard.PrinterStateReasons
java.security.Provider
java.awt.RenderingHints
javax.swing.UIDefaults
一个用于帮助实现您自己的 Map 类的抽象类
AbstractMap

内部哈希: 哈希映射技术

几乎所有通用 Map 都使用哈希映射。 这是一种将元素映射到数组的非常简单的机制,您应了解哈希映射的工作原理,以便充分利用 Map。

哈希映射结构由一个存储元素的内部数组组成。 由于内部采用数组存储,因此必然存在一个用于确定任意键访问数组的索引机制。 实际上,该机制需要提供一个小于数组大小的整数索引值。 该机制称作哈希函数。 在 Java 基于哈希的 Map 中,哈希函数将对象转换为一个适合内部数组的整数。 您不必为寻找一个易于使用的哈希函数而大伤脑筋: 每个对象都包含一个返回整数值的 hashCode() 方法。 要将该值映射到数组,只需将其转换为一个正值,然后在将该值除以数组大小后取余数即可。 以下是一个简单的、适用于任何对象的 Java 哈希函数


int hashvalue = Maths.abs(key.hashCode()) % table.length;


(% 二进制运算符(称作模)将左侧的值除以右侧的值,然后返回整数形式的余数。)

实际上,在 1.4 版发布之前,这就是各种基于哈希的 Map 类所使用的哈希函数。 但如果您查看一下代码,您将看到


int hashvalue = (key.hashCode() & 0x7FFFFFFF) % table.length;


它实际上是使用更快机制获取正值的同一函数。 在 1.4 版中,HashMap 类实现使用一个不同且更复杂的哈希函数,该函数基于 Doug Lea 的 util.concurrent 程序包(稍后我将更详细地再次介绍 Doug Lea 的类)。


图 3: 哈希工作原理



该图介绍了哈希映射的基本原理,但我们还没有对其进行详细介绍。 我们的哈希函数将任意对象映射到一个数组位置,但如果两个不同的键映射到相同的位置,情况将会如何? 这是一种必然发生的情况。 在哈希映射的术语中,这称作冲突。 Map 处理这些冲突的方法是在索引位置处插入一个链接列表,并简单地将元素添加到此链接列表。 因此,一个基于哈希的 Map 的基本 put() 方法可能如下所示


public Object put(Object key, Object value) {
//我们的内部数组是一个 Entry 对象数组
//Entry[] table;

//获取哈希码,并映射到一个索引
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % table.length;

//循环遍历位于 table[index] 处的链接列表,以查明
//我们是否拥有此键项 — 如果拥有,则覆盖它
for (Entry e = table[index] ; e != null ; e = e.next) {
//必须检查键是否相等,原因是不同的键对象
//可能拥有相同的哈希
if ((e.hash == hash) && e.key.equals(key)) {
//这是相同键,覆盖该值
//并从该方法返回 old 值
Object old = e.value;
e.value = value;
return old;
}
}

//仍然在此处,因此它是一个新键,只需添加一个新 Entry
//Entry 对象包含 key 对象、 value 对象、一个整型的 hash、
//和一个指向列表中的下一个 Entry 的 next Entry

//创建一个指向上一个列表开头的新 Entry,
//并将此新 Entry 插入表中
Entry e = new Entry(hash, key, value, table[index]);
table[index] = e;

return null;
}



如果看一下各种基于哈希的 Map 的源代码,您将发现这基本上就是它们的工作原理。 此外,还有一些需要进一步考虑的事项,如处理空键和值以及调整内部数组。 此处定义的 put() 方法还包含相应 get() 的算法,这是因为插入包括搜索映射索引处的项以查明该键是否已经存在。 (即 get() 方法与 put() 方法具有相同的算法,但 get() 不包含插入和覆盖代码。) 使用链接列表并不是解决冲突的唯一方法,某些哈希映射使用另一种“开放式寻址”方案,本文对其不予介绍。

优化 Hasmap

如果哈希映射的内部数组只包含一个元素,则所有项将映射到此数组位置,从而构成一个较长的链接列表。 由于我们的更新和访问使用了对链接列表的线性搜索,而这要比 Map 中的每个数组索引只包含一个对象的情形要慢得多,因此这样做的效率很低。 访问或更新链接列表的时间与列表的大小线性相关,而使用哈希函数访问或更新数组中的单个元素则与数组大小无关 — 就渐进性质(Big-O 表示法)而言,前者为 O(n),而后者为 O(1)。 因此,使用一个较大的数组而不是让太多的项聚集在太少的数组位置中是有意义的。

调整 Map 实现的大小

在哈希术语中,内部数组中的每个位置称作“存储桶”(bucket),而可用的存储桶数(即内部数组的大小)称作容量 (capacity)。 为使 Map 对象有效地处理任意数目的项,Map 实现可以调整自身的大小。 但调整大小的开销很大。 调整大小需要将所有元素重新插入到新数组中,这是因为不同的数组大小意味着对象现在映射到不同的索引值。 先前冲突的键可能不再冲突,而先前不冲突的其他键现在可能冲突。 这显然表明,如果将 Map 调整得足够大,则可以减少甚至不再需要重新调整大小,这很有可能显著提高速度。

使用 1.4.2 JVM 运行一个简单的测试,即用大量的项(数目超过一百万)填充 HashMap。 表 5 显示了结果,并将所有时间标准化为已预先设置大小的服务器模式(关联文件中的 Test3)。 对于已预先设置大小的 JVM,客户端和服务器模式 JVM 运行时间几乎相同(在放弃 JIT 编译阶段后)。 但使用 Map 的默认大小将引发多次调整大小操作,开销很大,在服务器模式下要多用 50% 的时间,而在客户端模式下几乎要多用两倍的时间!

表 5: 填充已预先设置大小的 HashMap 与填充默认大小的 HashMap 所需时间的比较 客户端模式 服务器模式
预先设置的大小 100% 100%
默认大小 294% 157%



使用负载因子

为确定何时调整大小,而不是对每个存储桶中的链接列表的深度进行记数,基于哈希的 Map 使用一个额外参数并粗略计算存储桶的密度。 Map 在调整大小之前,使用名为“负载因子”的参数指示 Map 将承担的“负载”量,即它的负载程度。 负载因子、项数(Map 大小)与容量之间的关系简单明了:


如果(负载因子)x(容量)>(Map 大小),则调整 Map 大小

例如,如果默认负载因子为 0.75,默认容量为 11,则 11 x 0.75 = 8.25,该值向下取整为 8 个元素。 因此,如果将第 8 个项添加到此 Map,则该 Map 将自身的大小调整为一个更大的值。 相反,要计算避免调整大小所需的初始容量,用将要添加的项数除以负载因子,并向上取整,例如,


对于负载因子为 0.75 的 100 个项,应将容量设置为 100/0.75 = 133.33,并将结果向上取整为 134(或取整为 135 以使用奇数)

奇数个存储桶使 map 能够通过减少冲突数来提高执行效率。 虽然我所做的测试(关联文件中的Test4)并未表明质数可以始终获得更好的效率,但理想情形是容量取质数。 1.4 版后的某些 Map(如 HashMap 和 LinkedHashMap,而非 Hashtable 或 IdentityHashMap)使用需要 2 的幂容量的哈希函数,但下一个最高 2 的幂容量由这些 Map 计算,因此您不必亲自计算。

负载因子本身是空间和时间之间的调整折衷。 较小的负载因子将占用更多的空间,但将降低冲突的可能性,从而将加快访问和更新的速度。 使用大于 0.75 的负载因子可能是不明智的,而使用大于 1.0 的负载因子肯定是不明知的,这是因为这必定会引发一次冲突。 使用小于 0.50 的负载因子好处并不大,但只要您有效地调整 Map 的大小,应不会对小负载因子造成性能开销,而只会造成内存开销。 但较小的负载因子将意味着如果您未预先调整 Map 的大小,则导致更频繁的调整大小,从而降低性能,因此在调整负载因子时一定要注意这个问题。

选择适当的 Map

应使用哪种 Map? 它是否需要同步? 要获得应用程序的最佳性能,这可能是所面临的两个最重要的问题。 当使用通用 Map 时,调整 Map 大小和选择负载因子涵盖了 Map 调整选项。

以下是一个用于获得最佳 Map 性能的简单方法

将您的所有 Map 变量声明为 Map,而不是任何具体实现,即不要声明为 HashMap 或 Hashtable,或任何其他 Map 类实现。


Map criticalMap = new HashMap(); //好

HashMap criticalMap = new HashMap(); //差


这使您能够只更改一行代码即可非常轻松地替换任何特定的 Map 实例。

下载 Doug Lea 的 util.concurrent 程序包 (http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html)。 将 ConcurrentHashMap 用作默认 Map。 当移植到 1.5 版时,将 java.util.concurrent.ConcurrentHashMap 用作您的默认 Map。 不要将 ConcurrentHashMap 包装在同步的包装器中,即使它将用于多个线程。 使用默认大小和负载因子。
监测您的应用程序。 如果发现某个 Map 造成瓶颈,则分析造成瓶颈的原因,并部分或全部更改该 Map 的以下内容: Map 类;Map 大小;负载因子;关键对象 equals() 方法实现。 专用的 Map 的基本上都需要特殊用途的定制 Map 实现,否则通用 Map 将实现您所需的性能目标。

Map 选择

也许您曾期望更复杂的考量,而这实际上是否显得太容易? 好的,让我们慢慢来。 首先,您应使用哪种 Map? 答案很简单: 不要为您的设计选择任何特定的 Map,除非实际的设计需要指定一个特殊类型的 Map。 设计时通常不需要选择具体的 Map 实现。 您可能知道自己需要一个 Map,但不知道使用哪种。 而这恰恰就是使用 Map 接口的意义所在。 直到需要时再选择 Map 实现 — 如果随处使用“Map”声明的变量,则更改应用程序中任何特殊 Map 的 Map 实现只需要更改一行,这是一种开销很少的调整选择。 是否要使用默认的 Map 实现? 我很快将谈到这个问题。

同步 Map

同步与否有何差别? (对于同步,您既可以使用同步的 Map,也可以使用 Collections.synchronizedMap() 将未同步的 Map 转换为同步的 Map。 后者使用“同步的包装器”)这是一个异常复杂的选择,完全取决于您如何根据多线程并发访问和更新使用 Map,同时还需要进行维护方面的考虑。 例如,如果您开始时未并发更新特定 Map,但它后来更改为并发更新,情况将如何? 在这种情况下,很容易在开始时使用一个未同步的 Map,并在后来向应用程序中添加并发更新线程时忘记将此未同步的 Map 更改为同步的 Map。 这将使您的应用程序容易崩溃(一种要确定和跟踪的最糟糕的错误)。 但如果默认为同步,则将因随之而来的可怕性能而序列化执行多线程应用程序。 看起来,我们需要某种决策树来帮助我们正确选择。

Doug Lea 是纽约州立大学奥斯威戈分校计算机科学系的教授。 他创建了一组公共领域的程序包(统称 util.concurrent),该程序包包含许多可以简化高性能并行编程的实用程序类。 这些类中包含两个 Map,即 ConcurrentReaderHashMap 和 ConcurrentHashMap。 这些 Map 实现是线程安全的,并且不需要对并发访问或更新进行同步,同时还适用于大多数需要 Map 的情况。 它们还远比同步的 Map(如 Hashtable)或使用同步的包装器更具伸缩性,并且与 HashMap 相比,它们对性能的破坏很小。 util.concurrent 程序包构成了 JSR166 的基础;JSR166 已经开发了一个包含在 Java 1.5 版中的并发实用程序,而 Java 1.5 版将把这些 Map 包含在一个新的 java.util.concurrent 程序包中。

所有这一切意味着您不需要一个决策树来决定是使用同步的 Map 还是使用非同步的 Map, 而只需使用 ConcurrentHashMap。 当然,在某些情况下,使用 ConcurrentHashMap 并不合适。 但这些情况很少见,并且应具体情况具体处理。 这就是监测的用途。


结束语

通过 Oracle JDeveloper 可以非常轻松地创建一个用于比较各种 Map 性能的测试类。 更重要的是,集成良好的监测器可以在开发过程中快速、轻松地识别性能瓶颈 - 集成到 IDE 中的监测器通常被较频繁地使用,以便帮助构建一个成功的工程。 现在,您已经拥有了一个监测器并了解了有关通用 Map 及其性能的基础知识,可以开始运行您自己的测试,以查明您的应用程序是否因 Map 而存在瓶颈以及在何处需要更改所使用的 Map。

以上内容介绍了通用 Map 及其性能的基础知识。 当然,有关特定 Map 实现以及如何根据不同的需求使用它们还存在更多复杂和值得关注的事项,这些将在本文第 2 部分中介绍。


--------------------------------------------------------------------------------
Jack Shirazi 是 O''Reilly 的“Java 性能调整”的作者,以及受欢迎的 JavaPerformanceTuning.com 网站(提供 Java 性能信息的全球知名站点)的总监。 Jack 在 Java 性能领域提供咨询并著书立说。 他还监督 JavaPerformanceTuning.com 提供的信息,其中包括每年大约发布 1000 条性能技巧以及许多有关性能工具、讨论组等内容的文章。 Jack 早年还曾发布有关蛋白质结构预测以及黑洞热力学方面的文章,而且在其空闲时还对某些 Perl5 核心模块作出了贡献。


摘自http://www.5ivb.net/Info/121/Info36849/
posted @ 2006-04-03 11:09 hopeshared 阅读(681) | 评论 (0)编辑 收藏

如果想要取得系统的时间,可以使用System.currentTimeMillis()方法,例如:


  • DateDemo.java

public class DateDemo {
    public static void main(String[]
args) {
        System.out.println(System.currentTimeMillis());
    }
}



执行结果会显示从1970年1月1日开始到取得系统时间为止所经过的毫秒数,例如1115346430703这个数字,但这样的数字没有人确切了解它的意 义是什么,您可以使用Date类别来让这个数字变的更有意义一些,例如:


  • DateDemo.java

import java.util.Date;

public class DateDemo {
    public static void main(String[]
args) {
        Date date = new Date();
        
        System.out.println(date.toString());
        System.out.println(date.getTime());
    }
}



执行的结果如下:

  

Fri May 06 10:31:13 GMT+08:00 2005
  1115346673531

  

当您生成Date对象时,实际上它会使用System.currentTimeMillis()来取得系统时间,而您使用
toString()方法时,会将取得的1970年1月1日至今的毫秒数转为dow mon dd hh:mm:ss zzz yyyy的格式,分别是:「星期 月 日 时:分:秒 公元」;使用Date的getTime()方法则可以取得毫秒数。

如果您想要对日期时间作格式设定,则可以使用DateFormat来作格式化,先来看看它的子类SimpleDateFormat如何使用:
  • DateDemo.java

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DateDemo {
    public static void main(String[]
args) {
        Date date = new Date();

        DateFormat dateFormat =
            new
SimpleDateFormat("EE-MM-dd-yyyy");
        
        System.out.println(dateFormat.format(date));
    }
}



执行结果:

  

星期五-05-06-2005

  

DateFormat会依计算机上的区域设定显示时间格式,EE表示星期,MM表示月份、dd表示日期,而yyyy是公元,每个字符的设定都各有其意义,您
可以参考 SimpleDateFormat 的API说明了解每个字符设定的意义。

您也可以直接从DateFormat指定格式生成DateFormat的实例,例如:
  • DateDemo.java

import java.text.DateFormat;
import java.util.Date;

public class DateDemo {
    public static void main(String[]
args) {
        Date date = new Date();

        DateFormat shortFormat =
            DateFormat.getDateTimeInstance(
                DateFormat.SHORT,
DateFormat.SHORT);

        DateFormat mediumFormat =
            DateFormat.getDateTimeInstance(
                DateFormat.MEDIUM,
DateFormat.MEDIUM);

        DateFormat longFormat =
            DateFormat.getDateTimeInstance(
                DateFormat.LONG, DateFormat.LONG);

        DateFormat fullFormat =
            DateFormat.getDateTimeInstance(
                DateFormat.FULL,
DateFormat.FULL);

        System.out.println(shortFormat.format(date));
        System.out.println(mediumFormat.format(date));
        System.out.println(longFormat.format(date));
        System.out.println(fullFormat.format(date));
    }
}



在使用getDateTimeInstance()取得DateFormat实例时,可以指定的参数是日期格式与时间格式,以上所指定的格式依讯息详细度 区分,执行结果如下:

2005/5/6 上午 10:45
2005/5/6 上午 10:45:25
2005年5月6日 上午10时45分25秒
2005年5月6日 星期五 上午10时45分25秒 GMT+08:00  


您也可以使用getDateInstance()取得DateFormat实 例,并同时指定日期的区域显示方式,例如:


  • DateDemo.java    


import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;

public class DateDemo {
    public static void main(String[]
args) {
        Date date = new Date();

        Locale locale = new
Locale("en", "US");
        DateFormat shortFormat =
            DateFormat.getDateInstance(
                DateFormat.SHORT,
locale);

        DateFormat mediumFormat =
            DateFormat.getDateInstance(
                DateFormat.MEDIUM,
locale);

        DateFormat longFormat =
            DateFormat.getDateInstance(
                DateFormat.LONG, locale);

        DateFormat fullFormat =
            DateFormat.getDateInstance(
                DateFormat.FULL, locale);

        System.out.println(shortFormat.format(date));
        System.out.println(mediumFormat.format(date));
        System.out.println(longFormat.format(date));
        System.out.println(fullFormat.format(date));
    }
}



这边指定了美国的时间显示方式,执行结果如下:

  

5/6/05
  May 6, 2005
  May 6, 2005
  Friday, May 6, 2005


-------------------------------------------------------------------------------------------------

1. 怎样计算两个时间之间的间隔?

间隔=Date1.getTime()-Date2.getTime();得出来的是毫秒数.
除1000是秒,再除60是分,再除60是小时..............................

记住java标准库中所有时间类都以此为基础转化的,只是他写好了一些
转化的方法给你用而已.但都离不开这个毫秒数为基础.

2. t=Calendar.getInstance();m=t.get(t.MONTH)+1;这里为什么要加一?

在java语言里,date的month的取值范围是:0~11,与人们的自然表达上相差1。

3. 系统时间与当前日期的区别?

系统时间确切的说应该是
System.currentTimeMillis();
new Date()是当前日期,虽然它getTime();和System.currentTimeMillis();
一样,但System.currentTimeMillis();

4. 如何计算两个日期的天数差值?

long beginTime = beginDate.getTime();
long endTime2 = endDate.getTime();
long betweenDays = (long)((endTime - beginTime) / (1000 * 60 * 60 *24) + 0.5);

5. 如何比较日期时间大小?

第一种方法:
use Calendar object to compare
java.util.Calendar class can be used to compare date. In order to do this,
you guy should parse that string into int year, month, day and construct a
Calendar object, and then do comparison.

Below is a sample

StringTokenizer token = new StringTokenizer(your string,"-");
int year = Integer.parseInt(token.nextToken());
int month = Integer.parseInt(token.nextToken());
int day = Integer.parseInt(token.nextToken());
Calendar date = Calendar.getInstance();
date.set(year,month,day);
Calendar today = Calendar.getInstacne();
if(date.after(today)){
//......
}
第二种方法
Date nowDate=new Date();//当前时间\r
long nowTime=nowDate.getTime;
long lastTime=userTime.longValue();//以前的时间\r
long time=nowTime-lastTime;//时间相减比较。
if(time>(long)60000)//1分钟{}

另外可用以下参考
用时间戳,Date.getTime()可以把当前时间改成时间戳,
用CompareTo();
用before(),after(),equals();

6. 格式化日期的问题\r

目的:
第一次求日期
java.text.SimpleDateFormat formatter = new java.text.SimpleDateFormat("yyyy-MM-dd");
String riqi=formatter.format(currentTime_1);
第二次求时间\r
java.text.DateFormat format1 = new java.text.SimpleDateFormat("hhmmss");
java.util.Date currentTime_2 = new java.util.Date();
String shijian=format1.format(currentTime_2);
得到的结果是
2002-02-19和115324(11点53分24秒)

实现:
java.text.SimpleDateFormat formatter = new java.text.SimpleDateFormat("yyyy-MM-dd-H-mm-ss");
java.util.Date currentTime_1 = new java.util.Date();
String str_date = formatter.format(currentTime_1);
StringTokenizer token = new StringTokenizer(str_date,"-");
String year = token.nextToken();
String month= token.nextToken();
String day = token.nextToken();
String hh = token.nextToken();
String mm = token.nextToken();
String ss = token.nextToken();
String riqi=year+"年\\"+month+"月"+day+"日"+" "+hh+"点\\"+mm+"分"+ss+"秒\\";
String newdir=year+month+day;
String wenjian = hh+mm+ss;

7. 怎么得到一个月的天数?

java.util.Calendar date = java.util.Calendar.getInstance();
System.out.println(date.getActualMaximum(date.DAY_OF_MONTH));


------------------------------------------------------------------------------------------------

 
取得指定月份的第一天与取得指定月份的最后一天  
       /**  
         *  取得指定月份的第一天  
         *  
         *  @param  strdate  String  
         *  @return  String  
         */  
       public  String  getMonthBegin(String  strdate)  
       {  
               java.util.Date  date  =  parseFormatDate(strdate);  
               return  formatDateByFormat(date,"yyyy-MM")  +  "-01";  
       }  

       /**  
         *  取得指定月份的最后一天  
         *  
         *  @param  strdate  String  
         *  @return  String  
         */  
       public  String  getMonthEnd(String  strdate)  
       {  
               java.util.Date  date  =  parseFormatDate(getMonthBegin(strdate));  
               Calendar  calendar  =  Calendar.getInstance();  
               calendar.setTime(date);  
               calendar.add(Calendar.MONTH,1);  
               calendar.add(Calendar.DAY_OF_YEAR,  -1);  
               return  formatDate(calendar.getTime());  
       }  

       /**  
         *  常用的格式化日期  
         *  
         *  @param  date  Date  
         *  @return  String  
         */  
       public  String  formatDate(java.util.Date  date)  
       {  
               return  formatDateByFormat(date,"yyyy-MM-dd");  
       }  

       /**  
         *  以指定的格式来格式化日期  
         *  
         *  @param  date  Date  
         *  @param  format  String  
         *  @return  String  
         */  
       public  String  formatDateByFormat(java.util.Date  date,String  format)  
       {  
               String  result  =  "";  
               if(date  !=  null)  
               {  
                       try  
                       {  
                               SimpleDateFormat  sdf  =  new  SimpleDateFormat(format);  
                               result  =  sdf.format(date);  
                       }  
                       catch(Exception  ex)  
                       {  
                               LOGGER.info("date:"  +  date);  
                               ex.printStackTrace();  
                       }  
               }  
               return  result;  
       }  
---------------------------------------------------------------  

package  com.app.util;  

/**  
*  日期操作  
*    
*  @author  xxx  
*  @version  2.0  jdk1.4.0  tomcat5.1.0  *  Updated  Date:2005/03/10  
*/  
public  class  DateUtil  {  
           /**  
             *  格式化日期  
             *    
             *  @param  dateStr  
             *                        字符型日期  
             *  @param  format  
             *                        格式  
             *  @return  返回日期  
             */  
           public  static  java.util.Date  parseDate(String  dateStr,  String  format)  {  
                       java.util.Date  date  =  null;  
                       try  {  
                                   java.text.DateFormat  df  =  new  java.text.SimpleDateFormat(format);  
                                   String  dt=Normal.parse(dateStr).replaceAll(  
                                                           "-",  "/");  
                                   if((!dt.equals(""))&&(dt.length()<format.length())){  
                                               dt+=format.substring(dt.length()).replaceAll("[YyMmDdHhSs]","0");  
                                   }  
                                   date  =  (java.util.Date)  df.parse(dt);  
                       }  catch  (Exception  e)  {  
                       }  
                       return  date;  
           }  

           public  static  java.util.Date  parseDate(String  dateStr)  {  
                       return  parseDate(dateStr,  "yyyy/MM/dd");  
           }  

           public  static  java.util.Date  parseDate(java.sql.Date  date)  {  
                       return  date;  
           }  
            
           public  static  java.sql.Date  parseSqlDate(java.util.Date  date)  {  
                       if  (date  !=  null)  
                                   return  new  java.sql.Date(date.getTime());  
                       else  
                                   return  null;  
           }  

           public  static  java.sql.Date  parseSqlDate(String  dateStr,  String  format)  {  
                       java.util.Date  date  =  parseDate(dateStr,  format);  
                       return  parseSqlDate(date);  
           }  

           public  static  java.sql.Date  parseSqlDate(String  dateStr)  {  
                       return  parseSqlDate(dateStr,  "yyyy/MM/dd");  
           }  

            
           public  static  java.sql.Timestamp  parseTimestamp(String  dateStr,  
                                   String  format)  {  
                       java.util.Date  date  =  parseDate(dateStr,  format);  
                       if  (date  !=  null)  {  
                                   long  t  =  date.getTime();  
                                   return  new  java.sql.Timestamp(t);  
                       }  else  
                                   return  null;  
           }  

           public  static  java.sql.Timestamp  parseTimestamp(String  dateStr)  {  
                       return  parseTimestamp(dateStr,  "yyyy/MM/dd  HH:mm:ss");  
           }  

           /**  
             *  格式化输出日期  
             *    
             *  @param  date  
             *                        日期  
             *  @param  format  
             *                        格式  
             *  @return  返回字符型日期  
             */  
           public  static  String  format(java.util.Date  date,  String  format)  {  
                       String  result  =  "";  
                       try  {  
                                   if  (date  !=  null)  {  
                                               java.text.DateFormat  df  =  new  java.text.SimpleDateFormat(format);  
                                               result  =  df.format(date);  
                                   }  
                       }  catch  (Exception  e)  {  
                       }  
                       return  result;  
           }  

           public  static  String  format(java.util.Date  date)  {  
                       return  format(date,  "yyyy/MM/dd");  
           }  

           /**  
             *  返回年份  
             *    
             *  @param  date  
             *                        日期  
             *  @return  返回年份  
             */  
           public  static  int  getYear(java.util.Date  date)  {  
                       java.util.Calendar  c  =  java.util.Calendar.getInstance();  
                       c.setTime(date);  
                       return  c.get(java.util.Calendar.YEAR);  
           }  

           /**  
             *  返回月份  
             *    
             *  @param  date  
             *                        日期  
             *  @return  返回月份  
             */  
           public  static  int  getMonth(java.util.Date  date)  {  
                       java.util.Calendar  c  =  java.util.Calendar.getInstance();  
                       c.setTime(date);  
                       return  c.get(java.util.Calendar.MONTH)  +  1;  
           }  

           /**  
             *  返回日份  
             *    
             *  @param  date  
             *                        日期  
             *  @return  返回日份  
             */  
           public  static  int  getDay(java.util.Date  date)  {  
                       java.util.Calendar  c  =  java.util.Calendar.getInstance();  
                       c.setTime(date);  
                       return  c.get(java.util.Calendar.DAY_OF_MONTH);  
           }  

           /**  
             *  返回小时  
             *    
             *  @param  date  
             *                        日期  
             *  @return  返回小时  
             */  
           public  static  int  getHour(java.util.Date  date)  {  
                       java.util.Calendar  c  =  java.util.Calendar.getInstance();  
                       c.setTime(date);  
                       return  c.get(java.util.Calendar.HOUR_OF_DAY);  
           }  

           /**  
             *  返回分钟  
             *    
             *  @param  date  
             *                        日期  
             *  @return  返回分钟  
             */  
           public  static  int  getMinute(java.util.Date  date)  {  
                       java.util.Calendar  c  =  java.util.Calendar.getInstance();  
                       c.setTime(date);  
                       return  c.get(java.util.Calendar.MINUTE);  
           }  

           /**  
             *  返回秒钟  
             *    
             *  @param  date  
             *                        日期  
             *  @return  返回秒钟  
             */  
           public  static  int  getSecond(java.util.Date  date)  {  
                       java.util.Calendar  c  =  java.util.Calendar.getInstance();  
                       c.setTime(date);  
                       return  c.get(java.util.Calendar.SECOND);  
           }  

           /**  
             *  返回毫秒  
             *    
             *  @param  date  
             *                        日期  
             *  @return  返回毫秒  
             */  
           public  static  long  getMillis(java.util.Date  date)  {  
                       java.util.Calendar  c  =  java.util.Calendar.getInstance();  
                       c.setTime(date);  
                       return  c.getTimeInMillis();  
           }  

           /**  
             *  返回字符型日期  
             *    
             *  @param  date  
             *                        日期  
             *  @return  返回字符型日期  
             */  
           public  static  String  getDate(java.util.Date  date)  {  
                       return  format(date,  "yyyy/MM/dd");  
           }  

           /**  
             *  返回字符型时间  
             *    
             *  @param  date  
             *                        日期  
             *  @return  返回字符型时间  
             */  
           public  static  String  getTime(java.util.Date  date)  {  
                       return  format(date,  "HH:mm:ss");  
           }  

           /**  
             *  返回字符型日期时间  
             *    
             *  @param  date  
             *                        日期  
             *  @return  返回字符型日期时间  
             */  
           public  static  String  getDateTime(java.util.Date  date)  {  
                       return  format(date,  "yyyy/MM/dd  HH:mm:ss");  
           }  

           /**  
             *  日期相加  
             *    
             *  @param  date  
             *                        日期  
             *  @param  day  
             *                        天数  
             *  @return  返回相加后的日期  
             */  
           public  static  java.util.Date  addDate(java.util.Date  date,  int  day)  {  
                       java.util.Calendar  c  =  java.util.Calendar.getInstance();  
                       c.setTimeInMillis(getMillis(date)  +  ((long)  day)  *  24  *  3600  *  1000);  
                       return  c.getTime();  
           }  

           /**  
             *  日期相减  
             *    
             *  @param  date  
             *                        日期  
             *  @param  date1  
             *                        日期  
             *  @return  返回相减后的日期  
             */  
           public  static  int  diffDate(java.util.Date  date,  java.util.Date  date1)  {  
                       return  (int)  ((getMillis(date)  -  getMillis(date1))  /  (24  *  3600  *  1000));  
           }              
}  
------------------------------------------------------------------------------------------------------------------

来个简单点的,也许有点用  

Calendar  now  =  Calendar.getInstance();  
int  year  =  now.get(Calendar.YEAR);  
int  date  =  now.get(Calendar.DAY_OF_MONTH);  
int  month  =  now.get(Calendar.MONTH)  +  1;  
int  hour  =  now.get(Calendar.HOUR);  
int  min  =  now.get(Calendar.MINUTE);  
int  sec  =  now.get(Calendar.SECOND);  



转自http://www.513pc.net/bbs/dispbbs.asp?boardid=4&id=239&star=1&page=1

posted @ 2006-04-03 10:56 hopeshared 阅读(2451) | 评论 (1)编辑 收藏

kXML is a small XML pull parser, specially designed for constrained environments such as Applets, Personal Java or MIDP devices.

最小的版本只有11k,比那些庞大的xml解析起确实小好多。当你对xml解析不需要很严格时可以使用它。

下面是使用kxml的一段示例代码:
import org.xmlpull.v1.*;

import java.util.*;
import java.io.*;
import java.net.*;

/** 
 * A simple example illustrationg some differences of the XmlPull API 
 * and SAX. For the corresponding SAX based implementation, please refer to 
 * http://www.cafeconleche.org/slides/sd2001east/xmlandjava/81.html ff. */publicclassWeblogs {

    static List listChannels()
        throws IOException, XmlPullParserException {
        return listChannels("http://static.userland.com/weblogMonitor/logs.xml");
    }

    static List listChannels(String uri)
        throws IOException, XmlPullParserException {

        Vector result = new Vector();

        InputStream is = new URL(uri).openStream();
        XmlPullParser parser =
            XmlPullParserFactory.newInstance().newPullParser();

        parser.setInput(is, null);

        parser.nextTag();
        parser.require(XmlPullParser.START_TAG, "", "weblogs");

        while (parser.nextTag() == XmlPullParser.START_TAG) {
            String url = readSingle(parser);
            if (url != null)
                result.addElement(url);
        }
        parser.require(XmlPullParser.END_TAG, "", "weblogs");

        parser.next();
        parser.require(XmlPullParser.END_DOCUMENT, null, null);

		is.close ();
		parser.setInput (null);

        return result;
    }

    publicstatic String readSingle(XmlPullParser parser)
        throws IOException, XmlPullParserException {

        String url = null;
        parser.require(XmlPullParser.START_TAG, "", "log");

        while (parser.nextTag() == XmlPullParser.START_TAG) {
            String name = parser.getName();
            String content = parser.nextText();
            if (name.equals("url"))
                url = content;
            parser.require(XmlPullParser.END_TAG, "", name);
        }
        parser.require(XmlPullParser.END_TAG, "", "log");
        return url;
    }

    publicstaticvoid main(String[] args)
        throws IOException, XmlPullParserException {

        List urls =
            args.length > 0
                ? listChannels(args[0])
                : listChannels();

        for (Iterator i = urls.iterator(); i.hasNext();)
            System.out.println(i.next());
    }
}




摘自http://www.21tx.com/dev/2004/11/27/11901.html
posted @ 2006-04-03 10:34 hopeshared 阅读(675) | 评论 (0)编辑 收藏

在本系列的第一篇文章中,我研究了一些用 Java 编写的主要的 XML 文档模型的性能。但是,在开始选择这种类型的技术时,性能只是问题的一部分。使用方便至少是同样重要的,并且它已是一个主要理由,来支持使用 Java 特定的模型,而不是与语言无关的 DOM 。

为切实了解哪个模型真正的作用,您需要知道它们在可用性程度上是如何排名的。本文中,我将尝试进行这个工作,从样本代码开始,来演示如何在每个模型中编码公共类型的操作。并对结果进行总结来结束本文,而且提出了促使一种表示比另一种更容易使用的一些其它因素。

请参阅以前的文章(请参阅参考资料或本文“内容”下的便捷链接)来获取这个对比中使用的各个模型的背景资料,包含实际的版本号。还可以参阅“参考资料”一节中关于源代码下载、到模型主页的链接以及其它相关信息。

代码对比
在对不同文档表示中用法技术的这些对比中,我将显示如何在每种模型中实现三种基本操作:

根据输入流构建文档
遍历元素和内容,并做一些更改:
从文本内容中除去前导和尾随的空白。
如果结果文本内容为空,就删除它。
否则,将它包装到父元素的名称空间中一个名为“text”的新元素中。
将已修改的文档写入输出流

这些示例的代码是以我在上篇文章中使用的基准程序为基础的,并进行了一些简化。基准程序的焦点是为了显示每个模型的最佳性能;对于本文,我将尝试显示在每种模型中实现操作的最简便方法。

我已经将每个模型的示例结构化为两个独立的代码段。第一段是读取文档、调用修改代码和编写已修改文档的代码。第二段是真正遍历文档表示和执行修改的递归方法。为避免分散注意力,我已在代码中忽略了异常处理。

您可以从本页底部参考资料一节链接到下载页,以获取所有样本的完整代码。样本的下载版本包括一个测试驱动程序,还有一些添加的代码用于通过计算元素、删除和添加的个数来检查不同模型的操作。

即使您不想使用 DOM 实现,但还是值得浏览下面对 DOM 用法的描述。因为 DOM 示例是第一个示例,所以与后面的模型相比,我用它来探究有关该示例的一些问题和结构的更详细信息。浏览这些内容可以补充您想知道的一些细节,如果直接阅读其它模型之一,那么将错过这些细节。

DOM
DOM 规范涵盖了文档表示的所有类型的操作,但是它没有涉及例如对文档的语法分析和生成文本输出这样的问题。包括在性能测试中的两种 DOM 实现,Xerces 和 Crimson,对这些操作使用不同的技术。清单 1 显示了 Xerces 的顶级代码的一种形式。

清单 1. Xerces DOM 顶级代码
1 // parse the document from input stream ("in")
2 DOMParser parser = new DOMParser();
3 parser.setFeature("http://xml.org/sax/features/namespaces", true);
4 parser.parse(new InputSource(in));
5 Document doc = parser.getDocument();

6 // recursively walk and modify document
7 modifyElement(doc.getDocumentElement());

8 // write the document to output stream ("out")
9 OutputFormat format = new OutputFormat(doc);
10 XMLSerializer serializer = new XMLSerializer(out, format);
11 serializer.serialize(doc.getDocumentElement());



正如我在注释中指出的,清单 1 中的第一块代码(第 1-5 行)处理对输入流的语法分析,以构建文档表示。Xerces 定义了 DOMParser 类,以便从 Xerces 语法分析器的输出构建文档。InputSource 类是 SAX 规范的一部分,它能适应供 SAX 分析器使用的几种输入形式的任何之一。通过单一调用进行实际的语法分析和文档构造,如果成功完成了这一操作,那么应用程序就可以检索并使用已构造的 Document。

第二个代码块(第 6-7 行)只是将文档的根元素传递给我马上要谈到的递归修改方法。这些代码与本文中所有文档模型的代码在本质上是相同的,所以在剩余的示例中我将跳过它,不再做任何讨论。

第三个代码块(第 8-11 行)处理将文档作为文本写入输出流。这里,OutputFormat 类包装文档,并为格式化生成的文本提供了多种选项。XMLSerializer 类处理输出文本的实际生成。

Xerces 的 modify 方法只使用标准 DOM 接口,所以它还与任何其它 DOM 实现兼容。清单 2 显示了代码。

清单 2. DOM Modify 方法
1 protected void modifyElement(Element element) {

2 // loop through child nodes
3 Node child;
4 Node next = (Node)element.getFirstChild();
5 while ((child = next) != null) {

6 // set next before we change anything
7 next = child.getNextSibling();

8 // handle child by node type
9 if (child.getNodeType() == Node.TEXT_NODE) {

10 // trim whitespace from content text
11 String trimmed = child.getNodeValue().trim();
12 if (trimmed.length() == 0) {

13 // delete child if nothing but whitespace
14 element.removeChild(child);

15 } else {

16 // create a "text" element matching parent namespace
17 Document doc = element.getOwnerDocument();
18 String prefix = element.getPrefix();
19 String name = (prefix == null) ? "text" : (prefix + ":text");
20 Element text =
21 doc.createElementNS(element.getNamespaceURI(), name);

22 // wrap the trimmed content with new element
23 text.appendChild(doc.createTextNode(trimmed));
24 element.replaceChild(text, child);

25 }
26 } else if (child.getNodeType() == Node.ELEMENT_NODE) {

27 // handle child elements with recursive call
28 modifyElement((Element)child);

29 }
30 }
31 }



清单 2 中显示的方法所使用的基本方法与所有文档表示的方法相同。 通过一个元素调用它,它就依次遍历那个元素的子元素。如果找到文本内容子元素,要么删除文本(如果它只是由空格组成的),要么通过与包含元素相同的名称空间中名为“text”的新元素来包装文本(如果有非空格的字符)。如果找到一个子元素,那么这个方法就使用这个子元素,递归地调用它本身。

对于 DOM 实现,我使用一对引用:child 和 next 来跟踪子元素排序列表中我所处的位置。在对当前子节点进行任何其它处理之前,先装入下个子节点的引用(第 7 行)。这样做使得我能够删除或替代当前的子节点,而不丢失我在列表中的踪迹。

当我创建一个新元素来包装非空白的文本内容(第 16-24 行)时,DOM 接口开始有点杂乱。用来创建元素的方法与文档关联并成为一个整体,所以我需要在所有者文档中检索当前我正在处理的元素(第 17 行)。我想将这个新元素放置在与现有的父元素相同的名称空间中,并且在 DOM 中,这意味着我需要构造元素的限定名称。根据是否有名称空间的前缀,这个操作会有所不同(第 18-19 行)。利用新元素的限定名称,以及现有元素中的名称空间 URI,我就能创建新元素(第 20-21 行)。

一旦创建了新元素,我只要创建和添加文本节点来包装内容 String,然后用新创建的元素来替代原始文本节点(第 22-24 行)。

清单 3. Crimson DOM 顶级代码
1 // parse the document from input stream
2 System.setProperty("javax.xml.parsers.DocumentBuilderFactory",
3 "org.apache.crimson.jaxp.DocumentBuilderFactoryImpl");
4 DocumentBuilderFactory dbf = DocumentBuilderFactoryImpl.newInstance();
5 dbf.setNamespaceAware(true);
6 DocumentBuilder builder = dbf.newDocumentBuilder();
7 Document doc = builder.parse(in);

8 // recursively walk and modify document
9 modifyElement(doc.getDocumentElement());

10 // write the document to output stream
11 ((XmlDocument)doc).write(out);



清单 3 中的 Crimson DOM 示例代码使用了用于语法分析的 JAXP 接口。JAXP 为语法分析和转换 XML 文档提供了一个标准化的接口。本示例中的语法分析代码还可以用于 Xerces(对文档构建器类名称的特性设置有适当的更改)来替代较早给定的 Xerces 特定的示例代码。

在本示例中,我首先在第 2 行到第 3 行中设置系统特性来选择要构造的 DOM 表示的构建器工厂类(JAXP 仅直接支持构建 DOM 表示,不支持构建本文中讨论的任何其它表示)。仅当想选择一个要由 JAXP 使用的特定 DOM 时,才需要这一步;否则,它使用缺省实现。出于完整性起见,我在代码中包含了设置这个特性,但是更普遍的是将它设置成一个 JVM 命令行参数。

接着我在第 4 行到第 6 行中创建构建器工厂的实例,对使用那个工厂实例构造的构建器启用名称空间支持,并从构建器工厂创建文档构建器。最后(第 7 行),我使用文档构建器来对输入流进行语法分析并构造文档表示。

为了写出文档,我使用 Crimson 中内部定义的基本方法。不保证在 Crimson 未来版本中支持这个方法,但是使用 JAXP 转换代码来将文档作为文本输出的替代方法需要诸如 Xalan 那样的 XSL 处理器的。那超出了本文的范围,但是要获取详细信息,可以查阅 Sun 中的 JAXP 教程。

JDOM
使用 JDOM 的顶级代码比使用 DOM 实现的代码稍微简单一点。为构建文档表示(第 1-3 行),我使用带有由参数值禁止验证的 SAXBuilder。通过使用提供的 XMLOutputter 类,将已修改的文档写入输出流同样简单(第 6-8 行)。

清单 4. JDOM 顶级代码
1 // parse the document from input stream
2 SAXBuilder builder = new SAXBuilder(false);
3 Document doc = builder.build(in);

4 // recursively walk and modify document
5 modifyElement(doc.getRootElement());

6 // write the document to output stream
7 XMLOutputter outer = new XMLOutputter();
8 outer.output(doc, out);



清单 5 中 JDOM 的 modify 方法也比 DOM 的同一方法简单。我获取包含元素所有内容的列表并扫描了这张列表,检查文本(象 String 对象那样的内容)和元素。这张列表是“活的”,所以我能直接对它进行更改,而不必调用父元素上的方法。

清单 5. JDOM modify 方法
1 protected void modifyElement(Element element) {

2 // loop through child nodes
3 List children = element.getContent();
4 for (int i = 0; i < children.size(); i++) {

5 // handle child by node type
6 Object child = children.get(i);
7 if (child instanceof String) {

8 // trim whitespace from content text
9 String trimmed = child.toString().trim();
10 if (trimmed.length() == 0) {

11 // delete child if only whitespace (adjusting index)
12 children.remove(i--);

13 } else {

14 // wrap the trimmed content with new element
15 Element text = new Element("text", element.getNamespace());
16 text.setText(trimmed);
17 children.set(i, text);

18 }
19 } else if (child instanceof Element) {

20 // handle child elements with recursive call
21 modifyElement((Element)child);

22 }
23 }
24 }



创建新元素的技术(第 14-17 行)非常简单,而且与 DOM 版本不同,它不需要访问父文档。

dom4j
dom4j 的顶级代码比 JDOM 的稍微复杂些,但是它们的代码行非常类似。这里的主要区别是我保存了用来构建 dom4j 文档表示的 DocumentFactory(第 5 行),并在输出已修改的文档文本之后刷新了 writer(第 10 行)。

清单 6. dom4j 的顶级代码
1 // parse the document from input stream
2 SAXReader reader = new SAXReader(false);
3 Document doc = reader.read(in);

4 // recursively walk and modify document
5 m_factory = reader.getDocumentFactory();
6 modifyElement(doc.getRootElement());

7 // write the document to output stream
8 XMLWriter writer = new XMLWriter(out);
9 writer.write(doc);
10 writer.flush();



正如您在清单 6 中看到的,dom4j 使用一个工厂方法来构造文档表示(从语法分析构建)中包含的对象。根据接口来定义每个组件对象,所以实现其中一个接口的任何类型的对象都能包含在表示中(与 JDOM 相反,它使用具体类:这些类在某些情况中可以划分子类和被继承,但是在文档表示中使用的任何类都需要以原始 JDOM 类为基础)。通过使用不同工厂进行 dom4j 文档构建,您能获取不同系列的组件中构造的文档。

在样本代码(第 5 行)中,我检索了用于构建文档的(缺省)文档工厂,并将它存储在一个实例变量(m_factory)中以供 modify 方法使用。并不严格需要这一步 — 可以在一个文档中同时使用来自不同工厂的组件,或者可以绕过工厂而直接创建组件的实例 — 但在该例中,我只想创建与在文档其余部分中使用的同一类型的组件,并且使用相同的工厂来确保完成这个步骤。

清单 7. dom4j modify 方法
1 protected void modifyElement(Element element) {

2 // loop through child nodes
3 List children = element.content();
4 for (int i = 0; i < children.size(); i++) {

5 // handle child by node type
6 Node child = (Node)children.get(i);
7 if (child.getNodeType() == Node.TEXT_NODE) {

8 // trim whitespace from content text
9 String trimmed = child.getText().trim();
10 if (trimmed.length() == 0) {

11 // delete child if only whitespace (adjusting index)
12 children.remove(i--);

13 } else {

14 // wrap the trimmed content with new element
15 Element text = m_factory.createElement
16 (QName.get("text", element.getNamespace()));
17 text.addText(trimmed);
18 children.set(i, text);

19 }
20 } else if (child.getNodeType() == Node.ELEMENT_NODE) {

21 // handle child elements with recursive call
22 modifyElement((Element)child);

23 }
24 }
25 }



清单 7 中 dom4j modify 方法与 JDOM 中使用的方法非常类似。不通过使用 instanceof 运算符来检查内容项的类型,我可以通过 Node 接口方法 getNodeType 来获取类型代码(也可以使用 instanceof,但类型代码方法看起来更清晰)。通过使用 QName 对象来表示元素名称和通过调用已保存的工厂的方法来构建元素可以区别新元素的创建技术(第 15-16 行)。

Electric XML
清单 8 中 Electric XML(EXML)的顶级代码是任何这些示例中最简单的一个,通过单一方法调用就可以读取和编写文档。

清单 8. EXML 顶级代码
1 // parse the document from input stream
2 Document doc = new Document(in);

3 // recursively walk and modify document
4 modifyElement(doc.getRoot());

5 // write the document to output stream
6 doc.write(out);



清单 9 中 EXML modify 方法尽管与 JDOM 一样,需要使用 instanceof 检查,但它与 DOM 方法最相似。在 EXML 中,无法创建一个带名称空间限定的名称的元素,所以取而代之,我创建新元素,然后设置其名称来达到相同的效果。

清单 9. EXML modify 方法
1 protected void modifyElement(Element element) {

2 // loop through child nodes
3 Child child;
4 Child next = element.getChildren().first();
5 while ((child = next) != null) {

6 // set next before we change anything
7 next = child.getNextSibling();

8 // handle child by node type
9 if (child instanceof Text) {

10 // trim whitespace from content text
11 String trimmed = ((Text)child).getString().trim();
12 if (trimmed.length() == 0) {

13 // delete child if only whitespace
14 child.remove();

15 } else {

16 // wrap the trimmed content with new element
17 Element text = new Element();
18 text.addText(trimmed);
19 child.replaceWith(text);
20 text.setName(element.getPrefix(), "text");

21 }
22 } else if (child instanceof Element) {

23 // handle child elements with recursive call
24 modifyElement((Element)child);

25 }
26 }
27 }



XPP
XPP 的顶级代码(在清单 10 中)是所有示例中最长的一个,与其它模型相比,它需要相当多的设置。

清单 10. XPP 顶级代码
1 // parse the document from input stream
2 m_parserFactory = XmlPullParserFactory.newInstance();
3 m_parserFactory.setNamespaceAware(true);
4 XmlPullParser parser = m_parserFactory.newPullParser();
5 parser.setInput(new BufferedReader(new InputStreamReader(in)));
6 parser.next();
7 XmlNode doc = m_parserFactory.newNode();
8 parser.readNode(doc);

9 // recursively walk and modify document
10 modifyElement(doc);

11 // write the document to output stream
12 XmlRecorder recorder = m_parserFactory.newRecorder();
13 Writer writer = new OutputStreamWriter(out);
14 recorder.setOutput(writer);
15 recorder.writeNode(doc);
16 writer.close();



因为使用 JAXP 接口,所以我必须首先创建分析器工厂的实例并在创建分析器实例之前启用名称空间处理(第 2-4 行)。一旦获取了分析器实例,我就能将输入设置到分析器中,并真正构建文档表示(第 5-8 行),但是这涉及比其它模型更多的步骤。

输出处理(第 11-16 行)也涉及比其它模型更多的步骤,主要因为 XPP 需要 Writer 而不是直接将 Stream 作为输出目标接受。

清单 11 中 XPP modify 方法尽管需要更多代码来创建新元素(第 13-21 行),但它与 JDOM 方法最类似。名称空间处理在这里有点麻烦。我首先必须创建元素的限定名称(第 15-16 行),然后创建元素,最后在稍后设置名称和名称空间 URI(第 18-21 行)。

清单 11. XPP modify 方法
1 protected void modifyElement(XmlNode element) throws Exception {

2 // loop through child nodes
3 for (int i = 0; i < element.getChildrenCount(); i++) {

4 // handle child by node type
5 Object child = element.getChildAt(i);
6 if (child instanceof String) {

7 // trim whitespace from content text
8 String trimmed = child.toString().trim();
9 if (trimmed.length() == 0) {

10 // delete child if only whitespace (adjusting index)
11 element.removeChildAt(i--);

12 } else {

13 // construct qualified name for wrapper element
15 String prefix = element.getPrefix();
16 String name = (prefix == null) ? "text" : (prefix + ":text");

17 // wrap the trimmed content with new element
18 XmlNode text = m_parserFactory.newNode();
19 text.appendChild(trimmed);
20 element.replaceChildAt(i, text);
21 text.modifyTag(element.getNamespaceUri(), "text", name);

22 }
23 } else if (child instanceof XmlNode) {

24 // handle child elements with recursive call
25 modifyElement((XmlNode)child);

26 }
27 }
28 }



结束语
DOM、dom4j 和 Electric XML 都得到这些几乎同样易于使用的代码样本,其中 EXML 可能最简单,而 dom4j 受一些小条件限制而较困难。DOM 提供了与语言无关的非常实在的好处,但是如果你只使用 Java 代码,那么通过与 Java 特定的模型相比较,它看上去有点麻烦。我认为这表明 Java 特定的模型通常成功地实现简化 Java 代码中的 XML 文档处理这个目标。

超越基础:真实世界可用性
代码样本显示 JDOM 和 EXML 为基本文档操作(使用元素、属性和文本)提供了简单和清晰的接口。根据我的经验,它们的方法并不能很好地完成处理整个文档表示的编程任务。要完成这些类型的任务,DOM 和 dom4j 使用的组件方法 — 其中从属性到名称空间的所有文档组件实现一些公共接口 — 工作得更好。
相关的例子是最近我为 JDOM 和 dom4j 实现的 XML 流型(XML Streaming (XMLS) )编码。这个代码遍历整个文档并编码每个组件。JDOM 实现比 dom4j 实现复杂得多,主要是因为 JDOM 使用一些没有公共接口的独特类来表示每个组件。

因为 JDOM 缺少公共接口,所以即使处理 Document 对象的代码与处理 Element 对象的代码都有一些诸如子组件那样相同类型的组件,但是它们必须有所不同。还需要特殊方法来检索与其它类型的子组件相对的 Namespace 组件。甚至当处理被认为是内容的子组件类型时,您需要在组件类型上使用多个带 instanceof 检查的 if 语句,而不是使用一条更清晰更快速的 switch 语句。

具有讽刺意味的可能是 JDOM 的最初目标之一是利用 Java Collection 类,这些类本身在很大程度上以接口为基础。库中接口的使用增加了许多灵活性,而这是以增加了一些复杂性为代价的,并且这对于为重用而设计的代码来说,通常是一个很好的折衷。这可能还主要归功于 dom4j,它达到一个成熟并且稳定的状态,比 JDOM 要快得多。

尽管如此,对于使用多种语言的开发人员来说,DOM 仍是一个非常好的选择。DOM 实现广泛应用于多种编程语言。它还是许多其它与 XML 相关的标准的基础,所以即使您使用 Java 特定的模型,也还有一个您逐步熟悉 DOM 所需要的好机会。因为它正式获得 W3C 推荐(与基于非标准的 Java 模型相对),所以在某些类型的项目中可能也需要它。

就使用方便这一范畴而言,在 JDOM、dom4j 和 Electric XML 这三个主要竞争者中,dom4j 与其它两个的区别在于它使用带有多个继承层的基于接口的方法。这会使得遵循 API JavaDocs 更为困难些。例如,您正在寻找的一个方法(例如 content(),在我们 dom4j 的 modify 方法示例的第 3 行中使用的)可能是 Element 扩展的 Branch 接口的一部分,而不是 Element 接口本身的一部分。尽管如此,这种基于接口的设计添加了许多灵活性(请参阅侧栏超越基础:真实世界可用性)。考虑到 dom4j 的性能、稳定性和特性设置的优点,您应把它当作多数项目中的一个有力的候选者。

在任一 Java 特定的文档模型之中,JDOM 可能拥有最广泛的用户基础,并且它的确是使用起来最简单的模型之一。尽管如此,作为项目开发的一个选择,它还是必须容忍 API 的不固定性和从一个版本到下一个版本的更新,在性能对比中它也表现得很糟糕。基于当前实现,我愿为着手新项目的人们推荐 dom4j,而不是 JDOM。

除了 XPP 以外,EXML 比其它任何模型占用的资源都要少得多,并且考虑到 EXML 易于使用的优点,您应肯定会认为它适用于 jar 文件大小很重要的应用程序。但是,EXML 的 XML 支持的局限性和受限的许可证,以及在较大文件上所表现出的相对拙劣的性能,不得不在许多应用程序中放弃使用它。

XPP 在语法分析和编写文本文档时需要更多步骤,并且在处理名称空间时也需要更多步骤。如果 XPP 打算添加一些便利的方法来处理其中一些常见情况,那么在对比中它可能会更胜一筹。正如它现在所表现的,上篇文章中性能方面的领先者却成了本文中的可用性方面的失败者。尽管如此,因为 XPP 性能方面的优势,所以对于需要较小的 jar 文件大小的应用程序还是值得将它作为 EXML 的替代方法。

下一次...
到目前为止在我写的两篇文章中,涉及到用 Java 编写的 XML 文档模型的性能和可用性。在本系列的后两篇文章中,我将讨论用 Java 技术进行 XML 数据绑定的方法。这些方法与文档模型的方法有许多相似处,但是它们更进一步将 XML 文档映射到实际应用程序数据结构中。我们将看到这一操作在使用的简便性和提高性能方面是如何做得如此好的。

回到 developerWorks,检查 Java 代码的 XML 数据绑定的实质。同时,您可以通过下面链接的论坛,给出您对本文的评论和问题。

参考资料

单击本文顶部或底部的讨论,参与本文的论坛。
如果您需要背景资料,尝试 developerWorks 的 XML Java 编程 教程、理解 SAX 教程和 理解 DOM 教程。
从下载页面下载本文中使用的测试程序和文档模型库。
查找有关 Java APIs for XML Processing (JAXP) 或者阅读 JAXP Tutorial。
获取作者有关 XML Streaming 的著作的详细信息,它作为程序间传送 XML 文档的 Java 序列化的另一种选择。
回顾作者以前的文章:XML in Java: Document models, part 1。
根据 Tony Darugar 的团队对几个大型 XML 项目的分析,参考他对 Effective DOM with Java 的建议。
Java 的 XML 文档模型:
Xerces Java
Crimson
JDOM
dom4j
Electric XML
XML Pull Parser (XPP)



原文:http://www.daima.com.cn/Info/127/Info37907/
posted @ 2006-04-03 10:30 hopeshared 阅读(537) | 评论 (0)编辑 收藏

我找到的两种方法,希望大家补充

第一种:利用Action
IWorkbenchWindow window = getViewSite().getWorkbenchWindow();
IWorkbenchAction max = ActionFactory.MAXIMIZE.create(window);
max.run();


这段代码要放在何适的位置上才会起到合适的作用。

第二种:利用Zoom
在ApplicationWorkbenchWindowAdvisor#postWindowOpen中做到
public void postWindowOpen() {
     IWorkbenchWindow window = Plugin.getDefault().getWorkbench().getActiveWorkbenchWindow();
     IViewPart part=null;
     try {
         part = window.getActivePage().showView(View.ID);
     } catch (PartInitException e) {
         e.printStackTrace();
     }
  
     if(part!=null){
        window.getActivePage().activate(part); 
        window.getActivePage().bringToTop(part);
  
        WorkbenchPage realPage = (WorkbenchPage) window.getActivePage();

        IWorkbenchPartReference partRef = window.getActivePage().getActivePartReference();

        if (partRef != null) {
            ((WorkbenchPage) window.getActivePage()).toggleZoom(partRef);
        }else{
         System.out.println("partRef is null!");
        }
    }
}

posted @ 2006-03-27 17:10 hopeshared 阅读(1741) | 评论 (1)编辑 收藏

仅列出标题
共30页: First 上一页 14 15 16 17 18 19 20 21 22 下一页 Last