287 - LinkedList实现类

1、常用方法

代码示例:

package test5_LinkedList;

import jdk.nashorn.internal.runtime.linker.LinkerCallSite;

import java.util.Iterator;
import java.util.LinkedList;

/**
 * @Auther: zhoulz
 * @Description: test5_LinkedList
 * @version: 1.0
 */
public class Test1 {
    public static void main(String[] args) {
        /*
        LinkedList常用方法:(这里介绍其特有的,常见的如add等就不介绍了)
        增加:addFirst(E e)、addLast(E e)
              offer(E e)、offerFirst(E e)、offerLast(E e)
        删除:poll()
              pollFirst()、pollLast() —— JDK1.6以后新出的方法,提高了代码的健壮性
              removeFirst()、removeLast() —— JDK1.0就有了
        修改:
        查看:element()
              getFirst()、getLast()
              indexOf(Object o)、lastIndexOf(Object o)
              peek()、peekFirst()、peekLast()
        判断:
         */

        //创建一个LinkedList集合对象:
        LinkedList<String> list = new LinkedList<>();
        //点进去发现是LinkedList<E>,即泛型类,
        // 而我们推荐泛型类在创建实例的时候把泛型加上

        list.add("aaa");
        list.add("bbb");
        list.add("ccc");
        list.add("ddd");
        list.add("aaa"); //验证能不能添加相同的元素,结果是可以的
        System.out.println(list);

        //1、增加方法:
        //首尾添加元素:
        list.addFirst("qqq");
        list.addLast("www");
        System.out.println(list);

        list.offer("eee");//添加元素在尾端
        System.out.println(list);

        list.offerFirst("oo");
        list.offerLast("pp");
        System.out.println(list);

        System.out.println("删除方法-------");
        //2、删除方法:
        list.poll(); //检索并删除此列表的头(第一个元素)
        System.out.println(list);
        //或直接:
        System.out.println(list.poll()); //还会把删除的元素进行返还
        System.out.println(list);

        //检索并删除此列表的 第一个/最后一个 元素,如果此列表为空,则返回 null 。
        System.out.println(list.pollFirst());
        System.out.println(list.pollLast());
        System.out.println(list);
        //删除—同上:
        System.out.println(list.removeFirst());
        System.out.println(list.removeLast());
        System.out.println(list);

        //疑惑:
        //同样是删除(后面的查看系列方法-同理),有什么区别吗?
        //list.clear();//清空集合
        //System.out.println(list);
        //System.out.println(list.pollFirst());//空集合删除返回null
        //System.out.println(list.removeFirst()); //空集合删除会报错/异常

        //后面的查看系列方法——类似,不在举例

        //集合的遍历:
        System.out.println("遍历-----");
        System.out.println(list);
        //1、普通for循环 —— 可以的,有一个get()方法
        System.out.println("普通for循环——遍历:");
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
        //2、增强for循环
        System.out.println("增强for循环——遍历:");
        for (String s : list){
            System.out.println(s);
        }
        //3、迭代器-iterator
        System.out.println("迭代器-iterator —— 遍历:");
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }

        //关于迭代器,还有一种写法:
        //下面这种方式好,节省内存:it 作用域
        System.out.println("另一种写法—— 遍历");
        for (Iterator<String> it = list.iterator();it.hasNext();){
            System.out.println(it.next());
        }
    }
}

2、LinkedList简要底层原理图

3、模拟LinkedList源码

代码示例:
先创建节点类:Node

package test6_LinkedList_simulation;

/**
 * @Auther: zhoulz
 * @Description: test5_LinkedList
 * @version: 1.0
 */
public class Node { //创建节点类
    //三个属性 —— 不确定什么类型,所以就用Object
    //1、上一个元素的地址:
    //Object pre;
    //由于上一个节点也是Node类型,故就用Node
    private Node pre;

    //2、当前存入的元素:
    private Object obj;
    //3、下一个元素的地址:
    //Object next;
    private Node next;

    //可以加个修饰符private,但是同时得加上set、get方法

    //set、get方法
    public Node getPre() {
        return pre;
    }

    public void setPre(Node pre) {
        this.pre = pre;
    }

    public Object getObj() {
        return obj;
    }

    public void setObj(Object obj) {
        this.obj = obj;
    }

    public Node getNext() {
        return next;
    }

    public void setNext(Node next) {
        this.next = next;
    }

    //再加上一个toString()方法
    @Override
    public String toString() {
        return "Node{" +
                "pre=" + pre +
                ", obj=" + obj +
                ", next=" + next +
                '}';
    }
}

再创建自己的 LinkedList 集合:

package test6_LinkedList_simulation;

/**
 * @Auther: zhoulz
 * @Description: test6_LinkedList_simulation
 * @version: 1.0
 */


/**
创建自己的 LinkedList 集合    —— 规范注释!!!!
 */
public class MyLinkedList {
    //里面有一个个节点Node
    /*Node a;
    Node b;
    Node c;*/
    //但是如果节点太多,还这么创建的话,就不友好

