Java 기본 튜토리얼이 칼럼에서는 맵과 컬렉션
권장(무료): Java 기본 튜토리얼
일부 고급 프로그래밍 언어 Java 프로그래밍 언어의 컬렉션 프레임워크와 같은 기본 데이터 구조의 구현은 다양한 구현을 제공합니다. 컬렉션 클래스에는 아래 목록과 같은 두 가지 주요 범주가 포함됩니다. 컬렉션은 우리가 자주 사용하는 컬렉션 클래스입니다. 첫째, 많은 비즈니스 코드가 컬렉션 클래스와 분리될 수 없습니다. 오늘은 List의 몇 가지 함정을 살펴보겠습니다.
예를 들어 다음 코드를 실행하면
List<String> strings = Arrays.asList("m", "g"); strings.add("h");
는 java.lang.UnsupportedOperationException
을 발생시킵니다. code> 예외. 이때 OS 당신의 마음은 무엇입니까? 반환된 ArrayList에 요소를 추가할 수 없는 이유는 무엇입니까? 나중에 요소를 올바르게 추가할 수 있습니까?
를 선택한 다음 Debug
Dafa를 확실하게 활성화합니다: java.lang.UnsupportedOperationException
异常,此时你内心 OS what?明明返回的 ArrayList 为啥不能往里面增加元素,这以后还能好好的增加元素吗?
,然后果断开启 Debug
大法:
发现返回的 ArrayList
并不是我们常用的 java.util.ArrayList
,而是 Arrays
的内部类 java.util.Arrays.ArrayList
。进入方法 Arrays.asList
源码如下:
public static <T> List<T> asList(T... a) { return new ArrayList<>(a); }
方法返回的是 Arrays
的静态内部类 java.util.Arrays.ArrayList
,该类虽然和 java.util.ArrayList
也继承自抽象类 java.util.AbstractList
,但是通过该类的源码发现它并没有对抽象父类AbstractList
的 add
方法默认就是抛出 java.lang.UnsupportedOperationException
异常。
这个坑的根本原因是我们调用返回的 strings
的 add
方法是继承自抽象父类的 add
方法,而抽象父类的方法默认就是抛出 java.lang.UnsupportedOperationException
这个异常。
Arrays.asList
方法除了上面这个 不支持增加、删除元素 这个坑之外,还有另外一个坑:
从以上代码可以发现,对原始数组的修改会影响我们通过 Arrays.asList
方法获得的新 List
,深入 java.util.Arrays.ArrayList
的源码:
private static class ArrayList<E> extends AbstractList<E> implements RandomAccess, java.io.Serializable { private static final long serialVersionUID = -2764017481108945198L; private final E[] a; ArrayList(E[] array) { a = Objects.requireNonNull(array); } ... }
可以发现是直接使用了原始的数组,所有当我们使用 Arrays.asList
方式获得的 List
时要特别注意,因为共享了数组,相互修改时可能产生一些意想不到的 Bug。标准的姿势之一是将其作为 ArrayList
构造方法的参数重新 new
一个 List
出来即可(e.g. List<string> stringList = new ArrayList(Arrays.asList(arrays))</string>
)或者通过 Guava
库中的 Lists.newArrayList
,将返回的新 List
和原始的数组解耦,就不会再互相影响了。
在直接遍历集合元素时增加、删除元素会报错,比如执行如下代码:
List<String> stringList = Lists.newArrayList("m", "g", "h"); for (String s : stringList) { if (Arrays.asList("m", "h").contains(s)) { stringList.remove(s); } }
以上代码可以正常编译通过,但是执行时会抛出 java.util.ConcurrentModificationException
异常,查看其源码可以发现,删除元素方法 remove
会使集合结构发生修改,也就是 modCount(
集合实际修改的次数)会修改,在循环过程中,会比较当前 List
的集合实际修改的次数 modCount
与迭代器修改的次数 expectedModCount
,而 expectedModCount
是初始化时的 modCount
, 二者不相等,就会报 ConcurrentModificationException
异常。解决方法主要有两种方式,1.使用 ArrayList
的迭代器方式遍历,然后调用其中的方法。2.在 JDK 1.8+ 可以使用 removeIf
方法进行删除操作。
最后扎心一问:调用 ArrayList
的 remove
方法传入 int
基本类型的数字和 Integer
ArrayList
가 우리가 일반적으로 사용하는 java.util.ArrayList
는 아니지만 배열의 내부 클래스 java.util.Arrays.ArrayList
. Arrays.asList
메소드를 입력합니다. 소스 코드는 다음과 같습니다. 🎜rrreee🎜 메소드는 Arrays의 정적 내부 클래스 <code>java.util.Arrays.ArrayList
를 반환합니다. 클래스와 java.util.ArrayList
도 추상 클래스 java.util.AbstractList
에서 상속되지만 이 클래스의 소스 코드를 통해 찾을 수 있습니다. 추상 상위 클래스 AbstractList
의 add
메소드에 대한 참조가 없다는 점은 기본적으로 java.lang.UnsupportedOperationException
예외를 발생시킵니다. 🎜🎜 🎜🎜이것 함정의 근본 원인은 호출에 의해 반환된 문자열
의 add
메서드가 추상 상위 클래스의 add
메서드에서 상속된다는 것입니다. 및 추상 상위 클래스의 메소드 기본값은 java.lang.UnsupportedOperationException
예외를 발생시키는 것입니다. 🎜🎜두 번째 함정은 Arrays.asList 메소드에 의해 반환된 새 List와 메소드의 원래 매개변수 그룹 수정이 위의 경우를 제외하고 서로 영향을 미친다는 것입니다🎜🎜Arrays.asList
메소드는 🎜합니다. 요소 추가 또는 삭제는 지원되지 않습니다🎜 이 구덩이 외에도 또 다른 구덩이가 있습니다: 🎜🎜배열을 통해 얻은 새 <code>목록
에 영향을 미치는 것을 알 수 있습니다. asList 메소드를 자세히 살펴보세요java.util.Arrays.ArrayList: 🎜rrreee🎜원본 배열을 그대로 사용하고 있는 것을 알 수 있으므로 를 사용하면 <code>List
를 얻기 위한 >Arrays.asList 메소드는 특히 주의해야 합니다. 배열이 공유되기 때문에 서로를 수정할 때 예상치 못한 버그가 발생할 수 있습니다. 표준 작업 중 하나는 List
를 new
하기 위한 ArrayList
생성자의 매개변수로 사용하는 것입니다(예: List<string> ; stringList = new ArrayList(Arrays.asList(arrays))</string>
) 또는 Guava
라이브러리의 Lists.newArrayList
를 통해 새 는 반환된 코드>목록
은 원래 배열에서 분리되어 더 이상 서로 영향을 주지 않습니다. 🎜🎜세 번째 함정은 List 컬렉션을 직접 순회하고 요소를 삭제하면 오류가 보고된다는 것입니다. 🎜🎜컬렉션 요소를 직접 순회하면서 요소를 추가하거나 삭제하면 예를 들어 실행하면 오류가 보고됩니다. 🎜rrreee🎜위 코드는 정상적으로 컴파일이 가능하지만, 실행 시 java.util.ConcurrentModificationException
예외가 발생합니다. 요소 메소드 remove
는 컬렉션 구조를 수정합니다. 즉, modCount(
컬렉션에 대한 실제 수정 횟수)는 루프 중에 실제 수정 횟수가 수정됩니다. 현재 List
컬렉션의 modCount
는 반복기 expectedModCount
에 대한 수정 횟수와 비교되며 expectedModCount
는 초기화 중 modCount
가 동일하지 않으면 ConcurrentModificationException
예외가 보고됩니다. 두 가지 주요 해결 방법이 있습니다. 1. ArrayList
의 반복자 모드를 사용하여 순회한 다음 그 안에 있는 메서드를 호출합니다. 2. JDK 1.8 이상에서는 removeIf
메서드를 사용하여 삭제 작업을 수행할 수 있습니다. 🎜🎜마지막 질문: ArrayList
의 remove
메소드를 호출하고 int
기본 유형 번호와 Integer
를 전달합니다. 포장 유형 번호에 대한 실행 결과는 동일합니까? 🎜위 내용은 Java 컬렉션 클래스 List의 함정 기록의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!