深入理解Java LinkedList:原理解析与自定义实现

Java的LinkedList是一个重要的集合类,实现了双向链表的数据结构。相比于ArrayList,LinkedList在某些场景下具有更高的性能优势。本文将深入解析Java LinkedList的原理,并通过自定义实现来帮助你理解其底层机制。

什么是LinkedList?

LinkedList是Java集合框架中的一个类,它实现了List和Deque接口。与ArrayList不同,LinkedList基于双向链表的数据结构,适用于频繁插入和删除操作的场景。

LinkedList的底层实现原理

双向链表

LinkedList使用双向链表来存储元素。每个节点包含一个元素和指向前后两个节点的引用。

节点的插入和删除

在双向链表中,插入和删除操作的时间复杂度为O(1),因为只需要调整相关节点的引用,而不需要像ArrayList那样移动大量元素。

随机访问

由于需要沿着链表遍历节点,LinkedList的随机访问时间复杂度为O(n),不如ArrayList的O(1)高效。

自定义LinkedList的实现

以下是一个自定义LinkedList的示例代码,帮助你理解其底层原理。

自定义LinkedList类

java 复制代码
public class CustomLinkedList<E> {
    private Node<E> head;
    private Node<E> tail;
    private int size;

    private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }

    // 添加元素到末尾
    public void add(E e) {
        Node<E> newNode = new Node<>(tail, e, null);
        if (tail == null) {
            head = newNode;
        } else {
            tail.next = newNode;
        }
        tail = newNode;
        size++;
    }

    // 获取指定位置的元素
    public E get(int index) {
        checkElementIndex(index);
        return node(index).item;
    }

    // 获取节点
    Node<E> node(int index) {
        if (index < (size >> 1)) {
            Node<E> x = head;
            for (int i = 0; i < index; i++) {
                x = x.next;
            }
            return x;
        } else {
            Node<E> x = tail;
            for (int i = size - 1; i > index; i--) {
                x = x.prev;
            }
            return x;
        }
    }

    // 删除指定位置的元素
    public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }

    // 删除节点
    private E unlink(Node<E> x) {
        E element = x.item;
        Node<E> next = x.next;
        Node<E> prev = x.prev;

        if (prev == null) {
            head = next;
        } else {
            prev.next = next;
            x.prev = null;
        }

        if (next == null) {
            tail = prev;
        } else {
            next.prev = prev;
            x.next = null;
        }

        x.item = null;
        size--;
        return element;
    }

    // 检查索引
    private void checkElementIndex(int index) {
        if (!isElementIndex(index)) {
            throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
        }
    }

    private boolean isElementIndex(int index) {
        return index >= 0 && index < size;
    }

    // 获取大小
    public int size() {
        return size;
    }
}

测试自定义LinkedList

java 复制代码
public class CustomLinkedListTest {
    public static void main(String[] args) {
        CustomLinkedList<String> list = new CustomLinkedList<>();
        list.add("Apple");
        list.add("Banana");
        list.add("Orange");

        System.out.println("Element at index 1: " + list.get(1));
        System.out.println("Size of list: " + list.size());

        list.remove(1);
        System.out.println("Element at index 1 after removal: " + list.get(1));
        System.out.println("Size of list after removal: " + list.size());
    }
}

详细讲解自定义LinkedList的各个部分

构造函数

构造函数初始化了头节点和尾节点,并设置初始大小为0。

添加元素

添加元素时,会创建一个新节点并将其添加到链表末尾。

java 复制代码
public void add(E e) {
    Node<E> newNode = new Node<>(tail, e, null);
    if (tail == null) {
        head = newNode;
    } else {
        tail.next = newNode;
    }
    tail = newNode;
    size++;
}
添加元素图解

