当前位置: 代码迷 >> Web前端 >> 深入懂得 Dojo 的 Collections 工具包
  详细解决方案

深入懂得 Dojo 的 Collections 工具包

热度:801   发布时间:2012-11-23 00:03:43.0
深入了解 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 等,介绍了它们的基本接口,高级接口和一些特殊接口。同时,也阐述了这些不同集合对象之间的相同点和不同点。本文主要是基于实际的代码示例来说明这些集合对象的用法,简明直观,推荐大家在日常开发中多参考。

  相关解决方案