    //链中一定有一个首节点:
    Node first;
    //链中一定有一个尾节点:
    Node last;
    //计数器:
    int count = 0;
    //提供一个构造器:
    public MyLinkedList(){

    }

    //定义方法

    //1、添加元素的方法
    public void add(Object o){ //不知道什么类型,所以用Object
        if(first == null){//如果你添加的元素是第一个节点
            //将添加的元素封装为一个Node对象
            Node n = new Node();
            n.setPre(null); //没有上一个元素
            n.setObj(o);    //设置当前元素
            n.setNext(null); //下一个元素也没有
            //即,只有这一个节点
            //当前链中,第一个节点变为n
            first = n;
            //当前链中最后一个节点也是n
            last = n;
        }else {//如果已经不是链中第一个节点了
            //首先还是的创建一个Node对象:
            //将添加的元素封装为一个Node对象
            Node n = new Node();

            n.setPre(last);//上一个元素就是当前链的最后一个元素
            n.setObj(o);
            n.setNext(null);

            //现在,这个元素已经指向了上一个元素
            //但是,还要加一个操作:
            // 就是让上一个元素(即:当前链(添加前的链)中的最后一个节点)指向该元素n

            //即:当前链(添加前的链)中的最后一个节点的下一个元素 要指向n
            last.setNext(n);
            //然后,将最后一个节点变为n
            last = n;
        }
        //链中元素数量加1
        count++;
    }

    //2、得到集合中元素的数量的方法:
    public int getSize(){
        return count;
    }

    //3、通过下标得到元素
    public Object get(int index){
        //首先,获取链表的头元素:
        Node n = first;
        //一路next得到想要的元素
        for (int i = 0; i < index; i++) {
            n = n.getNext();//一直循环,会将n置为当前的节点
        }
        //再将这个节点的元素(中间那个元素)获取到
        return n.getObj();
    }
}

//测试类
class Test{
    public static void main(String[] args) {
        //创建一个MyLinkedList集合对象
        MyLinkedList ml = new MyLinkedList();

        //只创建对象还不行,因为集合里面还什么都没有
        // 所以,还要往里添加元素
        //记住,添加操作之前,要在集合里面先模拟,如:add这个动作/方法
        ml.add("aa");
        ml.add("bb");
        ml.add("cc");
        System.out.println(ml.getSize());

        //获取元素
        System.out.println(ml.get(1));
        System.out.println(ml.get(2));
    }
}

debug验证数据添加成功:

4、LinkedList源码解析

【1】JDK1.7和JDK1.8的LinkedList的源码是一致的

【2】源码:

1.public class LinkedList<E>{//E是一个泛型,具体的类型要在实例化的时候才会最终确定
2.        transient int size = 0;//集合中元素的数量
3.        //Node的内部类
4.        private static class Node<E> {
5.        E item;//当前元素
6.        Node<E> next;//指向下一个元素地址
7.        Node<E> prev;//上一个元素地址
8.
9.        Node(Node<E> prev, E element, Node<E> next) {
10.            this.item = element;
11.            this.next = next;
12.            this.prev = prev;
13.        }
14.    }
15.
16.        transient Node<E> first;//链表的首节点
17.        transient Node<E> last;//链表的尾节点
18.        //空构造器:
19.        public LinkedList() {
20.    }
21.        //添加元素操作:
22.        public boolean add(E e) {
23.        linkLast(e);
24.        return true;
25.    }
26.        void linkLast(E e) {//添加的元素e
27.        final Node<E> l = last;//将链表中的last节点给l 如果是第一个元素的话 l为null
28.                //将元素封装为一个Node具体的对象:
29.        final Node<E> newNode = new Node<>(l, e, null);
30.                //将链表的last节点指向新的创建的对象:
31.        last = newNode;
32.                
33.        if (l == null)//如果添加的是第一个节点
34.            first = newNode;//将链表的first节点指向为新节点
35.        else//如果添加的不是第一个节点 
36.            l.next = newNode;//将l的下一个指向为新的节点
37.        size++;//集合中元素数量加1操作
38.        modCount++;
39.    }
40.        //获取集合中元素数量
41.        public int size() {
42.        return size;
43.    }
44.        //通过索引得到元素:
45.        public E get(int index) {
46.        checkElementIndex(index);//健壮性考虑
47.        return node(index).item;
48.    }
49.        
50.    Node<E> node(int index) {
51.        //如果index在链表的前半段,那么从前往后找
53.        if (index < (size >> 1)) {
54.            Node<E> x = first;
55.            for (int i = 0; i < index; i++)
56.                x = x.next;
57.            return x;
58.        } else {//如果index在链表的后半段,那么从后往前找
59.            Node<E> x = last;
60.            for (int i = size - 1; i > index; i--)
61.                x = x.prev;
62.            return x;
63.        }
64.    }
66.}

 Collection接口持续扩充:子接口List下的三个实现类:ArrayList、LinkedList、Vector

