其他分享
首页 > 其他分享> > 单列集合List

单列集合List

作者:互联网

1、Vector和ArrayList以及LinkedList区别和联系、应用场景

线程安全:

  Vector:如果创建Vector时没有指定容量,则默认容量为10,底层基于数组实现,线程是安全的,底层采用synchronized同步方法进行加锁

    Vector与ArrayList一样,也是通过数组实现的,不同的是它支持线程的同步,即某一时刻只有一个线程能够写Vector,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,因此,访问它比访问ArrayList慢

  ArrayList:底层基于数组,线程不安全,查询和修改效率高,但是增加和删除效率低

    ArrayList是最常用的List实现类,内部是通过数组实现的,它允许对元素进行快速随机访问。数组的缺点是每个元素之间不能有间隔,当数组大小不满足时需要增加存储能力,就要讲已经有数组的数据复制到新的存储空间中。当从ArrayList的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高。因此,它适合随机查找和遍历,不适合插入和删除。

  LinkedList:底层双向链表结构,线程不安全,查询和修改效率低,但是增加和删除效率高,另外,它还提供了List接口中没有定义的方法,专门用于操作表头和表尾元素,可以当作堆栈,队列和双向队列使用;

使用场景:

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

2、如果要保证ArraList线程安全,有几种方式?

2.1 自己表写一个ArrayList集合类,根据业务一般来说,add/set/remove加锁
2.2 利用List<Object> 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加锁

    /**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return {@code true} (as specified by {@link Collection#add})
     */
    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在执行修改操作的时候,会复制一份新的数组数据,代价昂贵,修改过后将原来的集合指向到新的集合完成操作使用ReentrantLock保证多线程环境下的集合安全

add添加:

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

    /**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return {@code true} (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        //获取了一把锁
        final ReentrantLock lock = this.lock;
        //加锁
        lock.lock();
        try {
            //获取当前数组数据,给elements
            Object[] elements = getArray();
            //记录当前数组的长度
            int len = elements.length;
            //复制一个新的数组
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            /将数据填入到新数组当中
            newElements[len] = e;
            //将当前array指针指向到新的数据
            setArray(newElements);
            return true;
        } finally {
            //释放锁
            lock.unlock();
        }
    }    

get读取:

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

    /**
     * {@inheritDoc}
     *
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E get(int index) {
        return get(getArray(), index);
    }

CopyOnWriteArrayList应用场景:

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

3.2 Collections.synchronizedList几乎底层方法都加上了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);}
        }

应用场景:

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

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

设计思想:

  读写分离,最终一致

缺点:

  内存占用,由于写时复制,内存中就会出现两个对象占用空间,如果对象大则容易发生YongGC和FullGC

5、 ArrayList扩容机制是怎么样的

JDK1.7 以及之前版本JDK,首先从默认大小来讲,默认为10
JDK1.8 ArrayList集合大小如果创建时没有指定,则默认为0,若已经指定集合大小,则初始值为指
  当第一次添加数据的时候,集合大小扩容为10,第二次及其后续每次按照int oldCapacity = elementData.length; newCapacity = oldCapacity+(oldCapacity>>1)

5.1 属性

默认初始值的大小:

    /**
     * Default initial capacity.
     */
    private static final int DEFAULT_CAPACITY = 10;

默认的空对象数组:

    /**
     * Shared empty array instance used for default sized empty instances. We
     * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
     * first element is added.
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

实际存储数据的数组:

    /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
     */
    transient Object[] elementData; // non-private to simplify nested class access

5.2 无参构造器

    /**
     * Constructs an empty list with an initial capacity of ten.
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

5.3 扩容机制(源码)

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

 

    //ensureCapacityInternal方法接受了size+1作为minCapacity,并且判断如果数组是空数组,那么10和minCapacity的较大值就作为新的minCapacity。
    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

 

    //判断传入的minCapacity和elementData.length的大小,如果elementData.length大于minCapacity,说明数组容量够用,就不需要进行扩容,
    //反之,则传入minCapacity到grow()方法中,进行扩容 
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        //如果其元素个数大于其容量,则进行扩容;
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

 

    /**
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     *
     * @param minCapacity the desired minimum capacity
     */
    private void grow(int minCapacity) {
        // overflow-conscious code
        //原来的容量
        int oldCapacity = elementData.length;
        //新的容量,原来容量的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        //如果大于ArrayList可以允许的最大容量,则设置为最大容量
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        //最终进行扩容,生成一个1.5倍元素
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

  进入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;

 

标签:index,int,List,单列,list,element,add,minCapacity,集合
来源: https://www.cnblogs.com/Zzzzn/p/12489262.html