for和foreach forecah值引用
一.概念
for:
for循环是用下标索引,对数组或集合的元素进行确定的。
foreach:
1、foreach适用于数组或实现了iterator的集合类。foreach就是使用Iterator接口来实现对集合的遍历的。
2、在用foreach循环遍历一个集合时,不能使用集合自带的方法改变集合中的元素,如增加元素、删除元素。否则会抛出ConcurrentModificationException异常。也不能修改集合中的元素(不报异常),但可以修改元素的属性。
3、foreach是值传递,故对基本数据类型的修改只对形参起作用,对实参不起作.
二.示例
1.删除集合里的元素
(1)在for循环里,通过list.remove(索引)方法,移除list的元素--正常运行
List<String> list = new LinkedList<String>(); list.add("Guangzhou"); list.add("Shengzhen"); list.add("Beijing"); //for list.remocve(索引)--正常运行 for(int i=0;i<=list.size();i++){ list.remove(0); i = 0; }
(2)在for循环里,通过list.remove(值)方法,移除list的元素--正常运行
//for list.remove(值)--正常运行 for(int i=0;i<3;i++){ System.out.println(list); if(list.get(1)=="Shengzhen") list.remove("Shengzhen"); }
(3)在foreach循环里,通过list.remove(索引)方法,移除list的元素--ConcurrentModificationException
报错的时间为,foreach循环第二次循环遍历时。
报错的地方为 for (String s:list) 该点。
原因是foreach底层是迭代器。
如果集合用迭代器进行遍历时,调用集合自身的list.remove(值),或者list.remove(索引)删除集合list自身的元素,第一次遍历过程中,调用remove方法会删除元素成功。
在第二次遍历时,迭代器会调用iterator.hasNext()方法,hasNext()是判断是否还有下一个元素。如果该集合有两个以上元素,则iterator.hasNext()会返回true,表示该集合接下来还有元素。然后会调用iterator.next()方法,来获取集合的下一个元素,此时iterator.next()内部会进行某些判断操作(篇幅较长下次再细说),然后iterator.next()返回ConcurrentModificationException。
//foreach list.remove(值)--ConcurrentModificationException for (String s:list) { list.remove(s); } System.out.println(list);
在list集合数量只有两个时,不会报错。
在只有两个元素的时候,删除第一个元素后,只剩下一个,此时调用迭代器的hasNext()是为false,所以不会继续遍历。也不会继续调用迭代器的next()。因为list里面的第一个元素被删除了,所以第二个元素的索引就由1变成0,所以迭代器以为第二个元素已经被遍历获取过了,就不会再通过next()来获取它,从而显示它了。其实第二个元素并没有被遍历获取,只是由于第一个元素被删除,它变成了修改后数组的第一个元素。
如果集合list有三个元素,则hasNext()不会返回false,会返回true,接下来通过next()来获取list的第二个元素(即集合没删除之前的第三个元素),此时由于next()内部的判断机制(下次再仔细讲解),在调用list自身的remove方法删除元素的情况下,会ConcurrentModificationException异常。
List<String> list = new LinkedList<String>(); list.add("Guangzhou"); list.add("Shengzhen"); for (String s:list) { System.out.println("foreach循环中的list 删除前:"+list); list.remove(s); System.out.println("foreach循环中的list 删除后:"+list); } System.out.println(list);
foreach循环中的list 删除前:[Guangzhou, Shengzhen]
foreach循环中的list 删除后:[Shengzhen]
[Shengzhen]
(4)在foreach循环里,通过list.remove(值)方法,移除list的元素--ConcurrentModificationException
原因和上面👆通过list.remove(索引)方法移除list集合元素一样。
所以在集合只有两个元素的情况下,出现的情况也一样。
//foreach list.remove(索引)--ConcurrentModificationException int i=0; for (String s:list) { list.remove(i); }
2.修改集合里的元素
for循环是根据集合的索引来获取集合元素,从而对集合进行修改。
foreach是值传递,故如果实参是基本数据类型,则对形参的修改不会影响实参。如果实参是对象引用,则对形参的修改会影响实参。
(1)用foreach修改基本数据类型
int[] arr = new int[10]; System.out.println("foreach中增加的i"); for(int i:arr){ i++; System.out.println(i); } System.out.println("arr数组中i"); for(int i:arr){ System.out.println(i); }
输出结果如下,可见foreach的实参是基本数据类型的情况下,值引用情况下赋值给形参的是,实参副本。对形参的修改,不会对实参造成影响。
foreach中增加的i 1 1 1 1 1 1 1 1 1 1 arr数组中i 0 0 0 0 0 0 0 0 0 0
(2)用foreach修改对象
class Per{ Per(){ } Per(String name,int age){ this.name = name; this.age = age; } private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
public class TestForeach { public static void main(String[] args) { Per p1 = new Per("oneone",11); Per p2 = new Per("twotwo",12); Per p3 = new Per("threethree",13); List<Per> list = new LinkedList<Per>(); list.add(p1); list.add(p2); list.add(p3); for (Per p:list) { p.setName("fourfour"); } list.forEach(per -> { System.out.println(per.getName()); }); } }
输出结果如下,可见foreach的实参是引用类型的情况下,值引用情况下赋值给形参的是,实参引用地址值的副本。对形参引用地址值所指向的对象的修改,会对实参引用地址值所指向的对象造成影响,因为这两个引用的地址值是一样的,指向同一个对象。
fourfour
fourfour
fourfour
参考的大佬文章: