集合-常用List
ArrayList
- 扩容机制
1.首次空数组获取最大容量(defalt,capacity)
2.容量大于当前数组,则扩容grow(minCapacity)。
3.声明当前数组容量oldCapacity,扩充容量为1.5倍oldCapacity(oldCapacity + (oldCapacity >> 1);
4.newCapacity如仍小于capacity,则capacity.
5.newCapacity如大于最大数组容量(MAX_ARRAY_SIZE=Integer.Max_VALUE-8)
6.newCapacity大于MAX_ARRAY_SIZE则Max_VALUE否则MAX_ARRAY_SIZE.
7.copy数组elementData = Array.copyof(elementData,newCapacity)
扩展
>>1右移运算符1/2
0x7FFFFFFF:0-正数 1-负数 7FFFFFFF-每个16进制(4bit),一个字节8bit,4字节=INT(2^31-1)
十六进制(简写为hex或下标16)是一种基数为16的计数系统,是一种逢16进1的进位制。通常用数字0、1、2、3、4、5、6、7、8、9和字母A、B、C、D、E、F(a、b、c、d、e、f)表示,其中:AF表示1015,这些称作十六进制数字。
- 时间复杂度
1.查询get(int index)- O(1)
2.增加add() 的时间复杂度最好情况为 O(1),最坏情况为 O(n)
3.remove(Object) 的时间复杂度最好情况 O(1),最坏情况 O(n)
set() ⽅法时)与查询操作类似,可以直接根据索引来访问元素,时间复
杂度为 O(1)
- Array.copyof(elementData,newCapacity)
- System.copyarray(elementData, index, elementData, index + 1, size - index);
elementData:表示要复制的源数组,即 ArrayList 中的元素数组。
index:表示源数组中要复制的起始位置,即需要将 index 及其后⾯的元素向后移动⼀位。
elementData:表示要复制到的⽬标数组,即 ArrayList 中的元素数组。
index + 1:表示⽬标数组中复制的起始位置,即将 index 及其后⾯的元素向后移动⼀位后,
应该插⼊到的位置。
size - index:表示要复制的元素个数,即需要将 index 及其后⾯的元素向后移动⼀位,需要
移动的元素个数为 size - index。
- Collections.sort(array)
- Collections.binarySearch(array, “b”);
LinkedList
- 私有静态内部类Node
item、next、prev - 添加元素实现
/**
* 在列表的尾部添加指定的元素。
*
* @param e 要添加到列表的元素
*/
void linkLast(E e) {
final Node<E> l = last; // 获取链表的最后⼀个节点
final Node<E> newNode = new Node<>(l, e, null); // 创建⼀个新的节点,并将其
设置为链表的最后⼀个节点
last = newNode; // 将新的节点设置为链表的最后⼀个节点
if (l == null) // 如果链表为空,则将新节点设置为头节点
first = newNode;
else
l.next = newNode; // 否则将新节点链接到链表的尾部
size++; // 增加链表的元素个数
}
添加第⼀个元素的时候,first 和 last 都为 null。
然后新建⼀个节点 newNode,它的 prev 和 next 也为 null。
然后把 last 和 first 都赋值为 newNode。
添加第⼆个元素的时候,first 和 last 都指向的是第⼀个节点。
然后新建⼀个节点 newNode,它的 prev 指向的是第⼀个节点,next 为 null。
然后把第⼀个节点的 next 赋值为 newNode。
添加第三个元素的时候,first 指向的是第⼀个节点,last 指向的是最后⼀个节点。
然后新建⼀个节点 newNode,它的 prev 指向的是第⼆个节点,next 为 null。
然后把第⼆个节点的 next 赋值为 newNode。
- remove(object)
遍历列表 - remove(index)和get(index)、set()
* 获取链表中指定位置的节点。
*
* @param index 节点的位置(从 0 开始)
* @return 指定位置的节点
* @throws IndexOutOfBoundsException 如果索引超出范围(index < 0 || index >=
size())
*/
Node<E> node(int index) {
if (index < (size >> 1)) { // 如果索引在链表的前半部分
Node<E> x = first;
for (int i = 0; i < index; i++) // 从头节点开始向后遍历链表,直到找到指定位置的节点
x = x.next;
return x; // 返回指定位置的节点
} else { // 如果索引在链表的后半部分
Node<E> x = last;
for (int i = size - 1; i > index; i--) // 从尾节点开始向前遍历链表,直到
找到指定位置的节点
x = x.prev;
return x; // 返回指定位置的节点
}
}
共性及特性
- 序列化
实现了Serializable,ArrayList(transient ElementData),LinkedList(transient size、first、last)
各自实现了writeObject(java.io.ObjectOutputStream s) 、readObject(java.io.ObjectOutputStream s)
/**
* 从指定的 ObjectInputStream 中读取此列表实例的状态序列。
* (即,从流中恢复这个列表实例。)
*
* @param s 从中读取此列表实例的状态序列的流
* @throws java.io.IOException 如果读取流时发⽣异常
* @throws ClassNotFoundException 如果在读取流时找不到类
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
elementData = EMPTY_ELEMENTDATA; // 初始化 elementData 数组为空数组
// 读取默认字段
s.defaultReadObject();
// 读取容ᰁ,这个值被忽略,因为在 ArrayList 中,容ᰁ和⻓度是两个不同的概念
s.readInt();
if (size > 0) {
// 分配⼀个新的 elementData 数组,⼤⼩为 size
ensureCapacityInternal(size);
Object[] a = elementData;
// 依次从输⼊流中读取元素,并将其存储在数组中
for (int i=0; i<size; i++) {
a[i] = s.readObject(); // 读取对象并存储在 elementData 数组中
}
}
}
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// 读取默认的序列化标记
s.defaultReadObject();
// 读取链表的节点个数
int size = s.readInt();
// 按正确的顺序读取所有元素
for (int i = 0; i < size; i++)
linkLast((E)s.readObject()); // 读取元素并将其添加到链表末尾
}
void linkLast(E e) {
final LinkedList.Node<E> l = last;
final LinkedList.Node<E> newNode = new LinkedList.Node<>(l, e, null);
last = newNode; // 将新节点作为链表尾节点
if (l == null)
first = newNode; // 如果链表为空,将新节点作为链表头节点
else
l.next = newNode; // 否则将新节点链接到链表尾部
size++; // 增加节点个数
}
- 应用特点
当需要频繁随机访问元素的时候,例如读取⼤量数据并进⾏处理或者需要对数据进⾏排序或查找
的场景,可以使⽤ ArrayList。例如⼀个学⽣管理系统,需要对学⽣列表进⾏排序或查找操作,可
以使⽤ ArrayList 存储学⽣信息,以便快速访问和处理。
当需要频繁插⼊和删除元素的时候,例如实现队列或栈,或者需要在中间插⼊或删除元素的场
景,可以使⽤ LinkedList。例如⼀个实时聊天系统,需要实现⼀个消息队列,可以使⽤
LinkedList 存储消息,以便快速插⼊和删除消息。
如需遍历LinkedList,尽量使用Iterator it = list.iterator(); it.hasNext();调用一次node(index)
LinkedList 还可以⽤于实现 LRU(Least Recently Used)缓存淘汰算
法。LRU 缓存淘汰算法是⼀种常⽤的缓存淘汰策略,它的基本思想是,当缓存空间不够时,优先
淘汰最近最少使⽤的缓存数据。在实现 LRU 缓存淘汰算法时,你可以使⽤我 LinkedList 来存储
缓存数据,每次访问缓存数据时,将该数据从链表中删除并移动到链表的头部,这样链表的尾部
就是最近最少使⽤的缓存数据,当缓存空间不够时,只需要将链表尾部的缓存数据淘汰即可。