• 单列集合List


    List的常用子类

    如果要执行大量的增删操作选择LinkedList,如果只是查询的话用ArrayList

    ArrayList集合

    1. 有序存储,继承了List的特性,平时常用来查询,遍历数据

    2. 底层是数组结构,所以增删慢,查询快,效率高意味着线程不安全

    LinkedList集合

    1. 有序存储,继承了List的特性

    2. 底层是链表结构,所以增删快,查询慢,同样的效率高,但是线程不安全

    3. LinkedList是一个双向链表

    4. 特有方法

    public void addFirst(E e) :将指定元素插入此列表的开头。
    public void addLast(E e) :将指定元素添加到此列表的结尾。
    public E getFirst() :返回此列表的第一个元素。
    public E getLast() :返回此列表的最后一个元素。
    public E removeFirst() :移除并返回此列表的第一个元素。
    public E removeLast() :移除并返回此列表的最后一个元素
    ​
    //添加元素
    link.addFirst("AA");
    link.addLast("CC");
    System.out.println(link);
    // 获取元素
    System.out.println(link.getFirst());
    System.out.println(link.getLast());
    // 删除元素
    System.out.println(link.removeFirst());
    System.out.println(link.removeLast());
    System.out.println(link)

    Vector源码:

    复制代码
        /**
         * Adds the specified component to the end of this vector,
         * increasing its size by one. The capacity of this vector is
         * increased if its size becomes greater than its capacity.
         *
         * <p>This method is identical in functionality to the
         * {@link #add(Object) add(E)}
         * method (which is part of the {@link List} interface).
         *
         * @param   obj   the component to be added
         */
        public synchronized void addElement(E obj) {
            modCount++;
            ensureCapacityHelper(elementCount + 1);
            elementData[elementCount++] = obj;
        }
    复制代码

    Arraylist源码:

    复制代码
        /**
         * Appends the specified element to the end of this list.
         *
         * @param e element to be appended to this list
         * @return <tt>true</tt> (as specified by {@link Collection#add})
         */
        public boolean add(E e) {
            ensureCapacityInternal(size + 1);  // Increments modCount!!
            elementData[size++] = e;
            return true;
        }
    复制代码

    LinkedList源码:

    复制代码
        /**
         * Appends the specified element to the end of this list.
         *
         * <p>This method is equivalent to {@link #addLast}.
         *
         * @param e element to be appended to this list
         * @return {@code true} (as specified by {@link Collection#add})
         */
        public boolean add(E e) {
            linkLast(e);
            return true;
        }
     

    .如果要保证ArrayList线程安全,有几种方式?

      2.1 自己编写一个ArrayList集合类,根据业务一般来说,add/set/remove加锁;

      2.2 利用List<Objecct> list=Collections.synchronizedList(new ArrayList<>());    采用synchronized加锁

    复制代码
    public E get(int index) {
        synchronized (mutex) {return list.get(index);}
    }
    public E set(int index, E element) {
        synchronized (mutex) {return list.set(index, element);}
    }
    public void add(int index, E element) {
        synchronized (mutex) {list.add(index, element);}
    }
    public E remove(int index) {
        synchronized (mutex) {return list.remove(index);}
    }
    复制代码

        

      2.3 new CopyOnWriteArrayList<>().add(" ");    采用ReentrantLock加锁

    复制代码
    public boolean add(E e) {
          //加锁 final ReentrantLock lock = this.lock; lock.lock(); try {
            //获取原始集合 Object[] elements = getArray(); int len = elements.length;
            //复制一个新集合 Object[] newElements = Arrays.copyOf(elements, len + 1); newElements[len] = e;
            //替换原始集合为新集合 setArray(newElements); return true; } finally {
            //释放锁 lock.unlock(); } }
    复制代码

    3.了解CopyOnWriteArrayList底层?CopyOnWriteArrayList与Collections.synchronizedList有什么区别?

      3.1 CopyOnWriteArrayList底层实现:

        CopyOnWriteArrayList在执行修改操作的时候,会复制一份新的数组数据,代价昂贵,修改过来将原来的集合指向新的集合完成操作;

        3.1.1 add添加:在添加的时候是需要加锁的,否则多线程写的时候会Copy出N个副本出来;使用ReentrantLock保证多线程环境下的集合安全;

    复制代码
            public boolean add(E e) {
                        final ReentrantLock lock = this.lock;            //获取了一把锁
                        lock.lock();                        //加锁
                        try {
                            Object[] elements = getArray();        //获取当前数组数据,给elements
                            int len = elements.length;            //记录当前数组的长度
                            Object[] newElements = Arrays.copyOf(elements, len + 1);        //复制一个新的数组
                            newElements[len] = e;    //将数据填入到新数组当中
                            setArray(newElements);    //将当前array指针指向到新的数据
                            return true;
                        } finally {
                            lock.unlock();            //释放锁
                        }
                    }
    复制代码

        3.1.2 get读取:读的时候没有加锁,如果读的时候有多个线程正在向CopyOnWriteArrayList添加数据,读还是会读到旧的数据,因为写的时候不会锁住旧的CopyOnWriteArrayList;

      public E get(int index) {
            return get(getArray(), index);
        }

        CopyOnWriteArrayList应用场景:适用于读取操作远大于写操作场景(底层get读取没有加锁,直接获取)

      3.2 Collections.synchronizedList几乎底层方法都加上了synchronized的锁;

        场景:写操作的性能比CopyOnWriteArrayList要好,但是读取的性能不如CopyOnWriteArrayList;

    4.CopyOnWriteArrayList设计思想是怎样的,有什么缺点?

      设计思想:读写分离,最终一致;

      缺点:

        内存占用问题:由于写时复制,内存中就会出现两个对象占用空间,如果对象大则容易发生Young GC和Full GC;

        数据一致问题:CopyOnWriteArrayList容器只能保证数据最终一致性,不能保证数据实时一致性;

    5.ArrayList扩容机制是怎样的?

      1.7以及之前版本JDK,默认的大小为10;

      1.8版本集合大小如果创建时没有指定,则默认为0;若已经指定集合大小,则初始值为当前指定的大小;

        当第一次添加数据的时候,集合大小扩容为10,第二次以及后续每次按照int oldCapacity=elementData.length;      newCapacity = oldCapacity+(oldCapacity>>1),就是其容量扩展为原来容量的1.5倍;

      5.1 属性

        默认初始值的大小:

    private static final int DEFAULT_CAPACITY = 10;

        默认的空对象数组:

    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

        实际存储数据的数组:

    transient Object[] elementData;

      5.2 无参构造器

    public ArrayList() {
            this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
        }

      5.3 扩容机制 

        public boolean add(E e) {
            ensureCapacityInternal(size + 1);  // Increments modCount!!
            elementData[size++] = e;
            return true;
        }
    复制代码
        private void ensureCapacityInternal(int minCapacity) {
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
                minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
            }
    
            ensureExplicitCapacity(minCapacity);
        }
    //ensureCapacityInternal方法接受了size+1作为minCapacity,并且判断如果数组是空数组,那么10和minCapacity的较大值就作为新的minCapacity。
    复制代码
    复制代码
      private void ensureExplicitCapacity(int minCapacity) {
            modCount++;
    
            // overflow-conscious code
            if (minCapacity - elementData.length > 0)  //如果其元素个数大于其容量,则进行扩容;
                grow(minCapacity);
        }

    //判断传入的minCapacity和elementData.length的大小,如果elementData.length大于minCapacity,说明数组容量够用,就不需要进行扩容,
    //反之,则传入minCapacity到grow()方法中,进行扩容
    复制代码
    复制代码
        private void grow(int minCapacity) {
            // overflow-conscious code
            int oldCapacity = elementData.length;      //原来的容量
            int newCapacity = oldCapacity + (oldCapacity >> 1);  //新的容量,原来容量的1.5倍
            if (newCapacity - minCapacity < 0)
                newCapacity = minCapacity;
            if (newCapacity - MAX_ARRAY_SIZE > 0)    //如果大于ArrayList可以允许的最大容量,则设置为最大容量
                newCapacity = hugeCapacity(minCapacity);
            // minCapacity is usually close to size, so this is a win:
            elementData = Arrays.copyOf(elementData, newCapacity);    //最终进行扩容,生成一个1.5倍元素
        }
    复制代码

      进入grow()方法,会将newCapacity设置为旧容量的1.5倍,这也是ArrayList每次扩容都为原来的1.5倍的由来。然后进行判断,如果newCapacity小于minCapacity,那么就将minCapacity的值赋予newCapacity。

      然后在检查新容量是否超出了定义的容量,如果超出则调用hugeCapacity方法,比较minCapacity和MAX_ARRAY_SIZE的值;如果minCapacity大,那么新容量为Integer。MAX_VALUE,否则新容量为MAX_ARRAYSIZE。最后调用Arrays.cpoyOf传递elementData和新容量,返回新的elementData;

  • 相关阅读:
    SpringBoot微服务电商项目开发实战 --- 模块版本号统一管理及Redis集成实现
    Spring Boot微服务电商项目开发实战 --- 多环境部署配置、端口号统一配置及Dubbo提供者消费者实现
    Spring Boot微服务电商项目开发实战 --- 基础配置及搭建
    Java开发面试题汇总 -- 精选版(附答案)
    springmvc用model传值到jsp页面,el表达式引用接收不到传递过来的值
    八、自定义starter
    七、Spring Boot 启动配置原理
    六、SpringBoot与数据访问
    五、Docker
    四、Spring Boot Web开发
  • 原文地址:https://www.cnblogs.com/mayuan01/p/12496893.html
Copyright © 2020-2023  润新知