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)); |
代码可能有点多,我们来一一结介绍:
- 注意 dxcs 是指代的 Dojo 的 Set 类 -- “dojox.collections.Set”。关注“a”和“b”两个集合。
- “union”表示联合,但是 Set 的联合方法会去除重复元素。
- “intersection”是做交叉,选出两个集合里面具有的元素,然后组成集合。
- “difference”是取得集合“a”中存在但集合“b”中不存在的元素。
- “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”等等这里就不一一列举了。我们来看一下它特有的方法:
- 注意到,虽然它是按“foo”,“baz”,“buck”,“apple”的顺序添加,但是它的内部顺序为:“apple”,“baz”,“buck”,“foo”,所以 value 的顺序应为:“orange”,“fab”,“shot”,“bar”。基于这种机制,我们就能了解它的一些接口的返回值。
- “getByIndex(2)”应该是返回第三个元素的 value,所以为“shot”。
- “getKey(0)”自然是第一个元素的 key,所以为“apple”。
- “indexOfKey("apple")”返回元素“apple”的 index,为 0;“indexOfValue”返回 value 元素“bar”的位置,为第四个元素,即 index 为 3。
- “setByIndex”和“getByIndex”是根据位置设置值和取值。
可以看出,SortedList 的接口是很全面的,建议大家在项目中多使用。
?
结束语
这篇文章介绍了 Dojo 中关于 Collections 集合对象的一些特性,从 collection 工具包的基础出发,依次介绍了 ArrayList、BinaryTree、Queue、Stack、Set、Dictionary 以及 SortedList 等,介绍了它们的基本接口,高级接口和一些特殊接口。同时,也阐述了这些不同集合对象之间的相同点和不同点。本文主要是基于实际的代码示例来说明这些集合对象的用法,简明直观,推荐大家在日常开发中多参考。