LinkedList的模拟实现(Java实现)

简介: LinkedList的底层是用一个双向链表实现的,即一个结点中除了有一个引用指向下一个结点的地址,还有一个引用指向前一个结点的地址。

🍔关于LinkedList


LinkedList的底层是用一个双向链表实现的,即一个结点中除了有一个引用指向下一个结点的地址,还有一个引用指向前一个结点的地址


LinkedList还有三个成员变量:


🍂size,表示该链表中结点的个数

🍂first,指向链表首结点

🍂last,指向链表尾结点


🍿模拟实现LinkedList

🍉准备工作

🍃创建静态内部类ListNode,后续创建结点都需要ListNode来创建新的结点

private static class ListNode<E> {
        ListNode<E> pre;  //指向前一个结点
        ListNode<E> next; //指向下一个结点
        E val;  //结点的值
        public ListNode(E val){
            this.val = val;
        }
    }

  🍃创建成员变量:first,last,size

private ListNode<E> first;  //指向链表首结点
    private ListNode<E> last;  //指向链表尾结点
    private int size;  //链表结点的个数

 

🍃重写toString()方法,在进行测试方法的时候需要将链表打印出来


@Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        if(first == null){
            sb.append("]");
        }else {
            ListNode<E> cur = first;
            while(cur.next != null){
                sb.append(cur.val);
                sb.append(",");
                cur = cur.next;
            }
            sb.append(cur.val);
            sb.append("]");
        }
        return sb.toString();
    }

 

🍊头插

🍀在链表的头部插入结点,这里分两种情况:链表为空,链表不为空


🍀链表为空:直接将first,last指向该结点,因为这个结点既是第一个结点,也是最后一个结点

🍀链表不为空:将first的pre执行该结点,再将该结点的next指向first,更新first位置


🍀在结点头插完后,更新size的长度,进行size++

public void addFirst(E e){
        ListNode<E> newNode = new ListNode<>(e);
        if(first == null){
            first = newNode;
            last = newNode;
        }else {
            first.pre = newNode;
            newNode.next = first;
            first = newNode;
        }
        size++;
    }

 

🍂进行测试:依次头插入1,2,3,4,打印list

MyLinkedList<Integer> list = new MyLinkedList<>();
        list.addFirst(1);
        list.addFirst(2);
        list.addFirst(3);
        list.addFirst(4);
        System.out.println(list);

   

🍀打印结果:因为是头插,所以打印4,3,2,1


微信图片_20221029161645.png


🍋尾插

🍁在链表的尾部插入结点,分两种情况:链表为空,链表不为空


🍁链表为空:直接将first,last指向该结点,该结点既是链表的首结点,又是最后一个结点

🍁链表不为空:last的next执行该节点,该节点的pre指向last,更新last位置


🍁在结点尾插完后,更新size的长度,进行size++

public void addLast(E e){
        ListNode<E> newNode = new ListNode<>(e);
        if(first == null){
            first = newNode;
            last = newNode;
        }else {
            last.next = newNode;
            newNode.pre = last;
            last = newNode;
        }
        size++;
    }

   🍂进行测试:依次尾插入1,2,3,4,打印list

list.addLast(1);
        list.addLast(2);
        list.addLast(3);
        list.addLast(4);
        System.out.println(list);

   

🍀打印结果:因为是尾插,所以打印1,2,3,4

微信图片_20221029161729.png


🍏在任意位置插入

这里分三种情况讨论:在第一个位置插入,在最后一个位置插入,在其他位置插入


🍂在第一个位置插入:相当于头插,调用addFirst()方法

🍂在最后一个位置插入:相当于尾插,调用addLast()方法

image.png


🍂在其他位置插入:看下图解析

image.png

public boolean addIndex(int index,E e){
        ListNode<E> newNode = new ListNode<>(e);
        if(index < 0 && index > size){
            throw new IndexOutOfBoundsException("addIndex:下标越界");
        }
        if(index == size){
            addLast(e);
        }else if(index == 0){
            addFirst(e);
        }else {
            ListNode<E> cur = first;
            for(int i = 0;i < index;i++){
                cur = cur.next;
            }
            newNode.pre = cur.pre;
            newNode.next = cur;
            newNode.pre.next = newNode;
            cur.pre = newNode;
            size++;
        }
        return true;
    }


