8、集合--LinkedList的测试以及相关方法的源码分析

 

LinkedList测试

    public static void main(String[] args) {

        List list = new LinkedList();

        //添加元素
        list.add(1);
        list.add(2);
        //在指定位置添加元素
        list.add(0,0);
        System.out.println("list中的元素:" + list);

        //对象首次出现的位置
        System.out.println("0对象首先出现的位置:"+list.indexOf(0));
        //对象最后出现的位置
        System.out.println("1对象首先出现的位置:"+list.lastIndexOf(1));

        //for循环遍历
        for (int i = 0;i < list.size();i++){
            System.out.println("for循环遍历:" + list.get(i));
        }

        //返回列表的长度
        System.out.println("长度:" + list.size());

        //迭代器遍历
        Iterator it = list.iterator();
        while (it.hasNext()){
            System.out.println("迭代器遍历 :" + it.next() );
        }

        //将对象添加在集合的开头
        ((LinkedList) list).addFirst("a");
        //将对象添加在集合的末尾
        ((LinkedList) list).addLast("b");
        System.out.println("list中的元素:" + list);

        //修改指定位置的对象
        list.set(0,"c");
        System.out.println("修改0位置的对象值为c:" + list);

        //获取集合中开头的元素
        System.out.println("开头的元素:" + ((LinkedList) list).getFirst());

        //获取集合中末尾的元素
        System.out.println("末尾的元素:" + ((LinkedList) list).getLast());

        //删除列表开头的元素
        ((LinkedList) list).removeFirst();//删除的是a
        //删除列表末尾的元素
        ((LinkedList) list).removeLast();//删除的是b
        System.out.println("删除开头和结尾之后的列表:" + list);

        //删除指定索引的位置
        list.remove(0);
        System.out.println("删除指定索引位置的列表:" + list);

        //判断列表是否为空
        System.out.println("是否为空:" +  list.isEmpty());

        //是否包含对象1
        System.out.println("是否包含对象1 :" + list.contains(1));

        //清空集合
        list.clear();
    }

 

相关底层的方法实现:

1、new对象之后List list = new LinkedList();

可以看作是一个双向的链表每个节点都有first节点last节点

方法都是通过移动节点指向来实现的

  //集合元素数量
  transient int size = 0;   //链表头节点 transient Node<E> first;   //链表为节点 transient Node<E> last;
  protected transient int modCount = 0;
 public LinkedList() { }
  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;
   }
  }

 

2、add(E e)方法

    public boolean add(E e) {
        linkLast(e);
        return true;
    }

调用linkLast()方法:

 void linkLast(E e) {q
        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++;
    }
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;
        }
    }

 

3、indexOf(Object o):o元素首先出现的位置

首先定义一个索引值index=0;用于返回元素第一次出现的位置

判断传入的对象o是否为null

①、如果为null

  使用for循环进行遍历

  从起始节点first开始

  然后判断Node类的item属性是否等于null 

  如果等于null则返回index

  否则index++

②、如果传入的对象o不为null

  直接进行遍历链表

  使用equals()方法进行判断元素o和item属性值是否相同

  如果等于则返回index  

  否则index ++

③、如果这个对象不存在

  此时直接返回-1

此时在循环中使用x=x.next向下循环遍历

public int indexOf(Object o) {
        int index = 0;
        if (o == null) {//如果目标对象是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;
    }

 

4、lastIndexOf(Object o):判断元素最后一次出现的所以位置

此时定义index所引值大小size,从链表的最后面开始遍历,即尾节点开始遍历

判断传入的元素o是否为空:

①、如果传入的元素o为null

  此时循环中定义的Node对象x=last即尾节点

  首先将index--

  在去判断x.item 是否等于null,然后返回index

  遍历是使用x = x.prev只想前置节点

②、如果不为null

  则首先让index--

  然后使用equals()方法进行数据的对比

  如果比对相同则返回index

③、如果这个元素o不存在则返回-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;
    }

 

5、size()方法:返回链表的长度

此时直接返回size的值

    public int size() {
        return size;
    }

 

6、get(int index)方法:获取指定索引位置的值

checkElementIndex(index)方法检测index是否越界

具体的实现在isElementIndex(int index)进行返回

    public E get(int index) {
        checkElementIndex(index);
        return node(index).item;
    }
