思维导图:
7.3 List接口
7.3.1 List接口简介
- 继承自Collection接口:List是Collection的子接口。
- 元素特性:
- 允许重复元素。
- 元素以线性方式存储。
- 通过索引访问元素。
- 保持元素的添加顺序。
- 方法扩展:List接口不仅继承了Collection接口的所有方法,还增加了基于索引操作的特有方法。
常用方法
void add(int index, Object element)
- 插入元素:在List的指定索引处插入元素element。
boolean addAll(int index, Collection c)
- 插入集合:将集合c中的所有元素插入List的指定索引处。
Object get(int index)
- 获取元素:返回List中指定索引处的元素。
Object remove(int index)
- 删除元素:移除List中指定索引处的元素。
Object set(int index, Object element)
- 替换元素:将List中指定索引处的元素替换为element,并返回原元素。
int indexOf(Object o)
- 查找元素:返回对象o在List中首次出现的索引。
int lastIndexOf(Object o)
- 查找元素:返回对象o在List中最后一次出现的索引。
List subList(int fromIndex, int toIndex)
- 子集合:返回从索引fromIndex(包括)到toIndex(不包括)的子集合。
List接口的实现
- 实现类操作:List接口的所有实现类(如ArrayList、LinkedList等)都支持上述方法,允许通过这些方法操作集合元素。
总结
List接口为集合元素提供了顺序存储和索引访问的能力,是处理有序集合时的首选接口。掌握List接口的这些常用方法对于有效地处理列表数据至关重要。理解和熟练使用这些方法有助于在Java编程中灵活地处理数据集合。
我的理解:
1. void add(int index, Object element)
- 适用场景:在List的特定位置插入元素。
- 使用技巧:确保index在List的现有大小范围内。
- 易错点:索引超出范围(IndexOutOfBoundsException)。
2. boolean addAll(int index, Collection c)
- 适用场景:在特定位置插入另一个集合的所有元素。
- 使用技巧:确保index合适,以及被添加集合与List兼容。
- 易错点:索引超出范围或集合类型不匹配。
3. Object get(int index)
- 适用场景:获取List中特定位置的元素。
- 使用技巧:确保索引值在有效范围内。
- 易错点:索引超出范围或List为空时尝试获取元素。
4. Object remove(int index)
- 适用场景:移除List中特定位置的元素。
- 使用技巧:在移除元素后记得更新索引,因为List大小会改变。
- 易错点:索引超出范围或在迭代过程中直接使用此方法。
5. Object set(int index, Object element)
- 适用场景:替换List中特定位置的元素。
- 使用技巧:确保替换的元素与List兼容。
- 易错点:索引超出范围或替换类型不匹配。
6. int indexOf(Object o)
- 适用场景:查找元素在List中首次出现的位置。
- 使用技巧:适用于确定元素是否存在于List中及其位置。
- 易错点:对于自定义对象,未正确重写equals方法可能导致查找失败。
7. int lastIndexOf(Object o)
- 适用场景:查找元素在List中最后一次出现的位置。
- 使用技巧:在查找元素时,从List的尾部开始查找。
- 易错点:类似于indexOf,未正确实现equals方法可能影响结果。
8. List subList(int fromIndex, int toIndex)
- 适用场景:创建List的一个子集合。
- 使用技巧:子集合是原始List的视图,对其的更改会反映在原List中。
- 易错点:修改子集合时可能不了解其对原List的影响。
小贴士
- 在使用List的索引操作时,始终注意索引边界,以避免IndexOutOfBoundsException。
- 理解List方法与迭代器使用时的并发修改问题,特别是在多线程环境下。
- 当操作自定义对象时,确保正确实现了equals和hashCode方法,以便正确地使用List的方法,如indexOf和lastIndexOf。
7.3.2 ArrayList
ArrayList简介
- 实现类:ArrayList是List接口的一个实现。
- 内部结构:使用可变长度数组作为内部存储机制。
- 动态扩容:当元素数量超过数组容量时,ArrayList会创建一个更大的数组。
- 元素插入过程:图7-2展示了ArrayList如何在内存中扩展数组来存储更多元素。
ArrayList特性
- 允许重复元素:与List接口一致,允许存储重复元素。
- 有序集合:保持元素的插入顺序。
- 索引访问:可以通过索引快速访问元素。
常用操作
- 元素添加:
add(Object o)
方法用于添加元素。 - 元素获取:
get(int index)
方法用于通过索引获取元素。 - 集合大小:
size()
方法用于获取集合中元素的个数。 - 元素删除:
remove(int index)
方法用于删除指定索引的元素。 - 元素替换:
set(int index, Object element)
方法用于替换指定索引处的元素。
示例
- 创建ArrayList:
ArrayList list = new ArrayList();
- 添加元素:
list.add("张三");
- 获取元素:
String element = list.get(1);
- 删除元素:
list.remove(3);
- 替换元素:
list.set(1, "李四2");
注意事项
- 索引从0开始:与数组类似,ArrayList的索引从0开始。
- 索引越界异常:访问超出范围的索引会导致
IndexOutOfBoundsException
。 - 性能考虑:由于底层是数组,频繁的插入和删除操作(尤其是在列表中间)效率较低,因此ArrayList更适合查找操作。
总结
ArrayList是一个灵活且广泛使用的集合类,其基于数组的内部结构使其在元素访问方面表现出色,但在进行大量插入和删除操作时效率较低。理解ArrayList的这些特性和限制对于在Java编程中合理选择和使用集合类至关重要。
我的理解:
1. add(Object o)
- 适用场景:向ArrayList中添加元素。
- 使用技巧:添加元素时无需指定数组大小,ArrayList会自动扩容。
- 易错点:在循环中添加大量元素时可能导致性能问题。
2. get(int index)
- 适用场景:获取ArrayList中指定索引的元素。
- 使用技巧:由于ArrayList提供快速随机访问,可用于高效的索引查询。
- 易错点:索引超出范围时会抛出
IndexOutOfBoundsException
。
3. size()
- 适用场景:获取ArrayList中的元素总数。
- 使用技巧:可用于循环遍历和条件检查。
- 易错点:与数组的length属性不同,这是一个方法调用。
4. remove(int index)
- 适用场景:删除ArrayList中指定索引的元素。
- 使用技巧:理解删除元素后,后续元素索引会发生变化。
- 易错点:删除元素后继续使用原索引访问可能导致错误。
5. set(int index, Object element)
- 适用场景:替换ArrayList中指定索引处的元素。
- 使用技巧:可以用于更新已存在的元素值。
- 易错点:同样需注意索引范围,防止
IndexOutOfBoundsException
。
6. addAll(Collection c)
- 适用场景:将另一个集合中的所有元素添加到ArrayList中。
- 使用技巧:适合于合并两个集合。
- 易错点:确保被添加的集合与ArrayList类型兼容。
7. clear()
- 适用场景:清空ArrayList中的所有元素。
- 使用技巧:快速清除所有元素,而不需要逐个删除。
- 易错点:一旦调用,所有元素将被删除,操作不可逆。
8. contains(Object o)
- 适用场景:判断ArrayList是否包含特定元素。
- 使用技巧:用于检查元素是否存在于集合中。
- 易错点:对于自定义对象,确保正确实现了equals方法。
9. iterator()
- 适用场景:遍历ArrayList中的所有元素。
- 使用技巧:使用迭代器可以安全地遍历集合,并在需要时删除元素。
- 易错点:在迭代过程中修改集合(除了通过迭代器本身的方法)可能导致并发修改异常。
小贴士
- 在使用ArrayList时,重点关注性能问题,特别是在添加或删除元素时考虑到其背后的数组复制操作。
- 对于索引操作,始终保持警觉,避免出现索引越界的错误。
7.3.3 LinkedList
LinkedList简介
- List接口实现:LinkedList是List接口的一个实现。
- 内部结构:内部使用双向循环链表实现,每个元素都记录着前后元素的引用。
- 增删效率:由于链表结构,插入和删除操作效率高。
LinkedList的特有操作
void add(int index, E element)
- 功能:在指定索引处插入元素。
void addFirst(Object o)
- 功能:在链表开头插入元素。
void addLast(Object o)
- 功能:在链表末尾添加元素。
Object getFirst()
- 功能:返回链表的第一个元素。
Object getLast()
- 功能:返回链表的最后一个元素。
Object removeFirst()
- 功能:移除并返回链表的第一个元素。
Object removeLast()
- 功能:移除并返回链表的最后一个元素。
boolean offer(Object o)
- 功能:将元素添加到链表末尾。
Object peekFirst()
- 功能:获取链表的第一个元素。
Object pollFirst()
- 功能:移除并返回链表的第一个元素。
void push(Object o)
- 功能:将元素推入链表头部。
使用场景和例子
- 创建LinkedList:
LinkedList<String> link = new LinkedList<>();
- 添加元素:
link.add("张三");
,link.addFirst("First");
- 获取元素:
String firstElement = link.getFirst();
- 移除元素:
link.remove(3);
,link.removeFirst();
LinkedList的优势
- 快速增删:在链表头部或尾部的操作尤其高效。
- 灵活的链表操作:提供了大量的链表操作方法,使得数据操作更加灵活。
注意事项
- 索引操作性能:由于是链表,索引操作(如
get(int index)
)性能低于ArrayList。 - 空间消耗:每个元素都有额外的前后引用,相比于ArrayList有更多的空间开销。
总结
LinkedList提供了丰富的链表操作方法,非常适合在需要频繁进行插入和删除操作的场景中使用,尤其是在列表的头部和尾部。然而,需要注意的是,由于其链表的性质,某些操作(如随机访问)的效率可能不如基于数组的实现(如ArrayList)。理解LinkedList的特性和适用场景对于高效使用Java集合类至关重要。
我的理解:
LinkedList方法的适用场景、使用技巧及易错点
1. void add(int index, E element)
- 适用场景:在LinkedList的特定位置插入元素。
- 使用技巧:适用于有序列表中特定位置的插入操作。
- 易错点:索引超出范围时会抛出
IndexOutOfBoundsException
。
2. void addFirst(Object o)
和 void addLast(Object o)
- 适用场景:在链表的开头或结尾添加元素。
- 使用技巧:适用于实现栈或队列等数据结构。
- 易错点:与ArrayList的
add()
方法不同,addFirst()
和addLast()
在特定位置插入。
3. Object getFirst()
和 Object getLast()
- 适用场景:获取链表的第一个或最后一个元素。
- 使用技巧:快速访问链表的端点元素。
- 易错点:当链表为空时调用这些方法会抛出
NoSuchElementException
。
4. Object removeFirst()
和 Object removeLast()
- 适用场景:移除链表的第一个或最后一个元素。
- 使用技巧:用于实现队列或双端队列的操作。
- 易错点:在空链表上调用会抛出
NoSuchElementException
。
5. boolean offer(Object o)
和 boolean offerFirst(Object o)
和 boolean offerLast(Object o)
- 适用场景:在链表的开始或结束处添加元素,但在添加失败时返回false而不是抛异常。
- 使用技巧:提供了更安全的元素添加方式。
- 易错点:与
addFirst()
和addLast()
相比,offer()
系列方法在添加失败时不会抛出异常。
6. Object peekFirst()
和 Object peekLast()
- 适用场景:查看但不移除链表的第一个或最后一个元素。
- 使用技巧:适用于仅检查而非操作链表端点元素。
- 易错点:与
getFirst()
/getLast()
不同,链表为空时这些方法返回null而不是抛异常。
7. Object pollFirst()
和 Object pollLast()
- 适用场景:移除并返回链表的第一个或最后一个元素,但在链表为空时返回null。
- 使用技巧:与
removeFirst()
/removeLast()
相比,提供了在空链表上的安全操作。 - 易错点:当链表为空时,这些方法返回null而不是抛出异常。
8. void push(Object o)
- 适用场景:将元素压入链表头部。
- 使用技巧:在实现栈结构时特别有用。
- 易错点:
push()
总是添加到链表的开始,与add()
和offer()
不同。
小贴士
- 使用LinkedList时,重点关注其链表特性,特别是在进行元素插入和删除操作时的高效率。
- 理解不同方法在链表为空时的行为,特别是抛出异常与返回null的差异。
- 考虑到LinkedList的索引操作(如
get(int index)
)的效率相对较低,避免在需要高效随机访问的场景中使用。
总结:
重点
- List接口的基本概念:理解List接口作为Collection的子接口,允许重复元素,且元素有序排列。
- ArrayList和LinkedList:掌握ArrayList和LinkedList两种List实现的特点和区别。ArrayList基于动态数组,适合快速随机访问;LinkedList基于链表,适合频繁的插入和删除。
- 常用方法:理解和掌握List接口的核心方法,如
add()
,get()
,set()
,remove()
等。
难点
- 理解不同实现的适用场景:区分ArrayList和LinkedList的使用场景和性能特点,如ArrayList适用于随机访问,而LinkedList优于插入和删除操作。
- 索引操作的理解:正确理解和使用基于索引的操作,尤其是在LinkedList中,这些操作可能效率较低。
易错点
- 索引越界:在使用基于索引的方法时,如
get(index)
,set(index, element)
等,可能会遇到IndexOutOfBoundsException
。 - 修改迭代中的List:在对List进行迭代时直接进行添加或删除操作,可能导致
ConcurrentModificationException
。 - ArrayList和LinkedList的选择:在不恰当的场景中使用ArrayList或LinkedList,例如在需要频繁插入和删除的场景中使用ArrayList。
- LinkedList的特殊方法使用:例如
addFirst()
,addLast()
,removeFirst()
,removeLast()
,使用时可能与ArrayList的方法混淆。
小贴士
- 在选择List实现时,要基于实际应用的需求考虑ArrayList和LinkedList的优缺点。
- 使用List接口的索引相关操作时,始终检查索引是否超出范围。
- 在迭代List时,如果需要修改List,建议使用迭代器的
remove()
方法或Java 8的removeIf()
方法来安全地进行修改。