深入了解Dojo的Collections工具包

Dojox 的 Collections 工具,一个模拟 Java 的某些实用数据结构的工具包,如:List(ArrayList)、Set、Dictionary、Queue、Stack、BinaryTree 等。这些工具对那些需要用到一些高级 Collections 功能的开发者非常有用。如果您是一位 Java 开发者,您一定对这些数据结构非常熟悉,基于这些工具进行 web 应用开发也将会游刃有余的多。这篇文章将主要来介绍 Dojox.collections 的特性以及一些使用上的技巧。

简介

Dojo 的 collections 工具包的存在主要是提供给开发人员一些比起基本的集合对象(collections)来说,功能更为丰富,使用起来也更加方便的集合对象,如 ArrayList、Dictionaries 等等。Dojo 的这些 collections 对象有点类似于 Java 的 collection 工具集(如“java.util.Collection”接口的那些派生类),并且同 Java 的类库一样,它们有比较完善的接口,性能上也做过优化。在 web 开发中使用这些对象会带给我们比较理想的效果。

回页首

Dojo 的 Collection 工具包基础

由于我们接下要介绍的主要是集合对象,所以我们需要先了解一下相关的迭代器和元素对象,这也就是 collection 的基础内容。

DictionaryEntry

所有关于 Dictionary 类集合的迭代元素均为 DictionaryEntry。参考如下代码:


清单 1. DictionaryEntry
				   var d=new dojox.collections.DictionaryEntry("foo","bar");   t.assertEqual("bar", d.valueOf());   t.assertEqual("bar", d.toString());  

其中“t.assertEqual”是 Dojo 中用来判断两个值是否相等的,根据代码可以看出,“DictionaryEntry”有键(key)和值(value)组成,它有两个主要的方法:“valueOf”和“toString”,均可以取得它的值。

Iterator

Iterator 是一个迭代器,相信大家都不陌生了吧,很多编程语言里面都有同名的迭代器,虽然 JavaScript 并没有,不过 Dojo 模拟了一个。参考如下代码:


清单 2. Iterator
				   var itr=new dojox.collections.Iterator(["foo","bar","baz","zoo"]);   t.assertEqual("foo", itr.element);//test initialization   t.assertTrue(!itr.atEnd());   t.assertEqual("foo", itr.get());//make sure the first get doesn't advance.   t.assertEqual("bar", itr.get());   t.assertEqual("baz", itr.get());   t.assertEqual("zoo", itr.get());   t.assertTrue(itr.atEnd());   t.assertEqual(null, itr.get());  

其用法和其它语言的 Iterator 类似,“itr.element”适用于取得当前元素,即 Iterator 迭代指针当前指向的元素,注意:这种用“.element”取值的方式不会造成迭代指针 的位移,指针指向的位置不变。但是,“get()”方法就不是了,每调用一次“get”方法,除了返回当前元素外,还会让指针后移一位,当指针移到末尾 时,会有“itr.atEnd()”为真,此时的“itr.get()”则返回 null。

除此以外 Iterator 还支持“map”操作,其本质就是“dojo.map”,参考如下代码:


清单 3. Iterator 配合 map
				   var a=itr.map(function(elm){   return elm+"-mapped";   });  

同“dojo.map”一样,这里的 Iterator 里面的每个节点的内容都加上了“-mapped”的尾椎。还有:“reset”方法可以让指针回到起始位置,进行新的迭代。

DictionaryIterator

想必大家根据名字就能看出来,这个是专门迭代 Dictionary 类集合的,参考如下代码:


清单 4. DictionaryIterator
				   var itr=new dojox.collections.DictionaryIterator({   first:"foo", second:"bar", third:"baz", fourth:"zoo"  });   t.assertEqual("foo", itr.element);//test initialization   t.assertTrue(!itr.atEnd());   t.assertEqual("foo", itr.get());//make sure the first get doesn't advance.   t.assertEqual("bar", itr.get());   t.assertEqual("baz", itr.get());   t.assertEqual("zoo", itr.get());   t.assertTrue(itr.atEnd());   t.assertEqual(null, itr.get());  

这里似乎和 Iterator 类似,事实上,DictionaryIterator 的迭代主要是基于“值”的迭代,所以这里的使用方式和返回值几乎相同,而且 DictionaryIterator 也支持“map”操作。

接下来我们来看看 Dojo 所支持的各种集合对象。

回页首

ArrayList

做过 Java 开发的软件工程师对这个对象应该再熟悉不过了。我们来看看它的一些示例代码:


