1 集合的一些坑
- 当集合的元素是自定义类时,自定义类强制实现 equals 和 hashCode 方法,并且两个都要实现。
在集合中,除了 TreeMap 和 TreeSet 是通过比较器比较元素大小外,其余的集合类在判断索引位置和相等时,都会使用到 equals 和 hashCode 方法,这个在之前的源码解析中,我们有说到,所以当集合的元素是自定义类时,我们强烈建议覆写 equals 和 hashCode 方法,我们可以直接使用 IDEA 工具覆写这两个方法,非常方便;
-
所有集合类,在 for 循环进行删除时,如果直接使用集合类的 remove 方法进行删除,都会快速失败,报 ConcurrentModificationException 的错误,所以在任意循环删除的场景下,都建议使用迭代器进行删除;
-
我们把数组转化成集合时,常使用 Arrays.asList(array),这个方法有两个坑,代码演示坑为:
public void testArrayToList(){
Integer[] array = new Integer[]{1,2,3,4,5,6};
List<Integer> list = Arrays.asList(array);
// 坑1:修改数组的值,会直接影响原 list
log.info("数组被修改之前,集合第一个元素为:{}",list.get(0));
array[0] = 10;
log.info("数组被修改之前,集合第一个元素为:{}",list.get(0));
// 坑2:使用 add、remove 等操作 list 的方法时,
// 会报 UnsupportedOperationException 异常
list.add(7);
}
坑 1:数组被修改后,会直接影响到新 List 的值。
坑 2:不能对新 List 进行 add、remove 等操作,否则运行时会报 UnsupportedOperationException 错误。
我们来看下 Arrays.asList 的源码实现,就能知道问题所在了,源码如下图:

- 集合 List 转化成数组,我们通常使用 toArray 这个方法,这个方法很危险,稍微不注意,就踩进大坑,我们示例代码如下:
public void testListToArray(){
List<Integer> list = new ArrayList<Integer>(){{
add(1);
add(2);
add(3);
add(4);
}};
// 下面这行被注释的代码这么写是无法转化成数组的,无参 toArray 返回的是 Object[],
// 无法向下转化成 List<Integer>,编译都无法通过
// List<Integer> list2 = list.toArray();
// 演示有参 toArray 方法,数组大小不够时,得到数组为 null 情况
Integer[] array0 = new Integer[2];
list.toArray(array0);
log.info("toArray 数组大小不够,array0 数组[0] 值是{},数组[1] 值是{},",array0[0],array0[1]);
// 演示数组初始化大小正好,正好转化成数组
Integer[] array1 = new Integer[list.size()];
list.toArray(array1);
log.info("toArray 数组大小正好,array1 数组[3] 值是{}",array1[3]);
// 演示数组初始化大小大于实际所需大小,也可以转化成数组
Integer[] array2 = new Integer[list.size()+2];
list.toArray(array2);
log.info("toArray 数组大小多了,array2 数组[3] 值是{},数组[4] 值是{}",array2[3],array2[4]);
}
19:33:07.687 [main] INFO demo.one.ArrayListDemo - toArray 数组大小不够,array0 数组[0] 值是null,数组[1] 值是null,
19:33:07.697 [main] INFO demo.one.ArrayListDemo - toArray 数组大小正好,array1 数组[3] 值是4
19:33:07.697 [main] INFO demo.one.ArrayListDemo - toArray 数组大小多了,array2 数组[3] 值是4,数组[4] 值是null
toArray 的无参方法,无法强转成具体类型,这个编译的时候,就会有提醒,我们一般都会去使用带有参数的 toArray 方法,这时就有一个坑,如果参数数组的大小不够,这时候返回的数组值竟然是空,上述代码中的 array0 的返回值就体现了这点,但我们去看 toArray 源码,发现源码中返回的是 4 个大小值的数据,返回的并不是空,源码如下:
// List 转化成数组
public <T> T[] toArray(T[] a) {
// 如果数组长度不够,按照 List 的大小进行拷贝,return 的时候返回的都是正确的数组
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
// 数组长度大于 List 大小的,赋值为 null
if (a.length > size)
a[size] = null;
return a;
}
从源码中,我们丝毫看不出为什么 array0 的元素值为什么是 null,最后我们去看方法的注释,发现是这样子描述的:
If the list fits in the specified array, it is returned therein. Otherwise, a new array is
allocated with the runtime type of the specified array and the size of this list。
翻译过来的意思就是说:如果返回的数组大小和申明的数组大小一致,那么就会正常返回,否则,一个新数组就会被分配返回。
所以我们在使用有参 toArray 方法时,申明的数组大小一定要大于等于 List 的大小,如果小于的话,你会得到一个空数组。
来源:oschina
链接:https://my.oschina.net/u/4010457/blog/3165445