<注> Hello~ 此篇博客需要一丢丢的Java基础,更适合找工作的中国宝宝观看噢。如有错误,欢迎在评论区指出~
目录
1、ArrayList
a) 概念
ArrayList是一个动态数组实现的类,它可以根据需要自动增长或缩小。ArrayList实现了List接口,可以存储任意类型的对象,包括基本数据类型和对象类型。使用ArrayList时,需要先导入java.util包,然后可以通过以下代码创建一个ArrayList对象。
import java.util.ArrayList;
ArrayList<String> list = new ArrayList<>();
ArrayList 底层数组默认初始化容量为10,jdk1.8中 ArrayList底层先创建一个长度为0的数组,当第一次添加元素(调用add()方法)时,会初始化一个长度为10的数组。当ArrayList的容量使用完后,会“自动”创建容量更大的数组,并将原数组中的 所有元素拷贝过去,但这会导致效率降低。
优化方法:使用构造方法 ArrayList(int capacity)或 ensureCapacity(int capacity) 提供一个初始化容量,避免刚开始就一直扩容,造成效率较低。
b) 构造方法
1. ArrayList():创建一个初始化容量为10的空列表。
2. ArrayList(Collection<? extends E> c):创建一个包含指定集合c中所有元素的列表。
3. ArrayList(int initialCapacity):创建一个指定初始容量为 initialCapacity 的列表。
ArrayList<String> list1 = new ArrayList<>();
ArrayList<String> list2 = new ArrayList<>(List.of("apple", "banana", "orange"));
ArrayList<Integer> list3 = new ArrayList<>(20);
在这三个例子中,list1是一个空的ArrayList,list2包含了元素"apple"、"banana"和"orange",list3是一个初始容量为20的ArrayList。
<Tips>
(1)Collection<? extends E>c 表示一个元素类型为E或者E的子类的集合。这种语法可以用来表示集合c中的元素类型的上界,即集合c中的元素类型必须是E或者E的子类。
(1)Collection<?> c 表示一个未知类型的集合,即其中元素类型未知或不确定,可以存储任意类型的元素。
c) 方法(了解即可)
1. add(E element): 将指定元素添加到列表的末尾。
2. remove(int index): 删除指定索引位置的元素。
3. get(int index): 返回指定索引位置的元素。
4. set(int index, E element): 将指定索引位置的元素替换为新元素。
5. size(): 返回列表中元素的数量。
6. clear(): 清空列表中的所有元素。
7. isEmpty(): 检查列表是否为空。
8. contains(Object obj): 检查列表是否包含指定元素。
9. indexOf(Object obj): 返回指定元素在列表中第一次出现的索引位置。
10. addAll(Collection<? extends E> c): 将指定集合中的所有元素添加到列表的末尾。
11. removeAll(Collection<?> c): 删除列表中包含在指定集合中的所有元素。
12. retainAll(Collection<?> c): 保留列表中包含在指定集合中的所有元素,删除其他元素。
13. subList(int fromIndex, int toIndex): 返回列表中指定范围的子列表。
14. toArray(): 将列表转换为数组。
d) 特点
- 可变大小:可以根据需要动态增加或减少元素的个数。
- 支持泛型:可以指定ArrayList存储的元素类型,避免了类型转换的麻烦。
- 随机访问:可以通过索引值快速访问ArrayList中的元素。
- 支持迭代:可以使用迭代器或增强for循环来遍历ArrayList中的元素。
e) 优点
- 可以动态增长:ArrayList的大小是动态增长的,可以根据需要添加或删除元素,不需要提前指定大小。
- 随机访问元素快速:由于ArrayList基于数组实现,可以通过索引快速访问元素。
- 支持泛型:ArrayList支持泛型,可以指定存储的元素类型,提高代码的类型安全性。
- 提供丰富的操作方法:ArrayList提供了丰富的方法来操作集合,如添加、删除、查找等。
f)缺点
- 插入和删除元素效率低:由于ArrayList基于数组实现,插入和删除元素时需要移动其他元素,效率较低(向ArrayList插入或删除较少元素时,不影响效率)。
- 需要频繁扩容:当ArrayList的大小超过初始容量时,需要进行扩容操作,会导致性能下降。
- 不支持基本数据类型:ArrayList只能存储对象类型,对于基本数据类型需要进行装箱和拆箱操作,影响性能。
- 线程不安全:ArrayList不是线程安全的,在多线程环境下需要额外处理同步操作。
- 无法存储大量数据:很难找到一块很大的连续内存空间。
g) 如何将ArrayList变成线程安全的?
调用Collections类中的synchronizedList方法将ArrayList转换为线程安全的List。
public static void main(String[] args) {
List<String> list = new ArrayList<>();
List<String> synchronizedList = Collections.synchronizedList(list);
// 在多个线程中操作线程安全的ArrayList
Thread thread1 = new Thread(() -> {
synchronizedList.add("A");
synchronizedList.add("B");
synchronizedList.add("C");
});
需要注意的是,虽然使用synchronizedList方法可以确保线程安全,但在高并发情况下可能会影响性能,因此在选择数据结构时需要根据具体需求和场景进行选择。
<Tips>
(1)线程安全:是指在多线程环境下,程序能够正确、可靠地运行,不会发生数据竞争、死锁等问题。一个线程安全的程序可以保证多个线程同时访问共享资源时,不会出现数据不一致、数据丢失等问题。
(2)synchronized:是Java中用来实现同步的关键字。当一个方法或代码块被synchronized修饰时,只有一个线程可以访问该方法或代码块,其他线程必须等待当前线程执行完毕才能访问。在多线程编程中,确保线程安全是非常重要的,使用synchronized关键字可以避免多个线程同时访问共享资源而导致的数据不一致或竞争条件的问题。
2、LinkedList
a) 概念
LinkedList是一个双向链表实现的类,它实现了List和Deque接口,可以用来存一组有序的元素。
<Tips> 与ArrayList相比,LinkedList在插入和删除元素时性能更好,但在随机访问元素时性能较差。LinkedList的每个元素都包含对前一个和后一个元素的引用,因此可以在O(1)时间内插入和删除元素。
b) 构造方法
- LinkedList():创建一个空的LinkedList。
- LinkedList(Collection<? extends E> c):创建一个包含指定集合中所有元素的LinkedList。
LinkedList<String> list1 = new LinkedList<>();
LinkedList<String> list2 = new LinkedList<>(Arrays.asList("A", "B", "C"));
在这两个例子中,list1是一个空的LinkedList,list2包含了元素"A"、"B"和"C"。
c) 方法(了解即可)
- addFirst(): 在链表的开头添加一个新节点。
- addLast(): 在链表的末尾添加一个新节点。
- removeFirst(): 移除链表的第一个节点。
- removeLast(): 移除链表的最后一个节点。
- getFirst(): 返回链表的第一个节点。
- getLast(): 返回链表的最后一个节点。
- size(): 返回链表中节点的数量。
- isEmpty(): 判断链表是否为空。
- clear(): 清空链表中的所有节点。
- contains(): 判断链表中是否包含指定的元素。
- indexOf(): 返回指定元素在链表中的索引位置。
- get(): 返回指定索引位置的节点值,每次查找都要从头节点开始遍历。
- set(): 设置指定索引位置的节点值。
- remove(): 移除链表中的 第一个元素。
- add(): 在链表末尾添加一个元素。
如果要将元素被添加到链表中间的某个指定位置,需要使用ListIterator接口的add方法。
d) 特点
- 双向链表,每个节点都包含一个指向前一个节点和后一个节点的引用。
- 插入和删除操作效率较高,因为只需要改变相邻节点的引用即可。
- 不支持随机访问,需要通过遍历来访问特定位置的元素。
- 空间效率较低,因为每个节点都需要额外的空间来存储前后节点的引用。
- 可以用作队列或栈的实现,也可以用作双端队列的实现。
e) 优点
- 操作效率较高,在插入和删除元素时,只需要改变相邻节点的引用即可。
- 可以快速地在任意位置插入或删除元素,而不需要移动其他元素。
- 可以实现队列或栈等数据结构。
f)缺点
- 访问效率较低,访问元素时需要遍历整个链表,尤其是在大量数据时。
- 占用的内存空间比较大,每个节点都需要额外的存储空间来保存指向下一个节点的引用。
- 不支持随机访问,只能通过遍历来查找元素,不适合需要频繁访问指定位置的情况。
g) add()方法源码解读
public boolean add(E e) {
linkLast(e); //向链表末尾添加元素e
return true;
}
void linkLast(E e) {
final Node<E> l = last; //暂时保存最后一个元素的指针
final Node<E> newNode = new Node<>(l, e, null);
//创建了一个新的Node对象,并将其赋值给名为newNode的变量。
//这个Node对象包含了三个参数:
//l表示节点的前一个节点,e表示节点的元素值,null表示节点的下一个节点为空
last = newNode; //newNode作为最后一个节点
if (l == null) //当前链表为空
first = newNode; //newNode作为第一个添加的节点
else //链表不空
l.next = newNode; //newNode作为当前链表的最后一个节点
size++; //链表长度+1
modCount++; //modCount作为一个用于记录链表被修改的次数的计数器
}
3、ListIterator
a) 概念
ListIterator 是 Iterator 的一个子接口,它扩展了Iterator接口,提供了在List集合中双向遍历和修改元素的功能。ListIterator可以在列表中向前和向后遍历,还可以添加、修改、删除元素。
<Tips>
(1)ListIterator接口只能用于List集合,例如ArrayList、LinkedList等。要获取ListIterator对象,可以通过List集合的ListIterator() 方法来获取。
(2)接口是一种抽象类型,用关键字"interface"来定义,它定义了一组抽象方法,但没有具体的实现。接口可以被类实现,一个类可以实现多个接口。通过实现接口,类可以获得接口中定义的方法,并且可以在类中提供方法的具体实现。接口可以用来实现多态性和解耦,提高代码的灵活性和可维护性。
b) 构造方法
-
ListIterator<E> listIterator()
创建一个ListIterator对象,指向列表的第一个元素。 -
ListIterator<E> listIterator(int index)
创建一个ListIterator对象,指向列表中指定索引位置的元素。 -
ListIterator<E> listIterator(List<E> list)
创建一个ListIterator对象,指向指定列表的第一个元素。
List<String list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add("cherry");
// 创建一个ListIterator对象,指向列表的第一个元素
ListIterator<String> iterator1 = list.listIterator();
// 创建一个ListIterator对象,指向列表中索引位置为1的元素
ListIterator<String> iterator2 = list.listIterator(1);
// 创建一个ListIterator对象,指向指定列表的第一个元素
ListIterator<String> iterator3 = list.listIterator(list);
c) 方法
hashasNext()
:判断列表是否有下一个元素。next()
:返回下一个元素。hasPrevious()
:判断列表迭代器中是否还有上一个元素可供访问。previous()
:返回前一个元素。add(E e)
:在列表中插入元素e(可选操作)。 E代表插入元素的类型,e代表插入元素。remove()
:删除由next()或者previous()返回的最后一个元素(可选操作)。set(E e)
:用指定元素替换由next()或者previous()返回的最后一个元素(可选操作)。
<Tips>
(1)当列表中还有下一个元素时,hasNext()方法返回true;当列表中没有下一个元素时,返回false。通常与next()方法一起使用,用于在循环中遍历正向列表中的所有元素。
(2)如果迭器中有上一个元素,hasPrevious()方法返回true,否则返回false。通常与previous()方法一起使用,用于在循环中遍历反向列表中的所有元素。
(3)怎样理解 “可选操作” ?
在大多数情况下,这个操作可以执行。但是在有些情况下,可能不支持这个操作,此时会抛出异常(如下),所以称此操作为可选操作。
UnsupportedOperationException - 列表不支持 add 操作 ClassCastException - 指定元素的类不允许它添加到此列表 NullPointerException - 指定的元素为 null,并且此列表不允许 null 元素 IllegalArgumentException - 指定元素的某些属性不允许它添加到此列表 IndexOutOfBoundsException - 索引超出范围 (index < 0 || index > size())
(4)Iterator中的remove()方法,调用next()后,删除的是迭代器左侧的元素;
调用previous()后,方法删除的是迭代器右侧的元素。
(5)ListIterator中的add()方法,调用next()后,在迭代器的左侧添加一个元素;
调用previous()后,在迭代器的右侧添加一个元素。
感谢您的观看,希望你今天特别特别开心,加油~