1、List(接上级--常用方法示例补充)
1.4 常用的方法
1.4.2 LinkedList(JDK8)
LinkedList是Java中一个实现了List接口和Deque接口的类,它采用链表结构存储数据,支持高效的插入和删除操作。
LinkedList中的所有方法及使用
使用示例:
1、构造方法
LinkedList<String> list = new LinkedList<>();
2、添加元素
// 在链表末尾添加元素
list.add("Apple");
// 在指定索引位置插入元素
list.add(0, "Banana");
3、删除元素
// 删除第一个元素(头节点)
String removedHead = list.removeFirst();
// 删除最后一个元素(尾节点)
String removedTail = list.removeLast();
// 删除特定对象首次出现的位置
if (list.contains("Apple")) {
list.removeFirstOccurrence("Apple");
}
// 删除特定对象最后一次出现的位置
if (list.contains("Apple")) {
list.removeLastOccurrence("Apple");
}
// 移除链表中的第一个元素
list.remove();
// 根据索引删除元素
list.remove(0);
// 移除指定元素(首次出现的)
list.remove("Apple"); // 如果存在的话,移除Apple
// 移除所有指定元素集合
list.removeAll(java.util.Arrays.asList("Apple", "Banana")); // 移除所有Apple和Banana(如果存在)
4、获取元素
// 获取第一个元素(头节点)
String firstItem = list.getFirst();
// 获取最后一个元素(尾节点)
String lastItem = list.getLast();
// 获取索引位置的元素
String itemAtIndex = list.get(0);
5、设置元素
// 设置第一个元素(头节点)
list.setFirst("Cherry");
// 设置最后一个元素(尾节点)
list.setLast("Cherry");
// 根据索引设置元素
list.set(0, "Cherry");
6、遍历和查找元素
// 使用迭代器遍历
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
// 使用for-each循环遍历
for (String element : list) {
System.out.println(element);
}
// 查找元素是否存在
boolean contains = list.contains("Cherry");
// 获取元素的索引
int index = list.indexOf("Cherry");
// 查找指定元素最后一次出现的索引
int lastIndex = list.lastIndexOf("Banana"); // 查找Banana的最后一个索引(如果只有一个,则和indexOf一样)
7、其他操作
// 获取列表大小(元素数量)
int size = list.size();
// 清空列表
list.clear();
// 判断列表是否为空
boolean isEmpty = list.isEmpty();
// 在链表头部添加元素
list.addFirst("Orange");
// 在链表尾部添加元素
list.addLast("Grape");
// 从链表头部弹出并返回元素
String poppedHead = list.pop();
// 从链表尾部移除并返回元素
String polledTail = list.pollLast();
// 检查链表是否包含另一个集合的所有元素
boolean allElementsPresent = list.containsAll(anotherList);
// 移除链表中所有与另一个集合相同的元素
list.removeAll(anotherList);
// 截取子列表
List<String> sublist = list.subList(fromIndex, toIndex);
8、排序操作
由于LinkedList实现了List接口,因此可以使用Collections.sort()进行排序,但请注意这将创建一个新的ArrayList来完成排序,并在排序后替换原链表内容。
Collections.sort(list);
如果是对某个实体类,需要根据不同的字段或逻辑来排序,也可以创建一个Comparator
:
例:
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
public class LinkedListExample {
public static void main(String[] args) {
LinkedList<Person> list = new LinkedList<>();
list.add(new Person("Alice", 25));
list.add(new Person("Bob", 20));
list.add(new Person("Charlie", 30));
// 自定义排序,按照年龄升序排序
Collections.sort(list, new Comparator<Person>() {
@Override
public int compare(Person p1, Person p2) {
return Integer.compare(p1.age, p2.age);
}
});
// 输出: [Bob, Alice, Charlie] 此处Person类需重写toString方法
System.out.println(list);
}
}
class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return name + " " + age;
}
}
或者是使用 Java 8 的 Lambda 表达式进行排序:
import java.util.Collections;
import java.util.LinkedList;
import java.util.Comparator;
public class LinkedListExample {
public static void main(String[] args) {
LinkedList<Person> list = new LinkedList<>();
list.add(new Person("Alice", 25));
list.add(new Person("Bob", 20));
list.add(new Person("Charlie", 30));
// 使用 Lambda 表达式进行自定义排序,按照年龄升序排序 :Person类需有getAge方法
Collections.sort(list, Comparator.comparingInt(Person::getAge));
// 输出: [Bob, Alice, Charlie]
System.out.println(list);
}
}
class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return name + " " + age;
}
}
适合使用的场景:
- 频繁增删的场景:LinkedList在插入和删除元素时具有较高的效率,特别适用于需要频繁进行这些操作的情况。例如,当需要在列表的头部或中间位置插入或删除元素时,LinkedList是一个很好的选择。
- 头尾操作或插入指定位置的场景:LinkedList允许在链表的两端以及任何指定位置进行高效的插入和删除操作。这使得它在需要灵活操作列表不同位置的场景中特别有用。
- 顺序访问的场景:如果应用需要按照顺序访问列表中的元素,而不是随机访问,那么LinkedList可能是一个合适的选择。因为LinkedList是通过节点链接实现的,所以在顺序访问时性能较好。
注意:
虽然LinkedList在插入和删除操作上具有优势,但在随机访问方面可能不如ArrayList高效。因此,在选择使用LinkedList还是其他数据结构时,需要根据具体应用场景的性能需求进行权衡。
此外,还需要注意LinkedList的内存使用情况。由于LinkedList的每个节点都需要消耗一定的空间来存储数据和指针,因此在处理大量数据时,可能会占用更多的内存。因此,在使用LinkedList时,也需要关注内存管理和优化。
使用场景示例:
- 实现队列或栈:LinkedList可以作为队列(Queue)或栈(Stack)的数据结构实现。队列是一种先进先出(FIFO)的数据结构,常用于处理需要按顺序处理的任务或事件,如打印任务队列。栈是一种后进先出(LIFO)的数据结构,常用于需要按相反顺序处理元素的情况,如函数调用栈。LinkedList的节点可以通过其指针关系方便地实现这些操作。
- 链表操作算法:在需要实现链表相关的算法时,LinkedList是一个很好的选择。例如,你可以使用LinkedList来实现链表的合并、反转、排序等算法。这些算法通常需要对链表进行频繁的插入和删除操作,LinkedList的高效性使得这些算法的实现更加高效。
- 动态插入和删除元素:在某些应用中,可能需要频繁地在列表的任意位置插入或删除元素。例如,在一个需要用户动态添加和删除任务的待办事项列表中,LinkedList就能够提供高效的插入和删除操作。由于LinkedList不需要像ArrayList那样移动大量元素来保持连续的内存空间,因此在这种场景下,LinkedList的性能优势会更为明显。
使用时需要注意的问题:
在使用LinkedList时,需要注意以下几个关键问题:
节点数据的准确性:确保链表中的每个节点都包含正确的数据。在插入或修改节点数据时,需要仔细核对数据的正确性,避免因为数据错误导致链表的状态不一致。
节点指针的正确性:LinkedList中的每个节点都有一个指向下一个节点的指针。在插入、删除或修改节点时,必须确保这些指针的正确性。如果指针设置错误,可能会导致链表断裂或形成循环,从而破坏链表的完整性。
内存管理:在创建新节点时,需要分配内存;在删除节点时,需要释放内存。必须确保在合适的时机进行内存分配和释放,避免内存泄漏或内存不足的问题。
并发安全:LinkedList不是线程安全的。在多线程环境下,如果多个线程同时修改LinkedList,可能会导致数据不一致或其他并发问题。因此,在并发环境下使用LinkedList时,需要采取适当的同步措施,例如使用锁或同步块来保护链表的访问和修改操作。
遍历和访问方式:LinkedList是基于节点链接实现的,因此在遍历和访问链表元素时,需要使用迭代器或循环遍历的方式。在遍历过程中,需要注意避免死循环或跳过某些节点的问题。同时,由于LinkedList在随机访问方面不如ArrayList高效,因此在需要频繁进行随机访问的场景下,ArrayList可能更合适。
性能考虑:虽然LinkedList在插入和删除元素时具有较高的效率,但在遍历元素时可能不如ArrayList快。因此,在选择使用LinkedList还是ArrayList时,需要根据具体应用场景的性能需求进行权衡。
LinkedList的扩容?
注意:LinkedList不存在扩容的说法
因为LinkedList是基于双向链表实现的,它没有固定的初始化大小,也没有特定的扩容机制。原因在于链表结构的特点使其可以在需要时动态地添加或删除节点,而不需要像数组那样预先分配或调整固定大小的内存空间。因此,LinkedList在添加元素时,只需要在链表头部或尾部创建新的节点,或者在指定位置插入新的节点即可。