(其中,常用的为ArrayList、LinkedList实现类,Vector已经被淘汰了。

 其中,List接口下的实现类的遍历方式有3种:

分别为:1)普通for循环; 2)增强for循环; 3)迭代器 iterator

下面再重点讲一下 迭代器 。

(以一个面试提展开)

5、面试题:iterator(), Iterator, Iterable 关系

【1】面试题:对应的关系

        发现,在接口Iterable中,有一个抽象方法:iterator()方法,而抽象方法要在具体的实现类中的得到实现。又发现在迭代器遍历(即:ArrayList底层)的时候用到/实现了这个方法.

(接口里面的方法都是抽象方法,但是在JDK1.8以后,添加了default修饰的非抽象方法。)

即:接口 Iterable中的抽象方法 iterator() 在底层 ArrayList 实现类中得到了实现

 并且,iterator()方法的返回值为 Iterator,点进去发现又是一个接口:

在这个 Iterator接口里面,有两个经典方法:

                                hasNext()、next(),这两个方法也是抽象方法。

那这两个方法抽象方法在哪实现的呢?(在Itr这个类中得到了具体的实现)

        见上图,iterator()方法返回的(new Itr() 就是具体的接口实现类,然后用返回值Iterator接口去接收(多态的一个小的应用)

        Itr 点进去发现, Itr这个类是ArrayList的内部类

       其中,Itr implements Iterator<E>:即 Itr 实现了Iterator,并且在其下,把hasNext()、next()、remove()等方法都重写了一下(这两个方法原来是在 Iterator接口里面的)。

【2】hasNext()、next()的具体实现

 说明:

ArrayList<E> 类下,有两个重要的属性:

//对应底层一个数组
transient Object[] elementData; // non-private to simplify nested class access
private int size;

【3】增强for循环  底层也是通过迭代器实现的

下面讲一个新的迭代器:

6、ListIterator迭代器

【1】加入字符串

代码示例:

package test8_ListIterator;

import java.util.ArrayList;
import java.util.Iterator;

/**
 * @Auther: zhoulz
 * @Description: test8_ListIterator
 * @version: 1.0
 */
public class Test1 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("aa");
        list.add("bb");
        list.add("cc");
        list.add("dd");
        list.add("ee");

        //在"cc"之后添加一个字符串"kk"
        //首先是对集合进行遍历:
        Iterator<String> it = list.iterator();
        while (it.hasNext()){
            //System.out.println(it.next());
            //将输入的结果和“cc”进行比较
            //如果遍历出来的字符串刚好和cc相等,
            if ("cc".equals(it.next())){
                list.add("kk"); //则在list后添加 kk
            }
        } //报错了!!!
    }
}

发现报错:ConcurrentModificationException —— 这是并发修改异常

 出错原因就是迭代器和 list 同时对集合进行操作:

解决办法:事情让一个“人”做 --》引入新的迭代器:ListIterator

迭代和添加操作都是靠ListIterator来完成的:

代码示例:

package test8_ListIterator;

import java.sql.SQLOutput;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;

/**
 * @Auther: zhoulz
 * @Description: test8_ListIterator
 * @version: 1.0
 */
public class Test1 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("aa");
        list.add("bb");
        list.add("cc");
        list.add("dd");
        list.add("ee");

        //在"cc"之后添加一个字符串"kk"
        //首先是对集合进行遍历:
        /*Iterator<String> it = list.iterator();
        while (it.hasNext()){
            //System.out.println(it.next());//遍历
            //将遍历的结果和“cc”进行比较
            //如果遍历出来的字符串刚好和cc相等,
            if ("cc".equals(it.next())){
                list.add("kk"); //则在list后添加 kk
            } // 报错了
        }*/

        //解决:
        //引入新的迭代器:ListIterator
        ListIterator<String> it = list.listIterator();
        while (it.hasNext()){
            //System.out.println(it.next());//将输入的结果和“cc”进行比较
            if ("cc".equals(it.next())){
                it.add("kk"); //注意,不是list.add("kk");了
                //通过it,即迭代器进行添加,上面的遍历也是迭代器进行的
            }
        }
        System.out.println(list);

        //这个迭代器的高级之处在于:除了可以判断/查看下一个元素
        System.out.println(it.hasNext());//遍历到底了,就没有下一个元素了
                                         //结果:false
        //还可以:
        //判断/查看上一个元素
        System.out.println(it.hasPrevious());//但是还有上一个元素
                                            //结果:true
        //所以可以做一个
        // 逆向遍历:
        while (it.hasPrevious()){
            System.out.println(it.previous());
        }
        //逆向遍历后(遍历到了数组的最上面),再查看:
        //应该是:没有上一个元素了,但是还有下一个元素
        System.out.println(it.hasNext());//true
        System.out.println(it.hasPrevious());//false
    }
}

即:现在有两个迭代器iterator() 和 listIterator() 。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值