zoukankan      html  css  js  c++  java
  • 浅谈LinkedList

    浅谈LinkedList

    LinkedList是一个链表结构类型的列表,底层通过链表结构来存储数据的链式存储,可以无限链接新元素(受限于硬盘存储容量),不存在ArrayList(底层使用数组实现)中的数组扩容问题,具有插入,删除元素快捷、方便的特点,但因为每个节点需要有上一个节点和下一个节点的引用,从而导致了每个结点需要存储空间的增加,而且不能做到像ArrayList那种快速随机访问指定元素(即可以直接根据索引访问元素值),在LinkedList中获取元素都需要从头节点开始逐个访问链表节点,源码中的节点查找方法(node(int index)方法)使用了头节点和尾节点同时往中间节点查找的方式,一定程度上提高了查找效率,以下为对应的源码:

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

    下面我们开始进入LinkedList的介绍,依旧是从构造方法开始讲起,在LinkedList中为我们提供了两个构造方法,我们来逐个查看:

    LinkedList();

    这是一个无形参的空构造,方法内除了隐藏的super();再没有其他初始化代码,实例对象中的未赋值属性将全部按照成员变量的默认值进行赋值,分别有first和last,还有一个size属性赋值为0

    LinkedList(Collection<? extends E> c);

    该构造方法传入了一个集合对象c,并在构造方法的首行使用this()调用了空构造方法,并在之后调用addAll(Collection<? extends E> c)方法将传入集合的所有元素添加到列表中

    public LinkedList(Collection<? extends E> c) {
        //调用空构造
        this();
        //将集合中的所有元素添加到列表的尾部
        addAll(c);
    }
    

    到这里构造方法部分也就介绍完了,在介绍LinkedList中提供的公开方法之前,要注意的是LinkedList和ArrayList一样是一个非线程安全的类对象,所以如果涉及并发操作,建议在初始化的时候就调用Collections.synchronizedList(list)来将线程不安全的列表对象转化为线程安全的对象,内部的实现原理:使用代理模式在原来的方法执行之前嵌套了一个同步机制,这里摘取其中的size()方法,源码如下:

    public int size() {
       synchronized (mutex) {  //同步代码块
           return c.size();    //c为被代理对象
       }
    }
    

    因为给所有的方法加上锁会降低代码的执行效率,而且有些方法是不需要加锁的,如果不想对所有的方法都加锁,可以在需要加锁的特定方法调用之前手动的做同步处理

    下面进入LinkedList中公开方法的详细解析,对于功能完全一样的方法会做为同类处理,对于索引这个词语的使用,仅表示从首节点开始算起,迭代的个数值减一,非真正的数组索引:

    offer(E element); offerLast(E element); add(E element);

    这三个方法功能相同,都是往列表的尾部链接上新元素,并返回一个布尔值,在offer(E element)方法中仅仅只是调用了add(E element)方法,没有做其他任何操作,而offerLast(E element)调用的是addLast(E element)方法,在add(E element)和addLast()方法中调用了内部的linkLast(E element)方法

    offerFirst(E element);

    该方法用于往列表的头部链接上新地元素,并返回一个布尔值,在offerFirst(E element)方法中调用的是addFirst(E element)方法,在addFirst(E element)方法调用内部的linkFirst(E element)方法

    add(int index, E element);

    该方法用于在指定索引的位置插入指定元素,无返回值

    push(E element); addFirst(E element);

    这两个方法功能相同,都是往列表头部链接上新元素,无返回值,在push(E element)方法中仅仅只是调用了addFirst(E element)方法,没有做其他任何操作,在addFirst(E element)中调用了内部的linkFirst(E element)方法

    addLast(E element);

    该方法用于在列表尾部链接上新元素,无返回值,方法内调用内部的linkLast(E element)方法

    pop(); remove(); removeFirst();

    这三个方法功能相同,都是移除列表的首元素,如果首元素不存在会抛出异常,存在则返回首元素的内容值,在pop()方法和remove()方法中仅仅只是调用了removeFirst()方法,没有做其他任何操作,在removeFirst()方法调用了内部的unlinkFirst(first)方法

    removeLast();

    该方法用于移除列表的尾元素,如果尾元素不存在会抛出异常,存在则返回尾元素的内容值,在removeLast()方法调用了内部的unlinkLast(last)方法

    poll(); pollFirst();

    这两个方法功能相同,都是移除列表的首元素并返回该元素,与removeFirst()方法不同的是,如果首元素不存在则返回null,poll()方法和pollFist()方法内都是调用的内部unlinkFirst(first)方法

    pollLast();

    该方法用于移除列表的尾元素并返回该元素,与removeLast()方法不同的是,如果尾元素不存在则返回null,方法内调用的是内部unlinkLast(last)方法

    peek(); peekFirst();

    这两个方法功能相同,都是获取列表的首元素的内容值,而且不从列表中删除该元素,如果首节点不存在则返回null

    peekLast();

    该方法用于获取列表的尾元素的内容值,而且不从列表中删除该元素,如果尾元素不存在则返回null

    element(); getFirst();

    这两个方法功能相同,都是检索列表的首元素、返回该元素而且不删除该元素,如果首元素不存在则抛出异常,在element()方法中仅仅只是调用了getFirst()方法,没有做其他任何操作

    getLast();

    该方法用于检索列表尾元素、返回该元素而且不删除该元素,如果尾元素不存在,则抛出异常

    removeFirstOccurance(Object o); remove(Object o);

    这两个方法功能相同,都是用于删除列表中出现的第一个指定元素,返回值为布尔值,在removeFirstOccurance(Object o)方法中仅仅只是调用了remove(Object o)方法,没有做其他任何操作,remove(Object o)方法内调用内部方法unlink(x)方法,其中x为迭代查找到的列表元素

    removeLastOccurance(Object o);

    该方法用于删除列表中最后一个指定元素,返回值为布尔类型,方法内调用内部方法unlink(x)方法,其中x为迭代查找到的列表元素

    remove(int index);

    该方法用于删除列表中的指定索引位置的元素,并返回被删除的元素内容值,方法内调用内部方法unlink(node(index))方法

    addAll(Collection<? extends E> c);

    该方法用于向列表中添加传入集合的元素值,内部调用addAll(size,c);即从列表尾部链接所有新元素

    addAll(int index, Collection<? extends E> c);

    该方法用于向列表中的指定索引位置链接上所有的新元素

    get(int index);

    该方法用于获取指定索引位置的列表元素内容值

    indexOf(Object o);

    该方法用于获取指定元素在列表中第一次出现的索引值,未找到则返回-1

    lastIndexOf(Object o);

    该方法用于获取指定元素在列表中最后一次出现的索引值,未找到则返回-1

    contains(Object o);

    该方法用于判断列表中是否含有特定的元素值,内部调用indexOf(Object o)方法

    size();

    该方法用于获取列表中元素的个数值,即列表的长度

    set(int index, E element);

    该方法用于将指定索引的元素替换为传入值,并返回被替换下元素的内容值

    clear();

    该方法用于清空列表中的所有值,用遍历方式清空所有列表元素,最后把last=first=0,并把size=0

    toArray(T[] a);

    该方法用于将列表转化为数组,可以在形参中指定返回数组的类型,例如:list.toArray(new String[0])即可将列表转化为String[]类型的数组并返回,无参方法相当于传入new Object[0],返回值类型为Object[]

    clone();

    该方法用于返回一个当前列表的浅克隆对象

    listIterator(int index);

    该方法用于返回一个当前列表的迭代器对象,可以指定迭代起始位置,内部提供了hasNext(),next(),hasPrevious(),previous(),nextIndex(),previousIndex(),remove()-删除当前位置的元素值,set(E element)-替换当前位置的元素值,add(E element)-在当前位置插入新的元素,forEachRemaining(Consumer<? super E> action)-用于对所有未遍历对象执行传入的指定操作逻辑
    注意:remove()方法不可连续调用多次,因为在一次调用之后,内部的lastReturned属性将变为null,当下一次调用previous()或next()方法时会重新被赋值,其次,在使用迭代器遍历列表元素时,不可以直接使用外部列表的方法对列表的结构进行修改(新增元素或删除元素),否则会报错,不过可以使用迭代器内部的add和remove方法来实现元素的新增和删除操作

    descendingIterator();

    该方法用于返回一个逆序的迭代器对象,实现原理:内部包含一个listIterator对象,并将索引初始化为size,然后每当调用descendingIterator的next方法时,调用内部listIterator的previous方法,previous方法同理

    spliterator();

    该方法用于返回一个可分割的列表对象(该对象底层为数组结构),可以用于多线程并发操作同一个列表的多个分割对象,调用该方法返回的对象可以调用trySplit()方法进行元素分割(五五分成),不过要注意的是第一次调用的trySplit()方法和后面调用的trySplit()方法不是同一个,具体查看源码:

    //首次调用的是列表对象调用spliterator方法生成的LLSpliterator内部类的trySplit方法,返回值为ArraySpliterator实例对象
    public Spliterator<E> trySplit() {
        Node<E> p;
        //获取剩余元素个数
        int s = getEst();
        //若元素个数大于1,并且首节点存在
        if (s > 1 && (p = current) != null) {
            //设置单次切割上限值,最大一次切分1024个元素
            int n = batch + BATCH_UNIT;
            //调整切割上限值
            if (n > s)
                n = s;
            //调整切割上限值,其中MAX_BATCH=2^25=33554432
            if (n > MAX_BATCH)
                n = MAX_BATCH;
            //新建一个对象数组,用于存储分割出的元素,因为ArraySpliterator是不支持链表结构的
            Object[] a = new Object[n];
            int j = 0;
            //其中的j<n用于控制切割数量
            do { a[j++] = p.item; } while ((p = p.next) != null && j < n);
            //将当前元素置为切割出去元素的后一个元素
            current = p;
            //将batch值置为切分出去的最后一个元素的索引的下一个索引位置
            batch = j;
            //当前列表元素个数减取切割出去的个数
            est = s - j;
            //调用Spliterators.spliterator方法,返回一个ArraySpliterator
            //下一个调用的trySplit将不再是这里的trySplit,而是ArraySpliterator中的trySplit方法,对列表元素进行对半切分
            return Spliterators.spliterator(a, 0, j, Spliterator.ORDERED);
        }
        return null;
    }
    
    public static <T> Spliterator<T> spliterator(Object[] array, int fromIndex, int toIndex, int additionalCharacteristics) {
        checkFromToBounds(Objects.requireNonNull(array).length, fromIndex, toIndex);
        return new ArraySpliterator<>(array, fromIndex, toIndex, additionalCharacteristics);
    }
    
    //第一次之后调用的trySplit方法,来自ArraySpliterator类中
    public Spliterator<T> trySplit() {
        int lo = index, mid = (lo + fence) >>> 1;
        return (lo >= mid) ? null : new ArraySpliterator<>(array, lo, index = mid, characteristics);
    }
    

    使用spliterator的示例代码

    LinkedList<Object> list = new LinkedList<>();
    list.add("1");list.add("2");list.add("3");list.add("4");list.add("5");
    list.add("6");list.add("7");list.add("8");list.add("9");
    Spliterator<Object> spliterator = list.spliterator();
    Spliterator<Object> a = spliterator.trySplit();
    Spliterator<Object> b = a.trySplit();
    Spliterator<Object> c = a.trySplit();
    Spliterator<Object> d = b.trySplit();
    a.forEachRemaining(System.out::println);
    System.out.println();
    b.forEachRemaining(System.out::println);
    System.out.println();
    c.forEachRemaining(System.out::println);
    System.out.println();
    d.forEachRemaini
    

    对应的处理结果为:

    7
    8
    9
    
    3
    4
    
    5
    6
    
    1
    2
    

    如果对你有帮助,点个赞,或者打个赏吧,嘿嘿
    整理不易,请尊重博主的劳动成果

  • 相关阅读:
    Java Output流写入包装问题
    SpringBoot项目单元测试不经过过滤器问题
    SpringSecurity集成启动报 In the composition of all global method configuration, no annotation support was actually activated 异常
    JWT jti和kid属性的说明
    Maven 排除依赖
    第五章 基因概念的发现
    第三章 孟德尔遗传的拓展
    第二章 孟德尔遗传
    第一章 引言
    GWAS全基因组关联分析
  • 原文地址:https://www.cnblogs.com/Mango-Tree/p/12731182.html
Copyright © 2011-2022 走看看