链表(LinkedList)
概念
- 链表是一种链式存储的线性表,所有存储的元素的内存地址不一定是连续的
链表接口设计
LinkedList,从字面上来看和ArrayList有一点相同,所以LinkedList和ArrayList有一些相同的方法。可能方法名跟ArrayList相同,但是里面的代码实现完全不一样,也有可能代码实现一模一样。所以先展示LinkedList的接口方法,后续逐个实现对应的方法
- 代码
public class LinkedList<E> {
private int size;
private Node<E> firstNode;
// 元素的数量
int size();
// 是否为空
boolean isEmpty();
// 是否包含某个元素
boolean contains(E element);
// 添加元素到最后面
void add(E element);
// 返回index位置对应的元素
E get(int index);
// 设置index位置的元素
E set(int index, E element);
// 往index位置添加元素
void add(int index, E element);
// 删除index位置对应的元素
E remove(int index);
// 查看元素的位置
int indexOf(E element);
// 清除所有元素
void clear();
// 私有类, 链表中的节点
private class Node<E> {
E element;
Node<E> next;
// 构造方法
public Node(E element, Node<E> next) {
this.element = element;
this.next = next;
}
}
}
单向链表的实现
构造方法
- 代码
private Node firstNode;
private static class Node<E> {
E element;
Node<E> next;
public Node(E element, Node<E> next) {
this.element = element;
this.next = next;
}
}
清空元素(Clear)
- 图解
- 分析
链表的长度是4个,size=4,清空整个链表,只需要first的地址指向为null,同时size置为0即可
- 代码
public void clear() {
firstNode = null;
size = 0;
}
获取index位置对应节点对象(node)
- 分析
因为链表的空间元素地址不是连续的,所以寻址速度相对于数组来讲是很慢的,如果需要通过index位置找到对应的节点对象,需要进行整个链表的遍历。
- 代码
private Node<E> node(int index) {
rangeCheck(index);
Node<E> node = firstNode;
for (int i = 0; i < index; i++) {
node = node.next;
}
return node;
}
获取某个元素的位置(Get)
- 分析
通过node方法可以获取到对应位置的节点对象,自然就得到了对应位置的元素。
- 代码
public E get(int index) {
Node<E> node = node(index);
return node.element;
}
设置某个位置的元素(Set)
- 分析
通过node方法可以获取到对应位置的节点对象,设置一个变量获得原位置的元素,然后再把元素进行替换。
- 代码
public E set(int index, E element) {
Node<E> node = node(index);
E oldElement = node.element;
node.element = element;
return oldElement;
}
添加链表元素(add)
- 图解
- 分析
常见情况:元素添加在中间或者尾部,获取到上个链表所在的链表节点(prev),然后创建新的链表节点,将next指定prev.next,然后prev.next重新指向新链表节点。
特殊情况:元素添加在第一位,创建新的链表节点,然后将firstNode赋给新节点的next,在将新节点重新赋给firstNode。
最后链表长度加一。
- 代码
public void add(int index, E element) {
rangeCheckForAdd(index);
if (index == 0) {
Node<E> newNode = new Node<>(element, firstNode);
firstNode = newNode;
} else {
Node<E> prev = node(index - 1);
Node<E> newNode = new Node<>(element, prev.next);
prev.next = newNode;
}
size++;
}
删除指定元素(remove)
- 图解
- 分析
常见情况,删除中间位置或者尾部,直接通过node方法获得上一个节点的信息,然后将上一个节点的地址指向下一个节点的next。然后链表长度减一。
- 代码
public E remove(int index) {
rangeCheck(index);
Node<E> node = node(index);
if (index == 0) {
firstNode = firstNode.next;
} else {
Node<E> prev = node(index - 1);
prev.next = prev.next.next;
}
size--;
return node.element;
}