清单 5. ArrayList 基本操作
				   var al=new dojox.collections.ArrayList(["foo","bar","test","bull"]);   t.assertEqual(4, al.count);   al.add("carp");   t.assertEqual("foo,bar,test,bull,carp", al.toString());   al.addRange(["oof","rab"]);   t.assertEqual("foo,bar,test,bull,carp,oof,rab", al.toString());  

“count”代表元素数量,“add”用于添加元素,“addRange”可以添加一系列元素(其传入值为数组),它的 “toString”方法相当于 JavaScript 的“join(",")”。大家是不是有一种似曾相识的感觉?不错,这些方法和返回结果与我们之前用过的 Java 的 Util 工具包里面的 ArrayList 类非常相似。所以,如果您基于 Dojo 的 ArrayList 做开发,可能您根本用不着了解 JavaScript 的相关语法和接口。

当然,不止这些接口,Dojo 的 ArrayList 还支持很多其它常用接口,如 clone、clear、indexOf 甚至 contains,相信大家在 Java 里面也用过,这里就不赘述了。

接下来我们来看看 Dojo 的 ArrayList 的一些高级的操作方法:


清单 6. ArrayList 高级操作
				   var al=new dojox.collections.ArrayList(["foo","bar","test","bull"]);   t.assertEqual("test", al.item(2));    var al=new dojox.collections.ArrayList(["foo","bar","test","bull"]);   al.insert(2, "baz");   t.assertEqual(2, al.indexOf("baz"));    var al=new dojox.collections.ArrayList(["foo","bar","test","bull"]);   al.remove("bar");   t.assertEqual("foo,test,bull", al.toString());    var al=new dojox.collections.ArrayList(["foo","bar","test","bull"]);   al.removeAt(3);   t.assertEqual("foo,bar,test", al.toString());  

“item”方法用于取值,它使得对于像 ArrayList 这样的集合同样可以通过“数组”的方式来取值,“item(2)”表示取第 2 个元素。同样,插入,删除等等操作也是支持的:“insert(2, "baz")”表示在第 2 位插入“baz”。删除可以通过两种方式:“remove”和“removeAt”,一个通过元素值定位,一个通过下标定位。

还有“reverse”和“sort”方法,一个用于集合的反转,一个用于排序,推荐大家关注一下。

最后,我们来看看它的迭代器用法:


清单 7. ArrayList 的迭代器
				   var al=new dojox.collections.ArrayList(["foo","bar","test","bull"]);   var itr=al.getIterator();   while(!itr.atEnd()){   itr.get();   }   t.assertEqual("bull", itr.element);  

和很多其它语言一样,通过“getIterator”拿到迭代器进行迭代,清单 7 中的代码是不是非常熟悉,我们好像写过太多类似的代码了。

回页首

BinaryTree

BinaryTree 也是一个线性结构,它的优势在于它的检索。它的基本操作(包括增删查改)与 ArrayList 基本无异,这里我们主要介绍一下它的检索操作:


清单 8. BinaryTree 检索
				   var bt=new dojox.collections.BinaryTree("foo");   bt.add("bar");   bt.add("baz");   bt.add("buck");   bt.add("shot");   bt.add("apple");   t.assertEqual("buck", bt.search("buck").value);  

“bt.search("buck")”这里得到的不是值本身,而是一个节点对象,它有“left”,“right”和“value”等等属性,这里的“value”就是它的值。

BinaryTree 的接口很简单,但是里面的实现比较丰富。每一个外部的“add”操作都会促使它进行二叉树的构造,用于以后的查找。所以它的“search”(包括 “contains”)方法的效率非常高,复杂度应该是 O(logn),而 ArrayList(“contains”方法)的复杂度应为 O(n)。但是这样也有缺点:就是 BinaryTree 的 add、deleteData 等方法需要消耗更多的时间。可以说,BinaryTree 比较适合存储并检索大量数据。

我们来参考一下 BinaryTree 的 add 方法的源代码:


