黑马程序员——Foreach的原理
来源:互联网 发布:ubuntu安装 分区 编辑:程序博客网 时间:2024/06/07 12:16
---------------------- <a href="http://edu.csdn.net"target="blank">ASP.Net+Android+IOS开发</a>、<a href="http://edu.csdn.net"target="blank">.Net培训</a>、期待与您交流! ----------------------
我们在使用for循环时,经常为for循环的结束标志而苦恼,尤其是做嵌套for循环对多维数组进行遍历时,更是令人头痛,稍微一疏忽就会发生indexOutOfBoundsException(数组角标越界)错误,为此在Java SE 5.0中推出了一种for循环的高级应用模式for each,它可以让我们很方便的依次处理数组中的每一个元素(其他类型的元素集合亦可) 而不必为指定下标值而分心。
Foreach语句的格式为
for(variable : collection) statement
定义一个变量用于暂存集合中的每一个元素,并执行相应的语句,collection这一集合表达式必须是一个数组或者是一个实现了Iterable接口的类对象,例如:
int[] a={………};
For(int temp :a[]){
Syso(temp);
}
遍历数组a中的元素并打印出来
Foreach语句的特点
foreach是for循环的简写模式,那么它把for循环简写之后的优点和缺点是什么呢?
优点:可以让我们很方便的遍历一个实现Iterable接口类的对象(集合、数组或其它的什么),尤其是foreach在遍历多维的集合或数组时更是叫人称赞不已,它省去让我们苦恼的下标值(不必为下标的起始值和终止值操心),可以让我们专心的来完成其他工作。
int[][] a=new int[i][j];
假定a已经赋值,如果我们要遍历a的话,我们要在内循环调用外循环的循环变量,来设定内循环的终止值,代码如下:
for(int x=0;x
for(y=0;y
}
}
类似这样设定的嵌套循环非常的容易出现indexOutOfBoundsException(数组角标越界)异常,但是我们如果使用foreach就可以很方便的使用嵌套循环来遍历多维数组或集合,例:
for(int[] temp : a){
for(int a : temp){
syso(a);
}
}
由此可见foreach的好处显而易见,它使循环语句显得更加简洁、更不易出错。
缺点:事物往往是相对,有其优点就有其缺点foreach语句也是这样的,foreach是一种功能很强的循环结构,但是其始终替代不了for循环,是因为要使用foreach循环必须要有一个被遍历项,所以在数组和集合之外foreach就显得无用武之地了。此外我们在用foreach遍历数组或集合过程中,我们不可对数组或集合中的元素进行操作,至于其原理我们稍后解释。
Foreach的原理
要使用foreach来遍历的对象必须实现Iterable接口,这是因为foreach内部其实是封装了一个迭代器对象来对所要遍历的对象进行迭代,而这个迭代器的来源是通过所要遍历对象的Iterator iterator()方法来 获取的,Iterator iterator()这个方法封装在Iterable接口中,所以要遍历对象必须实现Iterable 接口。此过程用代码表示为:
Iterable接口的代码:
public interface Iterable {
Iteratoriterator();
}
Foreach内部实现代码(此处为自己写的foreach内部实现原理代码并不一定准确)
调用for(Type type : object ) statement 就相当于
Iterator it=object.iterator();
while(it.hasNext())
{
type=it.next();
statement
}
迭代器
我们知道Foreach内部,封装了一个迭代器,那么迭代器又是什么呢?
将一个对象中的数据依次取出是一个复杂的动作,它包括判断动作,和取出动作,我们用一个方法不足以描述这个动作,所以就要用内部类来描述它,而每一个要被迭代的类都封装了这样一个内部类,那么我们就可以将这个内部类的共性特点抽取出来,由此我们就得到了一个Iterator,这个接口就是迭代器接口。这个接口的内部代码为:
public interface Iterator {
boolean hasNext();
E next();
void remove();
}
由此我们可知迭代器共有三个方法:
1、判断下一个元素是否存在
2、取出下一个元素
3、删除元素
我们可以使用前两个方法来对元素进行迭代,使用第三个方法删除当前元素。
迭代器的由来和迭代器所包含的方法我们已经知道,那么在类的内部是如何实现迭代器的呢?在ArrayList以内部类实现迭代器 代码如下:
private class Itr implements Iterator {
int cursor;
int lastRet = -1;
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
这段代码中有许多莫名的属性,我们依此来说明:
Cursor
用来指定当前元素的下一个元素
lastRet
用来指定当前元素
modCount
在父类AbstractList中定义的一个int型属性:modcount,用来记录ArrayList结构性变化的次数。在ArrayList中所涉及结构变化的方法有:add()、remove()、addAll()、removeRange()和clear()方法。这些方法每调用一次,modCount的值就加1;
Size
可以理解为当前对象存储数据的长度
@SuppressWarnings("unchecked")
这是Java SE 5.0的新特性注释类型指示应该在注释元素(以及包含在该注释元素中的所有程序元素)中取消显示指定的编译器警告。
checkForComodification
检测机制,假如在对集合迭代期间集合的结构性发生了改变,即在对集合遍历的同时还对集合进行的增删操作,那么将导致expectedModCount 与modCount不相等,checkForComodification可以检测出来并抛出ConcurrentModificationException异常。
elementData
这个可以理解为一个带角标的数据缓冲区,将集合中的元素取出放入此缓冲区中,然后依次遍历此缓冲区中的元素来实现遍历集合的目的。集合对象的容量就是此数据缓冲区的长度,该长度至少要足以包含集合对象的所有元素。
在迭代器的三个方法中还有一些,检测异常的代码,我在这里就不说了。
Foreach、Iterator、Iterable的关系
我们在foreach内部封装了迭代器,我们只要通过对象获取迭代器对象就行了,即对象所属的类的内部类只有实现Iterator接口就行了,和Iterable有什么关系?
Foreach要通过一个固定的方法获取对象的迭代器对象,而只要类的内部具有实现Iterator接口的内部类,那么此类就要有这个方法,因此我们将这个方法单独抽取出来封装为Iterable接口,这就是Iterable接口的由来。
要使用Foreach方法,则所要遍历对象所属的类必须实现Iterable接口,而实现Iterable接口,则此类内部又必须能产生Iterator对象(即:必须内部类实现Iterator接口)。
Foreach的使用注意事项
foreach的原理我们已经知道,那么我们不难得知使用foreach应注意的事项:
1、不可在使用foreach迭代集合或数组的同时对它们进行增删操作。
2、在没有特殊情况下foreach内部最好使用一个 object.next(); 如若不然将非常容易出现角标越界异常。
- 黑马程序员——Foreach的原理
- 黑马程序员-------------foreach循环的应用
- 黑马程序员——Tomcat服务器原理的小例子
- 黑马程序员——TreeSet集合添加元素的原理
- 黑马程序员——Enum的原理及用法总结
- 黑马程序员——Reflectfab反射的原理透析通俗易懂
- 黑马程序员--C#中for和foreach的区别
- foreach的基本用法及原理——C#
- foreach的实现原理
- foreach的原理
- java foreach的原理
- 黑马程序员 c:if和c:forEach
- 黑马程序员 java for 和foreach
- 【黑马程序员】 【转载】foreach循环 ---------BY elleniou
- 黑马程序员————————多线程(死锁的原理)
- 黑马程序员——OC语言日志——内存管理的原理、分类和原则
- 黑马程序员——类加载器的原理和编写
- 黑马程序员——Java中HashSet中hashCode()的原理
- 黑马程序员 枚举数和迭代器 总结
- QoS令牌桶
- 黑马程序员 委托、方法、事件 总结
- html5 canvas制作箭头
- 【ASO潜规则】之改名克隆 疯狂圈钱
- 黑马程序员——Foreach的原理
- 工厂三兄弟之抽象工厂模式(二)
- RelativeLayout用到的一些重要的属性
- iOS webservice+soap
- Apache+Tomcat负载均衡两种session共享方式的设置
- 程序员的十层楼——周伟明
- 在自己应用的XML中,使用系统资源
- jquery 找到当前可见的元素并切换显示
- 工厂三兄弟之抽象工厂模式(三)