🍂进行测试:原来链表为1,2,3,4,在位置0插入1,在位置5插入5,在位置3插入3

list.addIndex(0,1);
        list.addIndex(4,5);
        list.addIndex(3,3);
        System.out.println(list);

 

🍀打印结果:打印为1,1,2,3,3,4,5

微信图片_20221029161841.png


🍑删除remove

这里分为三种情况:删除的是第一个元素,删除的是最后一个元素,删除其他元素


🍃删除一个元素:将first指向first的next,再将first的pre指向null

🍃删除最后一个元素:将last更新为last的pre位置,再将last的next指向null

image.png


🍃删除其他位置元素:参照下图解析

image.png

🍃删除完后,进行size--操作

public void remove(E e){
        ListNode<E> cur = first;
        while(cur != null){
            if(e.equals(cur.val)){
                break;
            }
            cur = cur.next;
        }
        if(cur == first){
            first = first.next;
            first.pre = null;
        }else if(cur == last){
            last = last.pre;
            last.next = null;
        }else {
            cur.pre.next = cur.next;
            cur.next.pre = cur.pre;
        }
        size--;
    }

 


🍂进行测试:原链表为1,2,3,4,5,删除1,删除5,删除3

list.remove(1);
        list.remove(5);
        list.remove(3);
        System.out.println(list);

 

🍀打印结果:打印2,4

微信图片_20221029161959.png


🍒删除removeAll

这个与删除一次出现元素e的做法差不多,只是在找到第一次出现元素e时,将它删掉,继续遍历链表

public void removeAll(E e){
        ListNode<E> cur = first;
        while(cur != null){
            if(cur.val.equals(e)){
                if(cur == first){
                    first = first.next;
                    first.pre = null;
                }else if(cur == last){
                    last = last.pre;
                    last.next = null;
                }else {
                    cur.pre.next = cur.next;
                    cur.next.pre = cur.pre;
                }
                size--;
            }
            cur = cur.next;
        }
    }

🍂进行测试:链表为1,2,1,3,1,将所有的1全部删掉


       list.removeAll(1);

       System.out.println(list);

🍀打印结果:链表中的元素只剩下2,3

微信图片_20221029162041.png


🍐找元素下标indexOf

创建一个下标index,从0开始增加,每遍历一个元素进行index++,如果遍历的元素是要找的元素则返回index,当遍历完链表没有要找的元素时,返回-1

public int indexOf(E e){
        ListNode<E> cur = first;
        int index = 0;
        while(cur != null){
            if(e.equals(cur.val)){
                return index;
            }else {
                index++;
                cur = cur.next;
            }
        }
        return -1;
    }

 

🍂进行测试:链表为1,2,3,4,5,找3的位置和6的位置

System.out.println(list.indexOf(3));
        System.out.println(list.indexOf(6));

   

🍀打印结果:3在下标为2的位置,6不在该链表中,故返回-1

微信图片_20221029162108.png


🍍找元素下标lastIndexOf

这个从链表尾部往前遍历,创建index值为size-1,当元素不为我们要找的元素时,index--,找到返回index,当遍历完整个链表都没有找到时,返回-1

public int lastIndexOf(E e){
        ListNode<E> cur = last;
        int index = size-1;
        while(cur != null){
            if(e.equals(cur.val)){
                return index;
            }else {
                cur = cur.pre;
                index--;
            }
        }
        return -1;
    }

 

🍂进行测试:链表为1,2,3,3,4,5,找3的位置和6的位置

System.out.println(list.lastIndexOf(3));
        System.out.println(list.lastIndexOf(6));

   

🍀打印结果:最后一个3的下标为3,6不在该链表中

微信图片_20221029162157.png

🍅判断链表是否包含某个元素

这里可以调用indexOf方法,看返回的是不是-1,如果不是-1则说明链表包含该元素,如果返回的是-1,说明链表不包含该元素

public boolean contains(E e){
        return -1 != indexOf(e);
    }

🍂进行测试:链表为1,2,3,4,5,判断该链表是否包含2和6

boolean ret = list.contains(2);
        boolean ret1 = list.contains(6);
        System.out.println(ret);
        System.out.println(ret1);

 

🍀输出结果:2在链表中,6不在链表中

