LinkedList
双向链表实现,增删快,查询慢 (线程不安全)
属性:
transient int size = 0; 存储的元素个数
transient Node<E> first; 头节点
transient Node<E> last; 尾节点
节点的定义
private static class Node<E> { 静态内部类
E item; 节点上存储的具体数据
Node<E> next; 指向下一个节点
Node<E> prev; 指向上一个节点
//创建节点对象的参数
prev前一个节点的引用 element具体存储的数据 next下一个节点引用
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
构造器方法
public LinkedList() {
}
没有指定容积的构造器,理论上来说,链表实际上没有长度限制,但是int size属性要求元素个数必须在[0,Integer.max-value]范围内
add方法的定义:在双向链表的末尾新增元素
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
final Node<E> l = last; 缓存尾节点
final Node<E> newNode = new Node<>(l, e, null); 创建新的节点,新节点的下一个节点为null
last = newNode; 将尾指针执行新创建的节点
if (l == null) 如果原来的尾节点为null,则新增的节点应该是链表的第一个节点
first = newNode; 将头指针指向新创建的节点
else
l.next = newNode; 如果原来的尾节点不为null,则表示已经有元素了,将原来的尾节点指向新创建的节点,这样就将新节点加入到链表中
size++; 链表中元素个数加1
modCount++; 修改次数加1
}
--------------------------------------------------------------------------------
add(int,Object) 在指定位置新增元素,原来位置上的元素后移
public void add(int index, E element) {
checkPositionIndex(index); 检查index的合法性,要求index应该是在[0,size]的范围内
index >= 0 && index <= size;如果不合法则抛出异常IndexOutOfBoundsException
if (index == size) 如果索引值index等于size,则在链表默认添加元素
linkLast(element);
else 在指定下标位置之前添加元素
linkBefore(element, node(index));
}
//获取指定位置上的节点对象
Node<E> node(int index) {
if (index < (size >> 1)) { 如果序号值小于size/2则从头指针开始遍历链表,查找指定索引值的元素Node
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next; 从前向后遍历
return x;
} else { 如果序号值大于等于size/2则从尾指针开始遍历链表,查找指定索引值的元素Node
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev; 从后向前遍历
return x;
}
}
在指定节点对象之前添加e数据
void linkBefore(E e, Node<E> succ) {
final Node<E> pred = succ.prev; 获取指定位置的前一个节点
final Node<E> newNode = new Node<>(pred, e, succ); 创建节点对象,参数1是原始节点的前一个节点,参数2是具体存储的数据,参数3是原始位置上的节点
succ.prev = newNode; 设置原始位置上的node对象的前一个节点为新创建的节点【双向链表】
if (pred == null) 如果指定位置的节点的前一个节点为null,则插入的新节点应该就是头节点
first = newNode; 使头指针指向新创建的节点
else 设置前一个节点的下一个节点指针指向新创建的节点
pred.next = newNode;
size++; 元素个数加1
modCount++; 修改次数加1
}
--------------------------------------------------------------------------------
删除和参数相等的第一个元素
删除成功返回true,否则返回false
public boolean remove(Object o) {
if (o == null) { 删除值为null的元素,使用==判断
for (Node<E> x = first; x != null; x = x.next) {从头指针开始执行遍历,查找第一个值为null的节点
if (x.item == null) { //判断当前节点的值是否为null
unlink(x); 删除指定的Node节点,并且返回Node中存储的数据
return true;
}
}
} else { 删除值非null的元素,判断使用equals方法
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) { 判断当前需要删除的数据和节点中的数据是否相等
unlink(x); 删除指定的Node节点,并且返回Node中存储的数据
return true;
}
}
}
return false;
}
删除指定的Node节点,并且返回Node中存储的数据
E unlink(Node<E> x) {
final E element = x.item; 获取节点中的数据
final Node<E> next = x.next; 获取节点的下一个节点对象的引用
final Node<E> prev = x.prev; 获取上一个节点
if (prev == null) { 如果当前节点的前一个节点为null,则表示当前节点为头节点
first = next; 使头指针指向当前节点的下一个节点
} else { 如果不是头节点
prev.next = next; 上一个节点的下一个节点为当前节点的下一个节点,将当前节点从链表中剔除出来
x.prev = null; 当前节点的上一个节点赋null值
}
if (next == null) {如果当前节点的下一个节点为null,则表示当前节点为尾节点
last = prev; 使尾指针指向当前节点的上一个节点
} else { 如果不是尾节点
next.prev = prev; 下一个节点的上一个节点引用值为当前节点的上一个节点,将当前节点从链表中剔除出来
x.next = null; 当前节点的下一个节点赋null值
}
x.item = null; 将当前节点的数据赋null值
size--; 链表中的元素个数-1
modCount++; 修改次数+1
return element; 返回当前节点原来的数据
}
--------------------------------------------------------------------------------
按照指定的索引序号删除对应的元素
同时返回删除元素的具体数据值
public E remove(int index) {
checkElementIndex(index); 检查序号值是否合法,不合法抛出异常IndexOutOfBoundsException index >= 0 && index < size
return unlink(node(index)); 首先查找指定索引序号对应的节点对象,然后删除对应的节点,返回原来节点上存储的具体数据
}
修改操作
public E set(int index, E element) {
checkElementIndex(index);
Node<E> x = node(index);
E oldVal = x.item; //获取原始节点存储的数据
x.item = element; //修改节点上的数据为新值
return oldVal; //返回原始存储的数据
}
查找操作
如果找到则返回索引序号,如果没有找到则返回-1
public int indexOf(Object o) {
int index = 0;
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null)
return index;
index++;
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item))
return index;
index++;
}
}
return -1;
}