文章目录
前言
动态数组有个明显的缺点:可能会造成内存空间的大量浪费。能否设计一种数据结构以达到用到多少就申请多少内存?链表可以办到这一点。
一、基础概念
1.1 线性表的类型
线性表是一种逻辑结构,这种逻辑结构在计算机的表现形式(存储结构)主要有两种:
1.线性存储:用顺序存储结构的线性表也叫做顺序表,一般用数组实现。
2.链式存储:用链式结构存储的线性表也叫做链表,链表由一个个节点组成节点由数据域和指针域组成。
1.2 链表定义
链表是一种链式存储的线性表,所有元素的内存地址不一定是连续的,如下图所示:
1.3 ArrayList和LinkedList谁更占空间?
一般情况下,LinkedList的占用空间更大,因为每个节点要维护指向前后地址的两个节点,但也不是绝对如果刚好数据量超过ArrayList默认的临时值时,ArrayList占用的空间也是不小的,因为扩容的原因会浪费将近原来数组一半的容量不过因为ArrayList的数组变量是用transient关键字修饰的,如果集合本身需要做序列化操作的话,ArrayList这部分多余的空间不会被序列化。
1.4 链表的优缺点
- 优点:
1.元素的存储单元是任意的可以连续也可以不连续,插入删除效率高只需要改变引用即可。
2.没有空间限制存储的元素只要内存够大可以无上限存储。
3.动态分配内存空间,不用事先开辟较为灵活,空间利用率高。 - 缺点:
1.查找效率低,涉及到从链表的头遍历到查询数据元素的位置。
二、接口设计
2.1 接口类图
2.2 清空元素-clear
frist = null 就已经指明链表元素没有被引用,会被gc回收。
@Override
public void clear() {
size = 0;
first = null;
}
2.3 新增元素-add
@Override
public void add(int index, E element) {
/*
* 最好:O(1)
* 最坏:O(n)
* 平均:O(n)
*/
rangeCheckForAdd(index);
// 给空链表添加第一个元素的情况
if(index == 0){
first = new Node<>(element, first);
}else{
Node<E> prev = node(index - 1);
prev.next = new Node<>(element, prev.next);
}
size++;
}
2.4 获取元素–get
@Override
public E get(int index) {
return node(index).element;
}
//获取index位置的节点
private Node<E> node(int index) {
rangeCheck(index);
Node<E> node = first;
for (int i = 0; i < index; i++) {
node = node.next;
}
return node;
}
2.5 设置元素-set
@Override
public E set(int index, E element) {
/*
* 最好:O(1)
* 最坏:O(n)
* 平均:O(n)
*/
E old = node(index).element;
node(index).element = element;
return old;
}
2.6 删除元素–remove
@Override
public E remove(int index) {
/*
* 最好:O(1)
* 最坏:O(n)
* 平均:O(n)
*/
rangeCheck(index);
Node<E> node = first;
if (index == 0) { // 删除第一个元素是特殊情况
first = first.next;
} else {
Node<E> prev = node(index - 1); // 找到前一个元素
node = prev.next; // 要删除的元素
prev.next = node.next; // 删除元素
}
size--;
return node.element;
}
2.7 复杂度分析
2.8 代码实现
公用List接口封装:
public interface List<E> {
static final int ELEMENT_NOT_FOUND = -1;
/**
* 清除所有元素
*/
void clear();
/**
* 元素的数量
* @return
*/
int size();
/**
* 是否为空
* @return
*/
boolean isEmpty();
/**
* 是否包含某个元素
* @param element
* @return
*/
boolean contains(E element);
/**
* 添加元素到尾部
* @param element
*/
void add(E element);
/**
* 获取index位置的元素
* @param index
* @return
*/
E get(int index);
/**
* 设置index位置的元素
* @param index
* @param element
* @return 原来的元素ֵ
*/
E set(int index, E element);
/**
* 在index位置插入一个元素
* @param index
* @param element
*/
void add(int index, E element);
/**
* 删除index位置的元素
* @param index
* @return
*/
E remove(int index);
/**
* 查看元素的索引
* @param element
* @return
*/
int indexOf(E element);
}
抽象类AbstractList:
public abstract class AbstractList<E> implements List<E>{
public int size;
/**
*获取集合中的数据元素的个数
*/
@Override
public int size(){
return size;
}
/**
*判断集合是否为空(无数据元素)
*/
@Override
public boolean isEmpty() {
return size == 0;
}
/**
*获取集合中数据元素是否包含element参数
*/
@Override
public boolean contains(E element){
return false;
}
/**
*向集合中添加数据元素
*/
@Override
public void add(E element){
/*
* elements[size] = element;
* size++;
*/
add(size, element);
}
public void outOfBounds(int index){
throw new IndexOutOfBoundsException("Index:" + index+ ",Size:" +size);
}
public void rangeCheck(int index){
if (index < 0 || index >= size){
outOfBounds(index);
}
}
public void rangeCheckForAdd(int index){
if (index < 0 || index > size){
outOfBounds(index);
}
}
}
实现类LinkedList:
public class LinkedList<E> extends AbstractList<E>{
private Node<E> first;
// 链表中的节点
private static class Node<E> {
E element; // 节点元素
Node<E> next; // 节点指向下一个节点
public Node(E element, Node<E> next) {
this.element = element;
this.next = next;
}
}
@Override
public void clear() {
size = 0;
first = null;
}
@Override
public E get(int index) {
return node(index).element;
}
/**
* 根据索引找到节点
*/
private Node<E> node(int index) {
rangeCheck(index);
Node<E> node = first;
for (int i = 0; i < index; i++) {
node = node.next;
}
return node;
}
@Override
public E set(int index, E element) {
/*
* 最好:O(1)
* 最坏:O(n)
* 平均:O(n)
*/
E old = node(index).element;
node(index).element = element;
return old;
}
@Override
public void add(int index, E element) {
/*
* 最好:O(1)
* 最坏:O(n)
* 平均:O(n)
*/
rangeCheckForAdd(index);
// 给空链表添加第一个元素的情况
if(index == 0){
first = new Node<>(element, first);
}else{
Node<E> prev = node(index - 1);
prev.next = new Node<>(element, prev.next);
}
size++;
}
@Override
public E remove(int index) {
/*
* 最好:O(1)
* 最坏:O(n)
* 平均:O(n)
*/
rangeCheck(index);
Node<E> node = first;
if (index == 0) { // 删除第一个元素是特殊情况
first = first.next;
} else {
Node<E> prev = node(index - 1); // 找到前一个元素
node = prev.next; // 要删除的元素
prev.next = node.next; // 删除元素
}
size--;
return node.element;
}
@Override
public int indexOf(E element) {
// 有个注意点, 如果传入元素为null, 则不能调用equals方法, 否则会空指针
// 因此需要对元素是否为null做分别处理
if (element == null) {
Node<E> node = first;
for (int i = 0; i < size; i++) {
if (node.element == null) return i;
node = node.next;
}
} else {
Node<E> node = first;
for (int i = 0; i < size; i++) {
if (node.element.equals(element)) return i;
node = node.next;
}
}
return ELEMENT_NOT_FOUND;
}
@Override
public String toString() {
StringBuilder string = new StringBuilder();
string.append("[size=").append(size).append(", ");
Node<E> node = first;
for (int i = 0; i < size; i++) {
if (i != 0) {
string.append(", ");
}
string.append(node.element);
node = node.next;
}
string.append("]");
return string.toString();
}
}
测试类LinkedListTest :
public class LinkedListTest {
public static void main(String[] args) {
List<Integer> list = new LinkedList<>();
list.add(10);
list.add(20);
list.add(30);
list.add(list.size(),40);
list.remove(1);
System.out.println(list);
}
}
运行结果: