满地坑!细数List的十个坑!("避坑指南:List使用中的十大常见陷阱解析!")

原创
ithorizon 6个月前 (10-20) 阅读数 15 #后端开发

避坑指南:List使用中的十大常见陷阱解析!

一、List的长度可变,但并非没有代价

Java中的List是一个动态数组,其长度可以结合需要自动增长。然而,这种增长并非没有代价。每次List需要增长时,都会创建一个新的数组,并将旧数组的内容复制到新数组中。这是一个相对高价的操作,尤其是在List已经很大的时候。

List list = new ArrayList<>();

for (int i = 0; i < 1000000; i++) {

list.add("Item " + i);

}

// 此处添加元素也许致使性能问题

二、使用List的remove方法时要注意索引

使用List的remove方法时,需要传入要删除元素的索引。如果传入的索引超出List的范围,或者List为空,则会抛出IndexOutOfBoundsException。此外,删除元素后,List的大小会变化,后面的元素会前移,这也许会致使一些意外的失误。

List list = Arrays.asList("A", "B", "C", "D");

list.remove(1); // 删除索引为1的元素,即"B"

System.out.println(list); // 输出: [A, C, D]

list.remove(2); // 此处如果直接写list.remove(3),会抛出IndexOutOfBoundsException

三、使用List的get方法时也要注意索引

与remove方法类似,使用List的get方法时也需要传入索引。如果传入的索引超出List的范围,则会抛出IndexOutOfBoundsException。在处理用户输入或者其他不确定的数据源时,一定要检查索引的有效性。

List list = Arrays.asList("A", "B", "C", "D");

String item = list.get(1); // 获取索引为1的元素,即"B"

String itemOutOfRange = list.get(4); // 此处会抛出IndexOutOfBoundsException

四、遍历List时不要在循环中修改List

在遍历List的过程中,如果尝试修改List(如添加、删除元素),则也许会致使ConcurrentModificationException。这是基于List的迭代器在遍历时会检查List的结构是否出现了变化。

List list = Arrays.asList("A", "B", "C", "D");

for (String item : list) {

if ("B".equals(item)) {

list.remove(item); // 此处会抛出ConcurrentModificationException

}

}

五、List的迭代器不赞成set操作

虽然List的迭代器赞成remove操作,但它不赞成set操作。如果你尝试在迭代器中使用set方法来修改List中的元素,会抛出UnsupportedOperationException。

List list = Arrays.asList("A", "B", "C", "D");

Iterator iterator = list.iterator();

while (iterator.hasNext()) {

String item = iterator.next();

if ("B".equals(item)) {

iterator.set("X"); // 此处会抛出UnsupportedOperationException

}

}

六、使用List的contains方法时要注意元素的equals方法

List的contains方法用于检查List中是否包含某个元素,它依靠于元素的equals方法。如果自定义的类没有正确覆盖equals方法,那么contains方法也许无法正确工作。

class CustomObject {

private int value;

public CustomObject(int value) {

this.value = value;

}

// 没有覆盖equals方法

}

List list = new ArrayList<>();

list.add(new CustomObject(1));

list.add(new CustomObject(2));

boolean contains = list.contains(new CustomObject(1)); // 此处也许返回false

七、使用List的indexOf和lastIndexOf方法时要注意顺序

indexOf方法返回List中第一次出现指定元素的位置,而lastIndexOf方法返回最后一次出现指定元素的位置。如果List中的元素顺序出现了变化,这两个方法的最终也会有所不同。

List list = Arrays.asList("A", "B", "C", "B");

int firstIndex = list.indexOf("B"); // 返回1

int lastIndex = list.lastIndexOf("B"); // 返回3

八、List的subList方法返回的是视图,而非副本

subList方法返回的是原List的一个视图,而非副本。这意味着对subList的修改会影响到原List,反之亦然。

List list = new ArrayList<>(Arrays.asList("A", "B", "C", "D"));

List sublist = list.subList(1, 3);

sublist.add("X"); // 此处会修改原List

System.out.println(list); // 输出: [A, B, X, C, D]

九、使用List的toArray方法时要注意类型转换

当使用List的toArray方法时,如果不指定数组类型,则会返回一个Object数组。如果需要其他类型的数组,需要进行类型转换,并且要确保不会出现ClassCastException。

List list = Arrays.asList("A", "B", "C");

String[] array = list.toArray(new String[0]); // 正确的类型转换

Object[] objectArray = list.toArray(); // 返回Object数组

String[] wrongArray = (String[]) list.toArray(); // 此处也许抛出ClassCastException

十、避免使用List作为线程共享数据结构

List不是线程可靠的,如果多个线程同时访问同一个List,并且至少有一个线程在结构上修改了List,则必须保持外部同步。否则,也许会致使不可预知的行为和失误。

List list = new ArrayList<>();

synchronized (list) {

list.add("A");

list.add("B");

// 在同步块内操作List

}

// 使用同步方法或同步块来确保线程可靠

以上是List使用中的十大常见陷阱,期待这些解析能帮助开发者更好地领会和使用List,避免在实际开发中遇到这些问题。


本文由IT视界版权所有,禁止未经同意的情况下转发

文章标签: 后端开发


热门