微信图片_20221029162230.png

🫐获得链表长度

直接返回size即可

public int size(){
        return size;
    }

 

🍂进行测试:返回空链表的size,和有元素的size

list.clear();
        System.out.println(list.size());
        list.addFirst(1);
        System.out.println(list.size());

   

🍀打印结果:

微信图片_20221029162304.png


🥝链表判空

判断链表为空有两种方式:判断size是否为0或者判断first是否指向null

public boolean isEmpty(){
        //return size==0;
        return first==null;
    }

 

🍂进行测试:看有元素的链表是否为空,和空链表是否为空

list.clear();
        list.addFirst(1);
        boolean ret1 = list.isEmpty();
        System.out.println(ret1);
        list.clear();
        boolean ret2 = list.isEmpty();
        System.out.println(ret2);

  🍀打印结果:

微信图片_20221029162340.png

🥥清空链表

清空操作就是将first指向null,将last指向null,将size置0

public void clear(){
        first = null;
        last = null;
        size = 0;
    }

 

🍂进行测试:执行清空操作,打印list

list.clear();
        System.out.println(list);

   

🍀打印结果:链表中没有元素

微信图片_20221029162436.png


相关文章
|
22天前
|
存储 Java 索引
【Java集合类面试二十四】、ArrayList和LinkedList有什么区别?
ArrayList基于动态数组实现,支持快速随机访问;LinkedList基于双向链表实现,插入和删除操作更高效,但占用更多内存。
|
1月前
|
存储 算法 Java
14 Java集合(集合框架+泛型+ArrayList类+LinkedList类+Vector类+HashSet类等)
14 Java集合(集合框架+泛型+ArrayList类+LinkedList类+Vector类+HashSet类等)
36 2
14 Java集合(集合框架+泛型+ArrayList类+LinkedList类+Vector类+HashSet类等)
|
19天前
|
存储 消息中间件 Java
何时在 Java 中使用 ArrayList 和 LinkedList
【8月更文挑战第23天】
10 2
|
20天前
|
存储 Java
|
20天前
|
存储 Java 索引
Java 中 ArrayList 和 LinkedList 之间的区别
【8月更文挑战第22天】
49 1
|
29天前
|
存储 SQL 搜索推荐
一天十道Java面试题----第一天(面向对象-------》ArrayList和LinkedList)
这篇文章是关于Java面试的笔记,涵盖了面向对象、JDK/JRE/JVM的区别、`==`和`equals`、`final`关键字、`String`、`StringBuffer`和`StringBuilder`的区别、重载与重写、接口与抽象类、`List`与`Set`、`hashcode`与`equals`以及`ArrayList`和`LinkedList`的对比等十个主题。
|
3月前
|
安全 Java
Java Queue新玩法:用LinkedList打造高效队列,让你的代码飞起来!
【6月更文挑战第18天】Java集合框架中的`LinkedList`不仅是列表,还可作为高效队列。由于其在链表两端进行添加/移除操作的时间复杂度为O(1),故适合实现并发环境下的任务队列。通过案例展示了如何创建、添加任务及确保线程安全,揭示了`LinkedList`提升代码性能的秘密,特别是在多线程应用中的价值。
34 4
|
3月前
|
安全 Java 调度
Java Queue深度解析:LinkedList为何成为队列的最佳实践?
【6月更文挑战第18天】Java的`LinkedList`适合作为队列,因其双向链表结构支持O(1)的头尾操作。非线程安全的`LinkedList`在单线程环境下效率高,多线程时可通过`Collections.synchronizedList`封装。此外,它还可兼做栈和双端队列,提供任务调度的高效解决方案。
38 3
|
3月前
|
存储 安全 Java
告别低效!Java Queue与LinkedList的完美结合,让你的程序更高效!
【6月更文挑战第18天】Java的`LinkedList`作为`Queue`实现,提供高效并发队列。利用双向链表,它在头部和尾部操作有O(1)复杂度,适合大量数据和高并发。通过`Collections.synchronizedList`可使其线程安全,用于任务调度等场景,展现灵活性和高性能。
33 2
|
3月前
|
存储 Java 容器
【JAVA集合篇 - LinkedList】你真的了解LinkedList吗?
【JAVA集合篇 - LinkedList】你真的了解LinkedList吗?
17 0