private void checkElementIndex(int index) {
        if (!isElementIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
    private boolean isElementIndex(int index) {
        return index >= 0 && index < size;
    }
Node<E> node(int index) {
        // assert isElementIndex(index);

        if (index < (size >> 1)) {
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }

最后返回node(int index)方法

进行查找指定位置的Node类的实例

 

7、addFirst(E e):在链表的第一个位置添加元素o

    public void addFirst(E e) {
        linkFirst(e);
    }
 private void linkFirst(E e) {
        final Node<E> f = first;
        final Node<E> newNode = new Node<>(null, e, f);
        first = newNode;
        if (f == null)
            last = newNode;
        else
            f.prev = newNode;
        size++;
        modCount++;
    }

首先定义一个Node的实例 f 并且只想first(即只想首链)

在新建一个Node的实现newNode,用于创建新的实例,且next指向f

再把newNode的值赋值给first(首节点)

判断此时的f是否为空(首节点是否为null)

①、如果为空:则将newNode赋值给last

②、不为空:则将原首节点的prev指向新的节点newNode

 

8、addLast(E e):将对象添加在集合的末尾

public void addLast(E e) {
        linkLast(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++;
    }

首相定义Node的实例 l 指向尾节点

在定义一个新的实例newNode,且prev属性指向l(即last尾节点)

此时在将newNode赋值非尾节点last

判断l是否为空:尾节点是否为空

①、如果为空:则把newNode赋值非first首节点

②、不为空:则将尾节点l的next属性指向新建的节点newNode

 

9、set(int index,E element):修改指定位置的元素值

①、调用checkElementIndex()方法去检验index是否合法

②、调用node(int index)方法去找指定位置上的node节点

③、将旧值保存在oldValue上,在进行将要改的值element赋值给找到的节点

④、返回旧值oldValue

public E set(int index, E element) {
        checkElementIndex(index);
        Node<E> x = node(index);
        E oldVal = x.item;
        x.item = element;
        return oldVal;
    }
    private void checkElementIndex(int index) {
        if (!isElementIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
Node<E> node(int index) {
        if (index < (size >> 1)) {
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }

 

10、getFirst():获取集合中开头的元素

public E getFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return f.item;
    }

首相定义一个Node实例f指向链表的首节点first

在进行判断首节点是否为null

最后返回首节点的item属性值

 

11、getLast():获取集合中末尾的元素

public E getLast() {
        final Node<E> l = last;
        if (l == null)
            throw new NoSuchElementException();
        return l.item;
    }

与getFirst()方法类似

 

12、removeFirst():删除列表开头的元素

    public E removeFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return 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;
    }

首先定义一个Node实例f指向首节点first

在进行判断f是否为空

最后调用unlinkFirst()方法

定义一个值element用于保存首节点中的值(f.item)

在定义一个Node的实例next作为临时变量来保存第二个节点(first.next)

然后将first的item和next值均赋值为null

最后将first重新指向next的临时变量

判断临时变量next是否为空:

为空:将尾节点置为null

不为空:将临时节点的prev的属性值置为空

 

13、removeLast():删除列表末尾的元素

 public E removeLast() {
        final Node<E> l = last;
        if (l == null)
            throw new NoSuchElementException();
        return unlinkLast(l);
    }
 private E unlinkLast(Node<E> l) {
        // assert l == last && l != null;
        final E element = l.item;
        final Node<E> prev = l.prev;
        l.item = null;
        l.prev = null; // help GC
        last = prev;
        if (prev == null)
            first = null;
        else
            prev.next = null;
        size--;
        modCount++;
        return element;
    }

和removeFirst()类似

 

14、remove(int index):删除指定索引的位置

 public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }
 E 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;
    }

思想:

找到指定位置上的节点之后假设为A

A的前置节点为A1,A的后置节点为A2

此时将A1.next 指向 A2

同时将A节点置为null

 

15、remove(Object o):删除元素o

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;
    }
 E 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;
    }

思想:首先判断该元素o是否为空在进行遍历链表

unlink()方法的思想同上

 

16、isEmpty():集合是否为空

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

即判断size()是否为0

 

17、contaions(Object o):判断集合中是否包含元素o

    public boolean contains(Object o) {
        return indexOf(o) != -1;
    }
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;
    }

此时的思想是:

查找元素第一次出现的位置进行返回

 

18、clear():清空集合

public void clear() {

        for (Node<E> x = first; x != null; ) {
            Node<E> next = x.next;
            x.item = null;
            x.next = null;
            x.prev = null;
            x = next;
        }
        first = last = null;
        size = 0;
        modCount++;
    }

即从头遍历集合

同时将item、next、prev三个属性值都置为0

最后将size的值置为0

 

以上是对LinkedList底层一些方法实现的分析

更多的实现可以看底层源码

 

posted @ 2019-05-10 23:24  MrChengs  阅读(290)  评论(0编辑  收藏  举报