LinkedList底层实现源码分析与ArrayList区别

数据结构

双向数据链结构

//在LinkedList类中的属性只有三个属性
transient int size = 0;//记录节点个数
transient Node<E> first;//记录链表的第一个节点
transient Node<E> last;//记录链表最后一个节点

在这里插入图片描述

常用方法

构造方法

源码分析

//无参构造方法,创建一个空链表
public LinkedList() { }
//有参构造,参数必须是Collection的子类,将参数的数据依次放入链表中
public LinkedList(Collection<? extends E> c) {
       this();
       addAll(c);
    }

代码演示

ArrayList<Course> arrayList1 = new ArrayList();
        LinkedList<Course> linkedList1 = new LinkedList<>();
// 初始化5个对象
        int i = 0;
        for (i=0; i < 5; i++) {
            arrayList1.add(new Course("arrayList1  "+i));
        }
        LinkedList<Course> linkedList2 = new LinkedList<>(arrayList1);
        for (i=0; i < 3; i++) {
            linkedList2.add(new Course("linkedList2  "+i));
        }
        for(int j=0;j<linkedList2.size();j++){
            System.out.println(linkedList2.get(j));
        }
//插入的数据的类型可以不一致
ArrayList arrayList1 = new ArrayList();
        LinkedList<Course> linkedList1 = new LinkedList<>();
// 初始化5个对象
        int i = 0;
        for (i=0; i < 5; i++) {
            arrayList1.add(i);
        }
        LinkedList<Course> linkedList2 = new LinkedList<>(arrayList1);
        for (i=0; i < 3; i++) {
            linkedList2.add(new Course("linkedList2  "+i));
        }
        for(int j=0;j<linkedList2.size();j++){
            System.out.println(linkedList2.get(j));
        }

增加

当使用index定位特定位置插入时,会出现IndexOutOfBoundsException(index越界异常index>size或index<0)

add()

有两种方法一种直接插入数据,会自动尾插
一种有index参数,在index位插入数据
源码分析

//都是通过调用封装方法
public boolean add(E e) {
        linkLast(e);
        return true;
    }
public void add(int index, E element) {
        checkPositionIndex(index);
        if (index == size)
            linkLast(element);
        else
            linkBefore(element, node(index));
    }
//两个方法实现了封装的linkLast(e)尾插方法,和 linkBefore(E e, Node<E> succ)定位节点前插方法
void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }
void linkBefore(E e, Node<E> succ) {
        // succ是链表中原index位节点,需要在它之前插入新节点,它作为新节点的next
        final Node<E> pred = succ.prev;
        final Node<E> newNode = new Node<>(pred, e, succ);
        succ.prev = newNode;
        if (pred == null)
            first = newNode;
        else
            pred.next = newNode;
        size++;
        modCount++;
    }

代码演示

ArrayList arrayList1 = new ArrayList();
        LinkedList<Course> linkedList1 = new LinkedList<>();
// 初始化5个对象
        int i = 0;
        for (i=0; i < 5; i++) {
            arrayList1.add(i);
        }
        LinkedList<Course> linkedList2 = new LinkedList<>(arrayList1);
        for (i=0; i < 5; i++) {
            linkedList2.add(new Course("linkedList2  "+i));
        }
        linkedList2.add(3,new Course("newCourse  "+3));
        for(int j=0;j<linkedList2.size();j++){
            System.out.println(linkedList2.get(j));
        }

在这里插入图片描述

addFirst()头增

源码分析

public void addFirst(E e) {
        linkFirst(e);
    }
//调用的封装的linkFirst(e)方法,
private void linkFirst(E e) {
        final Node<E> f = first;
        final Node<E> newNode = new Node<>(null, e, f);//增加prev为null,next为前first的新节点
        first = newNode;//将first的地址换位新节点
        if (f == null)//修改前first的prev值为现first
            last = newNode;
        else
            f.prev = newNode;
        size++;
        modCount++;
    }

代码演示

ArrayList arrayList1 = new ArrayList();
        LinkedList<Course> linkedList1 = new LinkedList<>();
// 初始化5个对象
        int i = 0;
        for (i=0; i < 5; i++) {
            arrayList1.add(i);
        }
        LinkedList<Course> linkedList2 = new LinkedList<>(arrayList1);
        for (i=0; i < 5; i++) {
            linkedList2.addFirst(new Course("linkedList2  "+i));
        }
        linkedList2.addLast(new Course("LastCourse  "+3));
        for(int j=0;j<linkedList2.size();j++){
            System.out.println(linkedList2.get(j));
        }

在这里插入图片描述

addLast()尾增

源码分析

public void addLast(E e) {
        linkLast(e);
    }
//与add(E e)中的封装方法一样,实现尾插
void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }

代码演示看上节addFirst()中的代码演示

addAll()

源码分析
有两种实现方式一种直接尾插传入参数的所有数据,一种在指定位置插入所有数据,都是实现的下面的代码

//实现过程,将传入Collection子类中的数据转化为Object类数组,将数组中的数据依次创建节点,插入index位,并放好前后节点地址
 public boolean addAll(int index, Collection<? extends E> c) {
        checkPositionIndex(index);
        Object[] a = c.toArray();//将c化为Object类数组
        int numNew = a.length;
        if (numNew == 0)
            return false;
        Node<E> pred, succ;
        if (index == size) {
            succ = null;
            pred = last;
        } else {
            succ = node(index);
            pred = succ.prev;
        }
        for (Object o : a) {
            @SuppressWarnings("unchecked") E e = (E) o;
            Node<E> newNode = new Node<>(pred, e, null);
            if (pred == null)
                first = newNode;
            else
                pred.next = newNode;
            pred = newNode;
        }
        if (succ == null) {
            last = pred;
        } else {
            pred.next = succ;
            succ.prev = pred;
        }

        size += numNew;
        modCount++;
        return true;
    }

代码演示

ArrayList arrayList1 = new ArrayList();
        LinkedList<Course> linkedList1 = new LinkedList<>();
// 初始化5个对象
        int i = 0;

        LinkedList<Course> linkedList2 = new LinkedList<>();
        for (i=0; i < 5; i++) {
            linkedList2.addFirst(new Course("linkedList2  "+i));
        }
        for (i=0; i < 5; i++) {
            arrayList1.add(i);
        }
        linkedList2.addAll(arrayList1);
        linkedList2.addAll(1,arrayList1);
        linkedList2.addLast(new Course("LastCourse  "+3));
        for(int j=0;j<linkedList2.size();j++){
            System.out.println(linkedList2.get(j));
        }

在这里插入图片描述

删除

在这里插入图片描述
当链表中没有数据,但是删除节点时显示NoSuchElementException 错误
代码演示

ArrayList<Course> arrayList1 = new ArrayList();
        LinkedList<Course> linkedList1 = new LinkedList<>();
// 初始化5个对象
        int i = 0;

        LinkedList<Course> linkedList2 = new LinkedList<>();
        for (i=0; i < 5; i++) {
            linkedList2.addFirst(new Course("linkedList2  "+i));
        }
        Course course=new Course("特殊course");
        linkedList2.add(course);
        for (i=0; i < 5; i++) {
            arrayList1.add(new Course("arrayList1  "+i));
        }
        linkedList2.addAll(arrayList1);
        linkedList2.addLast(new Course("LastCourse  "+3));
        for(int j=0;j<linkedList2.size();j++){
            System.out.println(linkedList2.get(j)+"    ");
        }
        linkedList2.remove();//删除第一个节点
        linkedList2.remove(course);//删除特定对象,只会删除最靠前的那个
        linkedList2.remove(3);//删除特定位置节点
        linkedList2.removeFirst();//删除头节点
        linkedList2.removeLast();//删除尾节点
        System.out.println();
        for(int j=0;j<linkedList2.size();j++){
            System.out.println(linkedList2.get(j)+"   ");
        }

获取get与indexOf

当链表中没有数据,但是获取节点时显示NoSuchElementException 错误
代码演示

ArrayList<Course> arrayList1 = new ArrayList();
        LinkedList<Course> linkedList1 = new LinkedList<>();
// 初始化5个对象
        int i = 0;

        LinkedList<Course> linkedList2 = new LinkedList<>();
        for (i=0; i < 5; i++) {
            linkedList2.addFirst(new Course("linkedList2  "+i));
        }
        Course course=new Course("特殊course");
        linkedList2.add(course);
        for (i=0; i < 5; i++) {
            arrayList1.add(new Course("arrayList1  "+i));
        }
        linkedList2.add(course);
        linkedList2.addAll(arrayList1);
        linkedList2.addLast(new Course("LastCourse  "+3));
        for(int j=0;j<linkedList2.size();j++){
            System.out.println(linkedList2.get(j)+"    ");
        }
        System.out.println();
        System.out.println(linkedList2.get(5));//获取index为5的节点,index从0开始
        System.out.println(linkedList2.getFirst());//获取第一个节点
        System.out.println(linkedList2.getLast());//获取最后一个节点
        System.out.println(linkedList2.indexOf(course));//通过对象获取index值,没找到返回-1,找到了就是正找第一个的index
        System.out.println(linkedList2.lastIndexOf(course));//通过对象获取index值,没找到返回-1,找到了就是反找第一个的index

在这里插入图片描述

contains(Object o)

验证o在链表中是否存在,存在返回index,不存在返回-1,底层调用的是indexOf(Object o)方法

set(int index, E element)

将index位置下的数据换成element

peek()

**源码分析:**返回第一个节点的数值

public E peek() {
        final Node<E> f = first;
        return (f == null) ? null : f.item;
    }

element() 的区别都是获取头节点的数值,不同的是,peek()可以是头节点为null的情况, element()不行,会报NoSuchElementException错误

poll()

源码分析: 将头节点删除并输出它的值

public E poll() {
        final Node<E> f = first;
        return (f == null) ? null : unlinkFirst(f);
    }
 private E unlinkFirst(Node<E> f) {
        // assert f == first && f != null;
        final E element = f.item;
        final Node<E> next = f.next;
        f.item = null;
        f.next = null; // help GC
        first = next;
        if (next == null)
            last = null;
        else
            next.prev = null;
        size--;
        modCount++;
        return element;
    }

push(E e)

即头增,调用了addFirst(E e) 方法

pop()

即删除头节点,将头节点设为原头节点的next值,调用了removeFirst()头删方法

LinkedList与ArrayList区别

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/y75xwr/article/details/106865293