美文网首页
(二):集合框架List

(二):集合框架List

作者: JBryan | 来源:发表于2020-03-10 11:49 被阅读0次

1.对比 Vector、ArrayList、LinkedList 有何区别?

Vector 是 Java 早期提供的线程安全的动态数组,如果不需要线程安全,并不建议选择,毕竟同步是有额外开销的。Vector 内部是使用对象数组来保存数据,可以根据需要自动的增加容量,当数组已满时,会创建新的数组,并拷贝原有数组数据。
ArrayList 是应用更加广泛的动态数组实现,它本身不是线程安全的,所以性能要好很多。与 Vector 近似,ArrayList 也是可以根据需要调整容量,不过两者的调整逻辑有所区别,Vector 在扩容时会提高 1 倍,而 ArrayList 则是增加 50%。
LinkedList 顾名思义是 Java 提供的双向链表,所以它不需要像上面两种那样调整容量,它也不是线程安全的。
ArrayList:底层是数组实现,线程不安全,查询和修改非常快,但是增加和删除慢。
LinkedList: 底层是双向链表,线程不安全,查询和修改速度慢,但是增加和删除速度快。
Vector: 底层是数组实现,线程安全的,操作的时候使用synchronized进行加锁。

2.如果需要保证线程安全,ArrayList应该怎么做?

//方式一:Collections.synchronizedList(new ArrayList<>()); 使用synchronized加锁。
List<Object> objects = Collections.synchronizedList(new ArrayList<>());
//方式二:CopyOnWriteArrayList<>() 使⽤ReentrantLock加锁。
List<String> list = new CopyOnWriteArrayList(new ArrayList<String>());

3.CopyOnWriteArrayList和Collections.synchronizedList实现线程安全有什么区别, 使用场景是怎样的?

CopyOnWriteArrayList:执行修改操作时,会拷贝⼀份新的数组进行操作(add、set、remove等),代价十分昂贵,在执行完修改后将原来集合指向新的集合来完成修改操作,源码里面用ReentrantLock可重入锁来保证不会有多个线程同时拷贝⼀份数组。
场景:读高性能,适用读操作远远大于写操作的场景中使用(读的时候是不需要加锁的,直接获取,删除和增加是需要加锁的, 读多写少)
CopyOnWriteArrayList的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();
        }
    }

Collections.synchronizedList:线程安全的原因是因为它几乎在每个方法中都使用了synchronized同步锁。
场景:写操作性能比CopyOnWriteArrayList好,读操作性能并不如CopyOnWriteArrayList。
Collections.synchronizedList部分源码

        public boolean add(E e) {
            synchronized (mutex) {return c.add(e);}
        }
        public boolean remove(Object o) {
            synchronized (mutex) {return c.remove(o);}
        }

        public boolean containsAll(Collection<?> coll) {
            synchronized (mutex) {return c.containsAll(coll);}
        }
        public boolean addAll(Collection<? extends E> coll) {
            synchronized (mutex) {return c.addAll(coll);}
        }

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

答案:设计思想:读写分离+最终⼀致
缺点:内存占用问题,写时复制机制,内存里会同时驻扎两个对象的内存,旧的对象和新写入的对象,
如果对象⼤则容易发生Yong GC和Full GC。

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

JDK1.7之前ArrayList默认大小是10,JDk1.7之后是0

5.1.未指定集合容量,默认是0,若已经指定大小则集合大小为指定参数;

ArrayList源码四个属性:

/**
     * 默认初始容量
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 空数组.
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * 默认容量的空数组
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
     
    /**
     * 存储数据的数组
     */
     transient Object[] elementData;

构造方法:

//指定容量的构造方法,初始化长度为指定容量
public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

    /**
     * 未指定容量构造方法,初始化长度为0.
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
5.2.当集合第一次添加元素的时候,集合大小扩容为10

ArrayList的add()源码解析:

/**
*添加一个指定元素到数组尾部。
*1.调用ensureCapacityInternal(size + 1)方法保证内部容量。
*2.将指定的数据添加到数组尾部。
*/
public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
/**
*保证内部的容量,传入参数为add()方法里面传的size+1。
*1.调用calculateCapacity(elementData, minCapacity)方法,计算容量
*2.调用ensureExplicitCapacity()方法。初次添加,参数为10;
*/
private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

/**
*计算容量,传入参数为存储数据的数组,当前数组长度+1。
*初次添加元素,返回默认容量10
*/
private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

/**
*计算确定的容量:
*初次添加,参数为10,10-数组长度(0)>0,进入grow(10)方法。
*/
private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

/**
*初次添加元素,参数为10。
*/
private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        //新的容量=oldCapacity +oldCapacity /2
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //初次添加,newCapacity=0,minCapacity =10。
        if (newCapacity - minCapacity < 0)
            //将新的容量,赋值为10.
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // 拷贝数组
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
5.3.ArrayList的元素个数大于其容量,扩容的大小= 原始大小+原始大小/2。

假设当前数组长度为10,需要添加第11个元素,源码分析如下:

/**
*第一次扩容,此时数组长度为10,调用add()方法,添加第11个元素。
*/
public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
/**
*保证内部的容量,传入参数为add()方法里面传的size+1。
*1.调用calculateCapacity(elementData, minCapacity)方法,计算容量
*2.调用ensureExplicitCapacity()方法。参数为11;
*/
private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

/**
*计算容量,传入参数为存储数据的数组,当前数组长度+1。
*返回minCapacity,minCapacity=11
*/
private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

/**
*计算确定的容量:
*minCapacity = 11,
*/
private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // 11-10>0
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

/**
*minCapacity=11,
*/
private void grow(int minCapacity) {
        // oldCapacity  = 10
        int oldCapacity = elementData.length;
        //新的容量=oldCapacity +oldCapacity /2=15
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //newCapacity=15,minCapacity =11。
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // 拷贝数组
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

相关文章

网友评论

      本文标题:(二):集合框架List

      本文链接:https://www.haomeiwen.com/subject/vibmdhtx.html