실제 비즈니스 프로젝트 개발에서는 주어진 목록에서 조건에 맞지 않는 요소를 제거하는 작업을 누구나 숙지해야겠죠?
많은 학생들이 즉시 여러 가지 구현 방법을 떠올릴 수 있지만, 당신이 생각하는 이러한 구현 방법은 인간과 동물에게 무해합니까? 겉보기에 평범해 보이는 많은 작업이 실제로는 함정이며, 많은 초보자들이 조심하지 않으면 함정에 빠질 수 있습니다.
실수로 밟은 경우:
코드가 실행 중일 때 직접 예외를 발생시키고 오류를 보고합니다. 이는 적어도 시간이 지나면 발견하고 해결할 수 있다는 점입니다.
코드는 오류를 보고하지 않지만 비즈니스 로직은 설명할 수 없을 정도로 나타납니다. 이상한 문제는 이런 종류가 더 비극적입니다. 왜냐하면 이 문제에 주의를 기울이지 않으면 후속 비즈니스에 숨겨진 위험이 발생할 수 있기 때문입니다.public List<UserDetail> filterAllDevDeptUsers(List<UserDetail> allUsers) { for (UserDetail user : allUsers) { // 判断部门如果属于dev,则直接剔除 if ("dev".equals(user.getDepartment())) { allUsers.remove(user); } } // 返回剩余的用户数据 return allUsers; }
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)
아래 첨자 루프 연산
흠? foreach 메서드가 작동하지 않으므로 원래의 첨자 루프 메서드를 사용하면 오류가 발생하지 않습니다. 여전히 매우 쉽습니다...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; }
{id=2, name='lee思', Department='dev'}
{id =3, name ='王五', Department='product'}{id=4, name='Tiezhu', Department='pm'}기타..? 정말 괜찮은 걸까요? 우리의 코드 로직은 "dev".equals(department)인지 판단하는 것인데, 출력 결과에는 왜 아직도 제거해야 할 Department=dev 같은 데이터가 있나요? 실제 비즈니스 프로젝트라면 개발 단계에서 오류가 보고되지 않고, 결과가 꼼꼼히 검증되지 않은 후 생산 라인으로 흘러가게 된다면 비정상적인 비즈니스 로직이 발생할 수 있습니다. 이 현상이 나타나는 구체적인 이유를 살펴보겠습니다. 원인 분석: 우리는 실제로 목록의 요소와 첨자 사이에 강력한 바인딩 관계가 없다는 것을 알고 있습니다. 이는 단지 목록의 요소가 변경된 후 해당 첨자의 대응 관계일 뿐입니다. 각 요소는 아래와 같이 아래 첨자가 변경될 수 있습니다.
물론, 오류도 보고되지 않고 결과도 출력됩니다. , 완벽해요~
그런 다음 목록에서 요소를 삭제한 후 목록에서 삭제된 요소 뒤에 있는 모든 요소의 아래 첨자는 앞으로 이동하지만 for의 포인터 i 루프는 항상 거꾸로 누적됩니다. 예, 다음 루프를 처리할 때 일부 요소가 누락되어 처리되지 않을 수 있습니다.
예를 들어 아래 그림과 같이 i=0이면 A 요소를 삭제해야 한다고 판단하고, 재활용 시 i=1이면 바로 삭제됩니다. 목록의 요소가 앞으로 이동하면 B 요소가 원래 아래 첨자가 됩니다. 0의 위치가 직접 누락되었습니다.여기서 위 코드가 실행 후 누락되는 이유를 알 수 있습니다~
올바른 방법위의 2가지 함정을 확인했습니다. 수술 후 올바르고 적절한 수술방법은 무엇인가요? Iterator 메서드어? 좋아요? foreach 메서드도 반복자를 사용한다고 방금 말하지 않았는데 실제로는 트랩 작업인가요? 여기서 반복자 패턴이 올바른 방식이라고 말하는 이유는 무엇입니까?
虽然都是基于迭代器,但是使用逻辑是不一样的,看下代码:
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,所以在迭代器中增加、删除元素是可以正常运行的。,所以这样就不会出问题啦。
言简意赅,直接上代码:
public List<UserDetail> filterAllDevDeptUsers(List<UserDetail> allUsers) { allUsers.removeIf(user -> "dev".equals(user.getDepartment())); return allUsers; }
作为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; }
위 내용은 JAVA의 간단한 for 루프에서 예외를 방지하는 방법은 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!