LInkList 源码分析

1.集合结构:

考虑几个个问题:

1.概述

2.重要方法实现

     add 方法:

   remove 元素:

   修改元素:

    查找元素

3.重要特性:

            迭代方式,

            扩容集合

     

4.使用场景:

对1:概述

如上图:实现dequar 接口,集成AbstractequencnitonList:

对2:

     add 方法:

常见的方法有以下三种

1

2

3

linkedList.add(E e)

linkedList.add(int index, E element)

linkedList.addAll(Collection<? extends E> c)

public boolean add(E e) {
        linkLast(e);
        return true;
    }
 
 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++;
}
 
public void add(int index, E element) {
        checkPositionIndex(index);
 
        if (index == size)
            linkLast(element);
        else
            linkBefore(element, node(index));
    }
 
 void linkBefore(E e, Node<E> succ) {
        // assert succ != null;
        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++;
    }
 
public boolean addAll(Collection<? extends E> c) {
        return addAll(size, c);
    }

第2、6-16行:创建一个newNode它的prev指向之前队尾节点last,并记录元素值e,之前的队尾节点last的next指向当前节点,size自增,modcount自增

第18-20,27-38行:首先去检查下标是否越界,然后判断如果加入的位置刚好位于队尾就和我们add(E element)的逻辑一样了,如果不是则需要通过 node(index)函数定位出当前位于index下标的node,再通过linkBefore()函数创建出newNode将其插入到原先index位置。

OK,添加元素也很简单,如果是在队尾进行添加的话只需要创建一个新Node将其前置节点指向之前的last,如果是在队中添加节点,首选拆散原先的index-1、index、index+1之间的联系,新建节点插入进去即可。

2.3 删除元素

  常见方法有以下这几个方法

1

2

3

linkedList.remove(int index)

linkedList.remove(Object o)

linkedList.remove(Collection<?> c)

public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }
 
unlink(Node<E> x) {
        // assert x != null;
        final E element = x.item;
        final Node<E> next = x.next;
        final Node<E> prev = x.prev;
 
        if (prev == null) {
            first = next;
        } else {
            prev.next = next;
            x.prev = null;
        }
 
        if (next == null) {
            last = prev;
        } else {
            next.prev = prev;
            x.next = null;
        }
 
        x.item = null;
        size--;
        modCount++;
        return element;
    }
 
public boolean remove(Object o) {
        if (o == null) {
            for (Node<E> x = first; x != null; x = x.next) {
                if (x.item == null) {
                    unlink(x);
                    return true;
                }
            }
        } else {
            for (Node<E> x = first; x != null; x = x.next) {
                if (o.equals(x.item)) {
                    unlink(x);
                    return true;
                }
            }
        }
        return false;
    }
 
 public boolean removeAll(Collection<?> c) {
        Objects.requireNonNull(c);
        boolean modified = false;
        Iterator<?> it = iterator();
        while (it.hasNext()) {
            if (c.contains(it.next())) {
                it.remove();
                modified = true;
            }
        }
        return modified;
    }

第1-4,6-30行:首先根据index通过方法值node(index)来确定出集合中的下标是index的node,咋们主要看unlink()方法,代码感觉很多,其实只是将当前要删除的节点node的头结点的尾节点指向node的尾节点,然后将下标为index的node置空,供GC回收

第32-49行:首先判断一下当前要删除的元素o是否为空,然后进行for循环定位出当前元素值等于o的节点node,然后再走的逻辑就是上面我们看到过的unlink()方法,也很简单,比remove(int index) 多了一步

LinkedList使用的是ListItr小结一下, 按下标删,也是先根据index找到Node,然后去链表上unlink掉这个Node。 按元素删,会先去遍历链表寻找是否有该Node,考虑到允许null值,所以会遍历两遍,然后再去unlink它。

  修改元素:

public E set(int index, E element) {

        checkElementIndex(index);

        Node<E> x = node(index);

        E oldVal = x.item;

        x.item = element;

        return oldVal;

    }

只有这一种方法,首先检查下标是否越界,然后根据下标获取当前Node,然后修改节点中元素值item。

2.6 查找元素

public E get(int index) {
    checkElementIndex(index);//判断是否越界 [0,size)
    return node(index).item; //调用node()方法 取出 Node节点,
}
 
 
 public int indexOf(Object o) {
        int index = 0;
        if (o == null) {
            for (Node<E> x = first; x != null; x = x.next) {
                if (x.item == null)
                    return index;
                index++;
            }
        } else {
            for (Node<E> x = first; x != null; x = x.next) {
                if (o.equals(x.item))
                    return index;
                index++;
            }
        }
        return -1;
    }
 
 public int lastIndexOf(Object o) {
        int index = size;
        if (o == null) {
            for (Node<E> x = last; x != null; x = x.prev) {
                index--;
                if (x.item == null)
                    return index;
            }
        } else {
            for (Node<E> x = last; x != null; x = x.prev) {
                index--;
                if (o.equals(x.item))
                    return index;
            }
        }
        return -1;
    }

通过node(index)方法获取节点,然后获取元素值,indexOf和lastIndexOf方法的区别在于一个是从头向尾开始遍历,一个是从尾向头开始遍历。

迭代器:

public Iterator<E> iterator() {
        return listIterator();
    }
 
public ListIterator<E> listIterator() {
        return listIterator(0);
    }
 
public ListIterator<E> listIterator(final int index) {
        rangeCheckForAdd(index);
 
        return new ListItr(index);
    }
 
private class ListItr extends Itr implements ListIterator<E> {
        ListItr(int index) {
            cursor = index;
        }
 
        public boolean hasPrevious() {
            return cursor != 0;
        }
 
        public E previous() {
            checkForComodification();
            try {
                int i = cursor - 1;
                E previous = get(i);
                lastRet = cursor = i;
                return previous;
            } catch (IndexOutOfBoundsException e) {
                checkForComodification();
                throw new NoSuchElementException();
            }
        }
 
        public int nextIndex() {
            return cursor;
        }
 
        public int previousIndex() {
            return cursor-1;
        }
 
        public void set(E e) {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();
 
            try {
                AbstractList.this.set(lastRet, e);
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
 
        public void add(E e) {
            checkForComodification();
 
            try {
                int i = cursor;
                AbstractList.this.add(i, e);
                lastRet = -1;
                cursor = i + 1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
    }

最后使用的迭代器是使用的ListIterator类,ArrayList内部使用的类,hasNext()方法和我们之前的一样,判断不等于size大小,然后next()获取元素主要也是E next = get(i);这样就又走到我们之前的获取元素的源码当中,获得元素值。

参考博文:

https://www.cnblogs.com/wjtaigwh/p/9883828.html

发布了550 篇原创文章 · 获赞 10 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/xiamaocheng/article/details/104476748
今日推荐