How to avoid exceptions in simple for loops in JAVA?
Introduction
In actual business project development, everyone should be familiar with the operation of removing elements that do not meet the conditions from a given list, right?
Many students can immediately think of many ways to achieve it, but are these implementation methods you think of harmless to humans and animals? Many seemingly normal operations are actually traps, and many novices may fall into them if they are not careful.
If you accidentally step on it:
The code will directly throw an exception and report an error when running. This is a blessing in misfortune. At least it can be discovered and solved in time
The code runs without error, but various strange problems appear inexplicably in the business logic. This is more tragic, because if this problem is not paid attention to, it may cause hidden dangers for subsequent business.
So, what are the implementation methods? Which implementation methods may have problems? Let’s discuss it together here. Please note that what is discussed here is not the issue of how to write the word "fennel" in fennel beans, but a technical issue that is very serious, practical and easy to be ignored.
Hypothetical demand scenario:
Given a user list allUsers, it is necessary to remove the personnel whose subordinate department is dev from the list, and return the remaining personnel information
Step on the pit Operation
foreach loop elimination method
The first thought of many novices is to check the for loop one by one and then eliminate those that meet the conditions~ so easy...
I finished writing the code in 1 minute:
public List<UserDetail> filterAllDevDeptUsers(List<UserDetail> allUsers) { for (UserDetail user : allUsers) { // 判断部门如果属于dev,则直接剔除 if ("dev".equals(user.getDepartment())) { allUsers.remove(user); } } // 返回剩余的用户数据 return allUsers; }
Then I clicked the execute button with confidence:
java.util.ConcurrentModificationException: null at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909) at java.util.ArrayList$Itr.next(ArrayList.java:859) at com.veezean.demo4.UserService.filterAllDevDeptUsers(UserService.java:13) at com.veezean.demo4.Main.main(Main.java:26)
Eh? what are you doing? Why is the exception thrown?
If you don’t pay attention, you will step into a trap. Let’s analyze why an exception is thrown.
Cause analysis:
The actual processing of JAVA’s foreach syntax is based on the iterator Iterator.
At the beginning of the loop, an iteration instance will first be created, and the expectedModCount of this iteration instance is assigned the modCount of the collection. Whenever the iterator uses hashNext() / next() to traverse the next element, it will check whether the modCount variable and expectedModCount values are equal. If they are equal, the traversal will be returned; otherwise, a ConcurrentModificationException will be thrown to terminate the traversal.
If you add or delete elements in a loop, you directly call the add() and remove() methods of the collection, causing the modCount to increase or decrease, but these methods will not modify the expectedModCount in the iteration instance, resulting in If the values of expectedModCount and modCount in the iteration instance are not equal, a ConcurrentModificationException exception is thrown.
Subscript loop operation
Huh? Since the foreach method doesn't work, then use the original subscript loop method to do it. You won't get an error, right? It's still very easy...
public List<UserDetail> filterAllDevDeptUsers(List<UserDetail> allUsers) { for (int i = 0; i < allUsers.size(); i++) { // 判断部门如果属于dev,则直接剔除 if ("dev".equals(allUsers.get(i).getDepartment())) { allUsers.remove(i); } } // 返回剩余的用户数据 return allUsers; }
The code is completed in one go. Execute it and see the processed output:
{id=2, name='李思', department=' dev'}
{id=3, name='王五', department='product'}
{id=4, name='Tiezhu', department='pm'}
Sure enough, no error is reported, the result is also output, perfect~
Wait? Is this really OK?
The logic of our code is to determine if "dev".equals(department), but in the output result, why is there still data like department=dev that should be eliminated?
If this is a real business project, if no errors are reported during the development phase, and the results are not carefully verified, and then flow to the production line, it may cause abnormal business logic.
Let’s take a look at the specific reasons for this phenomenon.
Cause analysis:
We know that there is actually no strong binding relationship between the elements in the list and the subscripts. It is just a corresponding relationship of position order. After the elements in the list are changed, , the subscript corresponding to each element may change, as shown below:
Then, after deleting an element from the List, all elements after the deleted element in the List The subscripts are moved forward, but the pointer i of the for loop is always accumulated backwards. When processing the next one, some elements may be missed and not processed.
For example, as shown in the figure below, when i=0, it is judged that the A element needs to be deleted, and it is deleted directly; when recirculating, i=1, at this time, because the position of the element in the list moves forward, the B element becomes The original position with the subscript 0 was directly missed:
So here you can know why the above code will be missed after execution. La~
Correct way
After seeing the above two pitfalls, what should be the correct and appropriate way to operate?
Iterator method
Eh? That's right? Didn't I just say that the foreach method also uses iterators, but is it actually a trap operation? Why is it said here that the iterator pattern is the correct way?
虽然都是基于迭代器,但是使用逻辑是不一样的,看下代码:
public List<UserDetail> filterAllDevDeptUsers(List<UserDetail> allUsers) { Iterator<UserDetail> iterator = allUsers.iterator(); while (iterator.hasNext()) { // 判断部门如果属于dev,则直接剔除 if ("dev".equals(iterator.next().getDepartment())) { // 这是重点,此处操作的是Iterator,而不是list iterator.remove(); } } // 返回剩余的用户数据 return allUsers; }
执行结果:
{id=3, name='王五', department='product'}
{id=4, name='铁柱', department='pm'}
这次竟然直接执行成功了,且结果也是正确的。为啥呢?
在前面foreach方式的时候,我们提过之所以会报错的原因,是由于直接修改了原始list数据而没有同步让Iterator感知到,所以导致Iterator操作前校验失败抛异常了。
而此处的写法中,直接调用迭代器中的remove()方法,此操作会在调用集合的remove(),add()方法后,将expectedModCount重新赋值为modCount,所以在迭代器中增加、删除元素是可以正常运行的。,所以这样就不会出问题啦。
Lambda表达式
言简意赅,直接上代码:
public List<UserDetail> filterAllDevDeptUsers(List<UserDetail> allUsers) { allUsers.removeIf(user -> "dev".equals(user.getDepartment())); return allUsers; }
Stream流操作
作为JAVA8开始加入的Stream,使得这种场景实现起来更加的优雅与易懂:
public List<UserDetail> filterAllDevDeptUsers(List<UserDetail> allUsers) { return allUsers.stream() .filter(user -> !"dev".equals(user.getDepartment())) .collect(Collectors.toList()); }
中间对象辅助方式
既然前面说了不能直接循环的时候执行移除操作,那就先搞个list对象将需要移除的元素暂存起来,最后一起剔除就行啦 ~
嗯,虽然有点挫,但是不得不承认,实际情况中,很多人都在用这个方法 —— 说的就是你,你是不是也曾这么写过?
public List<UserDetail> filterAllDevDeptUsers(List<UserDetail> allUsers) { List<UserDetail> needRemoveUsers = new ArrayList<>(); for (UserDetail user : allUsers) { if ("dev".equals(user.getDepartment())) { needRemoveUsers.add(user); } } allUsers.removeAll(needRemoveUsers); return allUsers; }
或者:
public List<UserDetail> filterAllDevDeptUsers(List<UserDetail> allUsers) { List<UserDetail> resultUsers = new ArrayList<>(); for (UserDetail user : allUsers) { if (!"dev".equals(user.getDepartment())) { resultUsers.add(user); } } return resultUsers; }
The above is the detailed content of How to avoid exceptions in simple for loops in JAVA?. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics

Guide to Smith Number in Java. Here we discuss the Definition, How to check smith number in Java? example with code implementation.

In this article, we have kept the most asked Java Spring Interview Questions with their detailed answers. So that you can crack the interview.

Java 8 introduces the Stream API, providing a powerful and expressive way to process data collections. However, a common question when using Stream is: How to break or return from a forEach operation? Traditional loops allow for early interruption or return, but Stream's forEach method does not directly support this method. This article will explain the reasons and explore alternative methods for implementing premature termination in Stream processing systems. Further reading: Java Stream API improvements Understand Stream forEach The forEach method is a terminal operation that performs one operation on each element in the Stream. Its design intention is

Guide to TimeStamp to Date in Java. Here we also discuss the introduction and how to convert timestamp to date in java along with examples.

Capsules are three-dimensional geometric figures, composed of a cylinder and a hemisphere at both ends. The volume of the capsule can be calculated by adding the volume of the cylinder and the volume of the hemisphere at both ends. This tutorial will discuss how to calculate the volume of a given capsule in Java using different methods. Capsule volume formula The formula for capsule volume is as follows: Capsule volume = Cylindrical volume Volume Two hemisphere volume in, r: The radius of the hemisphere. h: The height of the cylinder (excluding the hemisphere). Example 1 enter Radius = 5 units Height = 10 units Output Volume = 1570.8 cubic units explain Calculate volume using formula: Volume = π × r2 × h (4

PHP and Python each have their own advantages, and the choice should be based on project requirements. 1.PHP is suitable for web development, with simple syntax and high execution efficiency. 2. Python is suitable for data science and machine learning, with concise syntax and rich libraries.

PHP is a scripting language widely used on the server side, especially suitable for web development. 1.PHP can embed HTML, process HTTP requests and responses, and supports a variety of databases. 2.PHP is used to generate dynamic web content, process form data, access databases, etc., with strong community support and open source resources. 3. PHP is an interpreted language, and the execution process includes lexical analysis, grammatical analysis, compilation and execution. 4.PHP can be combined with MySQL for advanced applications such as user registration systems. 5. When debugging PHP, you can use functions such as error_reporting() and var_dump(). 6. Optimize PHP code to use caching mechanisms, optimize database queries and use built-in functions. 7

Java is a popular programming language that can be learned by both beginners and experienced developers. This tutorial starts with basic concepts and progresses through advanced topics. After installing the Java Development Kit, you can practice programming by creating a simple "Hello, World!" program. After you understand the code, use the command prompt to compile and run the program, and "Hello, World!" will be output on the console. Learning Java starts your programming journey, and as your mastery deepens, you can create more complex applications.