清单 9. BinaryTree 的 add 方法实现
				   this.add=function(data){ 	var n=new node(data); 	var i; 	var current=root; 	var parent=null;  //定位待插入元素在二叉树中的插入位置 	while(current){ 		i=current.compare(n); 		if(i==0){ return; } 		parent=current; 		if(i>0){ current=current.left; } 		else{ current=current.right; } 	} 	this.count++;  //插入待插入元素到相应位置 	if(!parent){ 		root=n; 	}else{ 		i=parent.compare(n); 		if(i>0){ 			parent.left=n; 		}else{ 			parent.right=n; 		} 	} };  

参照代码中的注释,可以看出 BinaryTree 的每一个外部的“add”操作是如何进行二叉树的构造的。

回页首

Queue & Stack

Queue 和 Stack 也都是线性结构,我们学数据结构时可能对 Queue 和 Stack 就已经了如指掌了。Queue 是先进先出,Stack 是先进后出,或者说后进先出。它们的基本操作也都类似 ArrayList 的那些接口,下面我们来简单说说它们的特殊接口:


清单 10. Queue 示例
				   var q=new dojox.collections.Queue(["foo","bar","test","bull"]);   q.enqueue("bull");   t.assertEqual("bull", q.toArray().pop());    var q=new dojox.collections.Queue(["foo","bar","test","bull"]);   t.assertEqual("foo", q.dequeue());   t.assertEqual("bar,test,bull", q.toArray().join(","));  

“enqueue”表示入栈,“dequeue”表示出栈,可见 Queue 是先进先出的。如果仅仅想取值,请用“peek”方法。


清单 11. Stack 示例
				   var s=new dojox.collections.Stack(["foo","bar","test","bull"]);   t.assertEqual("bull", s.pop());   t.assertEqual("test", s.pop());    var s=new dojox.collections.Stack(["foo","bar","test","bull"]);   s.push("bug");   t.assertEqual("bug", s.peek());  

“push”表示入栈,“pop”表示出栈,“s.peek()”出的值为“bug”表示 Stack 为后进先出的线性结构。如果仅仅想取值,也请用“peek”。

回页首

Set

相信我们对 Set 这个对象已经不陌生了,它的最大的特点就是元素的唯一性。同样,Dojo 中的 Set 也是如此,但是 Dojo 中的 Set 的主要用法不是构建 Set 对象,而是 Set 本身的一些方法:


清单 12. Set 的方法
				   var dxcs=dojox.collections.Set;    var a = ["apple","bear","candy","donut","epiphite","frank"];   var b = ["bear","epiphite","google","happy","joy"];     var union=dxcs.union(a,b);   t.assertEqual("apple,bear,candy,donut,epiphite,frank,google,happy,joy",                                               union.toArray().join(','));                                                 var itsn=dxcs.intersection(a,b);   t.assertEqual("bear,epiphite", itsn.toArray().join(","));   t.assertEqual("bear", dxcs.intersection(["bear","apple"], ["bear"]));     var d=dxcs.difference(a,b);   t.assertEqual("apple,candy,donut,frank",d.toArray().join(','));     t.assertFalse(dxcs.isSubSet(a,["bear","candy"]));   t.assertTrue(dxcs.isSubSet(["bear","candy"],a));     t.assertTrue(dxcs.isSuperSet(a,["bear","candy"]));   t.assertFalse(dxcs.isSuperSet(["bear","candy"],a));  

代码可能有点多,我们来一一结介绍:

  1. 注意 dxcs 是指代的 Dojo 的 Set 类 -- “dojox.collections.Set”。关注“a”和“b”两个集合。
  2. “union”表示联合,但是 Set 的联合方法会去除重复元素。
  3. “intersection”是做交叉,选出两个集合里面具有的元素,然后组成集合。
  4. “difference”是取得集合“a”中存在但集合“b”中不存在的元素。
  5. “isSubSet”和“isSuperSet”分别指“是否为子集”和“是否为超集”。

回页首

Dictionary

之前其实我们已经简单了解过了,它的实类似于“java.util.Dictionary”。它的基本操作也是与 ArrayList 类似,但不同的是它是 map 结构,有两个属性:“key”和“value”,不同于简单线性结构(如:ArrayList)。我们先来看看以下实例:


清单 13. Dictionary 的方法
				   var d=new dojox.collections.Dictionary();    d.add("foo","bar");   d.add("baz","fab");   d.add("buck","shot");   d.add("apple","orange");    t.assertTrue(d.containsKey("buck"));    t.assertTrue(d.containsValue("shot"));    t.assertEqual("foo,baz,buck,apple", d.getKeyList().join(","));    t.assertEqual("bar,fab,shot,orange", d.getValueList().join(","));    d.remove("baz");   t.assertEqual(3, d.count);   t.assertEqual(undefined, d.item("baz"));  

可以看出,这里添加元素还是用“add”,但是不再是添加值,而是添加“ 键 - 值 ”对。它的“contains”方法也可分为“containsKey”和“containsValue”两种。同样,也有“getKeyList”和 “getValueList”两种。当调用“remove”方法删除元素以后,Dictionary 的长度也会变化,并且删除的值变为“undefined”。

回页首

SortedList

SortedList 和 Dictionary 有很相似性,不同的是,该线性结构里的元素是排好序(基于键“key”排序)存储的。


清单 14. SortedList 的方法
				   var sl=new dojox.collections.SortedList();    sl.add("foo","bar");   sl.add("baz","fab");   sl.add("buck","shot");   sl.add("apple","orange");    t.assertEqual("shot", sl.getByIndex(2));    t.assertEqual("apple", sl.getKey(0));    t.assertEqual(0, sl.indexOfKey("apple"));    t.assertEqual(3, sl.indexOfValue("bar"));    sl.setByIndex(0, "bar");   t.assertEqual("bar", sl.getByIndex(0));  

SortedList 的方法:“containsKey”,“containsValue”,“getKeyList”,“getValueList”和“remove”等等这里就不一一列举了。我们来看一下它特有的方法:

  1. 注意到,虽然它是按“foo”,“baz”,“buck”,“apple”的顺序添加,但是它的内部顺序 为:“apple”,“baz”,“buck”,“foo”,所以 value 的顺序应为:“orange”,“fab”,“shot”,“bar”。基于这种机制,我们就能了解它的一些接口的返回值。
  2. “getByIndex(2)”应该是返回第三个元素的 value,所以为“shot”。
  3. “getKey(0)”自然是第一个元素的 key,所以为“apple”。
  4. “indexOfKey("apple")”返回元素“apple”的 index,为 0;“indexOfValue”返回 value 元素“bar”的位置,为第四个元素,即 index 为 3。
  5. “setByIndex”和“getByIndex”是根据位置设置值和取值。

可以看出,SortedList 的接口是很全面的,建议大家在项目中多使用。

回页首

结束语

这篇文章介绍了 Dojo 中关于 Collections 集合对象的一些特性,从 collection 工具包的基础出发,依次介绍了 ArrayList、BinaryTree、Queue、Stack、Set、Dictionary 以及 SortedList 等,介绍了它们的基本接口,高级接口和一些特殊接口。同时,也阐述了这些不同集合对象之间的相同点和不同点。本文主要是基于实际的代码示例来说明这些集合 对象的用法,简明直观,推荐大家在日常开发中多参考。


参考资料

学习

  • Dojo 校园文档主页,Dojo 中控件的比较完全的 API 文档主页,包括 Dojo,Dijit,Dojox 等等。

  • Dojo 官方文档主页,Dojo 官方的很多支持 Ajax 应用程序开发的组件的文档。

  • Dojo 技术专题” (developerWorks,2009 年 12 月):伴随 Web 2.0,Ajax 和 RIA 的热潮,各种 JavaScript 开发工具包如雨后春笋般蓬勃发展,Dojo 正是这些工具包中的佼佼者。Dojo 为富互联网应用程序(RIA)的开发提供了完整的端到端的解决方案,包括核心的 JavaScript 库,简单易用的小部件(Widget)系统和一个测试框架,此外,Dojo 的开源开发社区也在不停地为它提供新的功能。本专题汇集了与 Dojo 相关的技术资源。

  • 找出并解决 JavaScript 和 Dojo 引起的浏览器内存泄露问题” (developerWorks,2012 年 4 月):如果大量使用 JavaScript 和 Ajax 技术开发 Web 2.0 应用程序,您很有可能会遇到浏览器的内存泄漏问题。如果您有一个单页应用程序或者一个页面要处理很多 UI 操作,问题可能比较严重。在本文中,学习如何使用 sIEve 工具检测并解决内存泄漏问题,本文也包含内存泄漏问题的应用示例以及解决方案。

  • 深入剖析 Dojo 中的有状态对象 - dojo.Stateful” (developerWorks,2011 年 7 月):现在 Dojo 还提供了一种创建有状态对象(dojo.Stateful)的接口,让 Dojo 用户不仅可以定义自己的类,还可以创建出有状态的对象。通过创建这种有状态对象,用户不仅可以修改对象的属性,还可以监听该对象属性值的变化;一旦对象属 性值有所改变,用户便会即时接收到属性值变化的通知,以及属性的原始值和变化后的值。

  • 查看 HTML5 专题,了解更多和 HTML5 相关的知识和动向。

  • developerWorks Web development 专区:通过专门关于 Web 技术的文章和教程,扩展您在网站开发方面的技能。

  • developerWorks Ajax 资源中心:这是有关 Ajax 编程模型信息的一站式中心,包括很多文档、教程、论坛、blog、wiki 和新闻。任何 Ajax 的新信息都能在这里找到。

  • developerWorks Web 2.0 资源中心,这是有关 Web 2.0 相关信息的一站式中心,包括大量 Web 2.0 技术文章、教程、下载和相关技术资源。您还可以通过 Web 2.0 新手入门 栏目,迅速了解 Web 2.0 的相关概念。

你可能感兴趣的:(Collections)