单列集合List
作者:互联网
1、Vector和ArrayList以及LinkedList区别和联系、应用场景
线程安全:
Vector:如果创建Vector时没有指定容量,则默认容量为10,底层基于数组实现,线程是安全的,底层采用synchronized同步方法进行加锁
Vector与ArrayList一样,也是通过数组实现的,不同的是它支持线程的同步,即某一时刻只有一个线程能够写Vector,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,因此,访问它比访问ArrayList慢
ArrayList:底层基于数组,线程不安全,查询和修改效率高,但是增加和删除效率低
ArrayList是最常用的List实现类,内部是通过数组实现的,它允许对元素进行快速随机访问。数组的缺点是每个元素之间不能有间隔,当数组大小不满足时需要增加存储能力,就要讲已经有数组的数据复制到新的存储空间中。当从ArrayList的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高。因此,它适合随机查找和遍历,不适合插入和删除。
LinkedList:底层双向链表结构,线程不安全,查询和修改效率低,但是增加和删除效率高,另外,它还提供了List接口中没有定义的方法,专门用于操作表头和表尾元素,可以当作堆栈,队列和双向队列使用;
使用场景:
- Vector很少用
- 如果需要大量的添加和删除则可以选择LinkedList
- 如果需要大量的查询和修改则可以选择ArrayList
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