foreach 循环里禁止进行元素的 remove/add 操作
参考网址:
https://mp.weixin.qq.com/s?__biz=MzI3NzE0NjcwMg==&mid=2650123395&idx=1&sn=b089eac4f561f58ee8a92602db17f577&chksm=f36bb1a2c41c38b4f36c1a84f205e188a6a8aa2684f316e4dcb8d1162b6e94b970c670b2e5b8&mpshare=1&scene=23&srcid=1117O5qEI4yxgwsIe32hRV1W&sharer_sharetime=1605575661903&sharer_shareid=9d1e76e919cc0b2f3ca23ed1f5ef67a8#rd
1.说明
-
阿里巴巴禁止在 foreach 循环里进行元素的 remove/add 操作
-
java.util.ConcurrentModificationException
foreach 循环里进行元素的 remove/add 操作会报异常ConcurrentModificationException
2.测试demo
@Test
public void test1(){
List<String> userNames = new ArrayList<String>() {{
add("Hollis");
add("hollis");
add("HollisChuang");
add("H");
}};
for (String userName : userNames) {
if (userName.equals("Hollis")) {
userNames.remove(userName);
}
}
System.out.println(userNames);
}
报错信息:
java.util.ConcurrentModificationException
at java.util.ArrayListItr.checkForComodification(ArrayList.java:901)atjava.util.ArrayListItr.checkForComodification(ArrayList.java:901) at java.util.ArrayListItr.checkForComodification(ArrayList.java:901)atjava.util.ArrayListItr.next(ArrayList.java:851)
at com.shaoming.foreach_throwexception.ForEachTestRemove.test1(ForEachTestRemove.java:24)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunne
说明:
使用增强for循环遍历元素,并尝试删除其中的Hollis字符串元素。运行以上代码,会抛出以下异常:
java.util.ConcurrentModificationException
3.解释报错的原因
fail-fast,即快速失败,它是Java集合的一种错误检测机制。当多个线程对集合(非fail-safe的集合类)进行结构上的改变的操作时,有可能会产生fail-fast机制,这个时候就会抛出ConcurrentModificationException(当方法检测到对象的并发修改,但不允许这种修改时就抛出该异常)。
说的啥,我具体也是没搞懂,以后再看吧
4.正确的demo格式
package com.shaoming.foreach_throwexception;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.stream.Collectors;
/**
* @Auther: shaoming
* @Date: 2020/11/17 10:54
* @Description: 为什么阿里巴巴禁止在 foreach 循环里进行元素的 remove/add 操作
*/
public class ForEachTestRemove {
/**
* 错误的示例代码
* 报错:
* java.util.ConcurrentModificationException
*/
@Test
public void test1() {
List<String> userNames = new ArrayList<String>() {{
add("Hollis");
add("hollis");
add("HollisChuang");
add("H");
}};
for (String userName : userNames) {
if (userName.equals("Hollis")) {
userNames.remove(userName);
}
}
System.out.println(userNames);
}
/**
* 测试遍历的时候对集合元素进行增删改的操作===>使用ForEach的方式进行操作
* 举例:
* 拿test1这个方法机型举例
* 我们遍历的时候,在每个string类型的元素后面拼接一个HelloWorld
*/
@Test
public void test2() {
List<String> userNames = new ArrayList<String>() {{
add("Hollis");
add("hollis");
add("HollisChuang");
add("H");
}};
List<String> userNamesAdd = new ArrayList<String>(userNames.size());
// userNames.clear();
for (String userName : userNames) {
userName = userName + "Hello World!";
System.out.println(userName);
userNamesAdd.add(userName);
}
System.out.println("开始遍历新的集合====================");
userNamesAdd.forEach(System.out::println);
}
/**
* 5种正确姿势
*/
//1.直接使用普通的for循环进行操作
@Test
public void test51() {
List<String> userNames = new ArrayList<String>() {{
add("Hollis");
add("hollis");
add("HollisChuang");
add("H");
}};
for (int i = 0; i < userNames.size(); i++) {
if(userNames.get(i).equals("Hollis")){
//list集合remove参数i是集合的下标(索引)值
userNames.remove(i);
}
}
System.out.println(userNames);
}
//2.直接使用Iteraor进行操作
@Test
public void test52(){
List<String> userNames = new ArrayList<String>() {{
add("Hollis");
add("hollis");
add("HollisChuang");
add("H");
}};
Iterator<String> iterator = userNames.iterator();
while(iterator.hasNext()){
String userName = iterator.next();
if(userName.equals("Hollis")){
iterator.remove();
}
}
System.out.println(userNames);
}
//3.使用java8种提供的filter进行过滤
@Test
public void test53(){
List<String> userNames = new ArrayList<String>() {{
add("Hollis");
add("hollis");
add("HollisChuang");
add("H");
}};
userNames = userNames.stream().filter(userName -> !userName.equals("Hollis")).collect(Collectors.toList());
System.out.println(userNames);
}
//4.使用fail-safe的集合类进行遍历
@Test
public void test54(){
ConcurrentLinkedDeque<String> userNames = new ConcurrentLinkedDeque<String>() {{
add("Hollis");
add("hollis");
add("HollisChuang");
add("H");
}};
for (String userName : userNames) {
if (userName.equals("Hollis")) {
userNames.remove();
}
}
}
//5.使用增强for循环其实也可以,不过步骤麻烦
@Test
public void test55(){
List<String> userNames = new ArrayList<String>() {{
add("Hollis");
add("hollis");
add("HollisChuang");
add("H");
}};
for (String userName : userNames) {
if (userName.equals("Hollis")) {
userNames.remove(userName);
break;
}
}
System.out.println(userNames);
}
}
5.结论
以上这五种方式都可以避免触发fail-fast机制,避免抛出异常。如果是并发场景,建议使用concurrent包中的容器,如果是单线程场景,Java8之前的代码中,建议使用Iterator进行元素删除,Java8及更新的版本中,可以考虑使用Stream及filter。
Java8及更新的版本中,可以考虑使用Stream及filter。
博客指出阿里巴巴禁止在Java的foreach循环里进行元素的remove/add操作,否则会报ConcurrentModificationException异常。介绍了测试demo及报错信息,解释报错原因是fail - fast机制。最后给出避免触发该机制的方法,并发用concurrent包容器,单线程Java8前用Iterator,Java8及以后可用Stream及filter。
3959

被折叠的 条评论
为什么被折叠?