假设链表初始为空,依次添加元素 "Apple"、"Banana" 和 "Orange"。

  1. 添加 "Apple" 时:

    null <- [Apple] -> null

  2. 添加 "Banana" 时:

    null <- [Apple] <-> [Banana] -> null

  3. 添加 "Orange" 时:

    null <- [Apple] <-> [Banana] <-> [Orange] -> null

获取元素

通过索引获取元素,首先检查索引是否合法,然后遍历链表找到对应的节点。

java 复制代码
public E get(int index) {
    checkElementIndex(index);
    return node(index).item;
}

获取节点

根据索引值从头或尾开始遍历链表,提高效率。

java 复制代码
Node<E> node(int index) {
    if (index < (size >> 1)) {
        Node<E> x = head;
        for (int i = 0; i < index; i++) {
            x = x.next;
        }
        return x;
    } else {
        Node<E> x = tail;
        for (int i = size - 1; i > index; i--) {
            x = x.prev;
        }
        return x;
    }
}

删除元素

删除元素时,首先找到对应的节点,然后调整前后节点的引用,并将节点置为null以便垃圾回收。

java 复制代码
public E remove(int index) {
    checkElementIndex(index);
    return unlink(node(index));
}
删除元素图解

假设链表当前状态如下,删除元素 "Banana"(即索引1的元素):

null <- [Apple] <-> [Banana] <-> [Orange] -> null
  1. 找到 "Banana" 的节点。

  2. 调整 "Apple" 和 "Orange" 节点的引用,使它们直接相连:

    null <- [Apple] <-> [Orange] -> null

  3. 将 "Banana" 节点置为null,以便垃圾回收。

删除节点

具体的节点删除操作。

java 复制代码
private E unlink(Node<E> x) {
    E element = x.item;
    Node<E> next = x.next;
    Node<E> prev = x.prev;

    if (prev == null) {
        head = next;
    } else {
        prev.next = next;
        x.prev = null;
    }

    if (next == null) {
        tail = prev;
    } else {
        next.prev = prev;
        x.next = null;
    }

    x.item = null;
    size--;
    return element;
}

检查索引

确保索引在合法范围内。

java 复制代码
private void checkElementIndex(int index) {
    if (!isElementIndex(index)) {
        throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
    }
}

private boolean isElementIndex(int index) {
    return index >= 0 && index < size;
}

获取大小

获取链表的大小。

java 复制代码
public int size() {
    return size;
}

总结

通过自定义LinkedList,我们深入理解了LinkedList的底层实现原理,包括其双向链表结构、元素插入与删除操作以及访问元素的方式。理解这些底层原理,有助于我们在使用LinkedList时更加高效和合理。

希望本文对你理解Java LinkedList的底层原理有所帮助。如果你喜欢这篇文章,请点赞并分享,关注我们的博客以获取更多关于Java编程和软件开发的精彩内容!

相关推荐
小冉在学习9 分钟前
day53 图论章节刷题Part05(并查集理论基础、寻找存在的路径)
java·算法·图论
y5236489 分钟前
Javascript监控元素样式变化
开发语言·javascript·ecmascript
IT技术分享社区40 分钟前
C#实战:使用腾讯云识别服务轻松提取火车票信息
开发语言·c#·云计算·腾讯云·共识算法
极客代码42 分钟前
【Python TensorFlow】入门到精通
开发语言·人工智能·python·深度学习·tensorflow
疯一样的码农1 小时前
Python 正则表达式(RegEx)
开发语言·python·正则表达式
代码之光_19801 小时前
保障性住房管理:SpringBoot技术优势分析
java·spring boot·后端
ajsbxi1 小时前
苍穹外卖学习记录
java·笔记·后端·学习·nginx·spring·servlet
&岁月不待人&1 小时前
Kotlin by lazy和lateinit的使用及区别
android·开发语言·kotlin
StayInLove1 小时前
G1垃圾回收器日志详解
java·开发语言
对许1 小时前
SLF4J: Failed to load class “org.slf4j.impl.StaticLoggerBinder“
java·log4j