zoukankan      html  css  js  c++  java
  • 关于java集合框架(二):List

    上一次整理详细说了各种Set的区别,这次再来整理以下各List。

    首先是LinkedList:

    允许有null元素,主要用于创建链表数据结构
    没有同步方法,如果多个线程同时访问一个List,则必须自己实现访问同步,解决方法是在创建List的时候构造一个同步的List:
    List list = Collections.synchronizedList(new LinkedList<>());
    LinkedList查找效率低。

    为什么查找效率低呢?

    我们来看看LinkedList的查找方法LinkedList.get(index i)的底层源码

    public class Test{
        public static void main(String[] args) {
            LinkedList<String> linkedList = new LinkedList<String>();
            linkedList.add("a");
            linkedList.add("b");
            linkedList.add("c");
    
            System.out.println(linkedList);
            System.out.println(linkedList.get(1));
        }
    }

    对于这段测试代码,输出结果是

    [a, b, c]
    b

    使用IDEA对linkedList.add的源码进行查看,得到如下代码:

        public E get(int index) {
            checkElementIndex(index);
            return node(index).item;
        }

    这段代码里用到了node方法(即查找编号所对应的链表节点),我们再来看看这个node的代码:

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

    这里index<(size >> 1)        >>指的是二进制右移一位,对于十进制数据来说,可以近似相当于对原始数据除以2。(整除,不要小数部分)

    所以这里就是指如果先把整体对半切开,如果index在前半部分的(<size/2),那就从第一个节点开始向后查找;而如果index在后半部分的,那就从最后一个节点开始向前查找。查找的方式是利用循环迭代,一个个向后(向前)查找,这种查找方式便是查找缓慢的原因(对于一个较大的链表来说,这样的查找方式会导致循环很多次)。

    然后是ArrayList:

    该类也是实现了List的接口,实现了可变大小的数组,随机访问和遍历元素时,提供更好的性能。该类也是非同步的,在多线程的情况下不要使用。超出容量时ArrayList 增长当前长度的50%,插入删除效率低。

    下面是ArrayList增长长度的源码

    private void grow(int minCapacity) {
            // overflow-conscious code
            int oldCapacity = elementData.length;
            int newCapacity = oldCapacity + (oldCapacity >> 1);
            if (newCapacity - minCapacity < 0)
                newCapacity = minCapacity;
            if (newCapacity - MAX_ARRAY_SIZE > 0)
                newCapacity = hugeCapacity(minCapacity);
            // minCapacity is usually close to size, so this is a win:
            elementData = Arrays.copyOf(elementData, newCapacity);
        }
    
        private static int hugeCapacity(int minCapacity) {
            if (minCapacity < 0) // overflow
                throw new OutOfMemoryError();
            return (minCapacity > MAX_ARRAY_SIZE) ?
                Integer.MAX_VALUE :
                MAX_ARRAY_SIZE;
        }

    可以看到是oldCapacity + (oldCapacity >> 1)(即old+old/2,增加50%)。

    对于查找速度快,是因为ArrayList是以数组方式存储的,所以访问元素只需要根据元素的下表访问对应的元素即可。下面依旧列出源码:

    public E get(int index) {
            rangeCheck(index);
    
            return elementData(index);
        }
    E elementData(int index) {
            return (E) elementData[index];
        }

    可以看到是直接return对应元素(以数组方式存储的数据,地址连续,下标也就代表着对应指针增量,数组名为头指针,所以可以根据下标直接取对应元素,但相应得,扩容时,程序会开辟一块全新的存储空间,并将原始数据复制进新的地址空间里,这相比链表来说效率是更低的。)

    至于插入删除为何效率低,在java集合框架(一)中已有说到,这里不再重复说明。

    常用的List便是以上两种。

  • 相关阅读:
    [洛谷P1155] 双栈排序
    [洛谷P4315] 月下”毛景“树
    [洛谷P2486] [SDOI2011]染色
    [HNOI2010] 弾飞绵羊
    mysql注入总结
    cisco交换机实现端口聚合
    python为运维人员打造一个监控脚本
    复习ACCESS注入
    利用sfc文件构建网络渗透
    FTP站点设置
  • 原文地址:https://www.cnblogs.com/MYoda/p/11151045.html
Copyright © 2011-2022 走看看