java和javascript的for,foreach,iterator

<pre name="code" class="plain">
 

各种编程语言都有数组或者容器。对它们进行遍历,想必大家对for,foreach,iterator都有所了解。但是在同时学习了java和javascript语言的时候,看到了不同语言中对它们的不同实现与使用,下面分享一下对它们的使用心得:

1、for
for循环是最简单最基本的,任何语言都提供了其语法层次上的支持:

 
</pre><pre code_snippet_id="372624" snippet_file_name="blog_20140601_1_4096029" name="code" class="html">java:
int a[] = {1,2,3};
for(int i=0;i<a.length;i++){
System.out.println(a[i]);
}
javascript:
var a = [1,2,3];
for(var i=0;i<a.length;i++){
console.log(a[i]);
}


这里没有什么好讲的,就是普通的for循环,这种普通循环的特点就是:
需要自己维护一个索引变量,这里就是i.
然后就是要明确知道数组的长度。


提示:for循环的一种简单优化就是事先保存要遍历对象的length,或者从后往前通过i--反过来遍历。这样的好处是不需要每次循环都访问变量,并读取其长度属性。尤其是当被遍历的是一个dom对象的时候,这种优化尤为重要,因为dom的访问开销很大。
2、foreach
foreach,很多语言也都实现了这种遍历语法,但是java和javascript中,我们平时所说的foreach可能会有些差异,让我们来看看:
java:
int a[] = {1,2,3};
for(int element: a){
System.out.println(element);
}
输出:1,2,3
javascript:
var a= [1,2,3];
for(var element in a){
console.log(element);
}
输出:0,1,2



java对foreach的语法实现是for( : ){}形式,如例子中看到,element为a中的元素。然而javascript中有一个语法实现for( in ){},很多人说它是foreach,我们姑且也说它是foreach,但是它的执行却完全不同,如例子中看到,element是a的索引,对于这个语法一会再讲。所以如果从功能相同性上来讲,javascript中的for in实际上并不是我们正常意义上理解的foreach。而事实上javascript也有foreach的实现,但是并不是实现在语法层次,而是实现为了Array对象的一个原型方法,请看:
var a = [1,2,3];
a.forEach(function(element){
console.log(element);
});



这里是通过回调函数来处理元素,element才真正是a中的每一个元素。这种方式遍历数组的特点是:
简洁方便,节省代码,不需要显示维护一个索引i,不需要知道数组的长度。所以和for相比较,如果循环中不需要用到对应元素的索引,那么用这种方式就比较好,貌似有一定优化(这个没有细研究)。实际上javascript的forEach方法是有第二个参数的,第二个参数就是对应元素的索引,而且在forEach内部肯定也是通过一个索引来访问数组的,所以对于javascript,for和foreach就没有索引上的差别了。而只是形式上和性能上的差别了。

2.1插入性的讲讲javascript的for in
实际上javascript的for in是用来遍历对象属性的,而数组也是一个特殊的对象,所以我们也可以用它来遍历数组,正确的遍历方式:
var a = [1,2,3];
for(var i in a){
console.log(a[i]);
}


输出:1,2,3


在这个语法中,把a看做一个数组对象,数组对象是一个特殊对象,它特殊就特殊在它既有索引值,可以通过a[1]这样的方式访问数组元素(我猜这种访问应该和其它语言一样是通过内存地址加偏移量快速访问的,存猜测,恐怕不对),还有对应的索引属性和一个length属性,即在对象a上还有名字叫做‘'0','1','2'这样的属性,而众所周知javascript访问属性有两种方式,一种是通过.,一种是通过[],在上面的例子上我们访问的实际上就是a['0'],a['1'],a['2'].现在大家应该理解了这种遍历方式,那么就应该能够想到这种方式遍历数组还有一种隐患:遍历到你不想遍历的属性,因为in操作符会遍历对象包括其原型中所有[[enumrable]]为true,即可枚举的属性。所幸的是Array对象及其原型中大部分属性都是不可枚举的,比如length,join,slice,sort,forEach,fiter等等。但是很难保证不会有人为了操作,在遍历之前在数组上添加了某些属性,甚至覆盖了某些原有属性,比如length(在大多数浏览器中,length被覆盖后将变为可枚举,IE8及之前版本除外),这个时候就将出现你不想看到的结果。所以尽量不要用它来遍历数组,因为它的本质工作是遍历对象属性,并且用它来遍历数组并不会有什么好的效果。这里再提一句,java中如果要遍历对象所有属性,恐怕就要用到反射了,所以js还真的是很灵活方便呢。
tip:这段可能提到一些js相关知识点,比如原型,属性的特性,数组对象方法等,这些不会细讲。请学习对应基础知识。这里只关注本文主题。


3、iterator


终于到了最强大的iterator,也就是迭代器。迭代器作为一种设计模式,比较之前两种方法,就不仅仅是形式上的变化和完成遍历需求了,其更重要的目的是对设计上的帮助。
首先看java:
在java中,有一个专门的Iterator类,而迭代器的使用一般是结合各类容器的。先看一个简单的例子:
List<Integet> a = new ArrayList<Integer>();
a.add(1);
a.add(2);
a.add(3);
Iterator<Integer> aIt = a.iterator();
while(aIt.hasNext()){
System.out.println(aIt.next());
}


代码很简单,就不细说了,从遍历的形式上来讲,它和foreach有一样的特点:不需要维护索引值,不需要知道数组长度。所以如果从实现遍历需求的角度,迭代器看似也没什么特点(这也是我原来的想法),但是今天我却碰到了一种情况,让我又有了改观,这篇文章里先不讲。
不过迭代器最重要的地方在于其内部迭代不必知晓被迭代的容器对象的确切类型,也就是不需要知道这个容器是ArrayList,linedList,HashSet,TreeSet或者什么,这有什么好处呢?对于代码复用,数据与操作的解耦很有帮助,
比如:
public static void display(Iterator<Integer> it){
while(it.hasNext()){
System.out.println(it.next());
} 
}
public static void main (String args[]){
ArrayList<Integer> a = new ArrayList<Integer>();
a.add(1);
a.add(2);
a.add(3);
LinkedList<Integer> b = new LinkedList<Integer>(a);
HashSet<Integer> c = new Hashset<Integer>(a);
display(a.iterator());
display(b.iterator());
display(c.iterator());
} 


对于display方法,它所遍历的对象是不和容器相关的,所以对于a,b,c,display是可重用的。想一想如果display不用迭代器实现,它接收一个ArrayList对象,然后用for或者foreach进行遍历。那么如果你想遍历的是一个LinkedList对象,你不得不为LinedList重写,或者重载这个方法。如果又多了一个HashSet呢,又多了一个TreeSet呢……
这才是迭代器的牛逼之处。


下面我们再来看看javascript

javascript应该不存在迭代器的原生实现,一些类库中应该有。所以我们可以自己实现一个简单的迭代器,它的形式就模仿java的就可以,自己实现可控性就很强了,迭代器的实现也并不是很难,基础功能就是实现hasNext和next方法,想要添加其它的什么功能就随你喜欢了。然而javascript中,因为其弱类型性,迭代器设计上的这种作用好像不那么明显了,因为对于任何类型的对象display都是可以接受的,这样就不需要用iterator来统一接口进行接受(这可能也是没有原生迭代器的原因)。但是其实迭代器作为一种模式,其作用不仅如此,就像上文中我说的我碰到的一种情况,这应该作为设计模式进行讲解,这里先不讲。下一篇文章会讲到迭代器设计模式和js的迭代器实现。
以上就是我对for,foreach和iterator的理解以及java和javascript在这方面上的差异。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值