关于java:arrayList相关面试题

问:Vector和ArrayList、LinkedList区别?别离的应用场景?

  • 区别

    Vector: 底层是数组实现,线程平安的,操作的时候应用synchronized进行加锁.
    ArrayList:底层是数组实现,线程不平安,查问和批改十分快,然而减少和删除慢.
    LinkedList: 底层是双向链表,线程不平安,查问和批改速度慢,然而减少和删除速度快.
  • 应用场景

    减少和删除场景多则用LinkedList
    查问和批改多则用ArrayList
    Vector曾经很少用了

    如果须要保障线程平安,ArrayList应该怎么做,用有几种形式?

  • 罕用形式是两种,如果你说你本人写一个arrayList保障线程平安,那就是三种

    形式一:应用synchronized加锁
    Collections.synchronizedList(new ArrayList<>()); 
    形式二:应用ReentrantLock加锁
    CopyOnWriteArrayList<>()  

    ==死记硬背必定是不容易记住,咱们一起浏览一下源码吧==

  • 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用的是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();
    }
    }
    
    public E remove(int index) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
      Object[] elements = getArray();
      int len = elements.length;
      E oldValue = get(elements, index);
      int numMoved = len - index - 1;
      if (numMoved == 0)
        setArray(Arrays.copyOf(elements, len - 1));
      else {
        Object[] newElements = new Object[len - 1];
        System.arraycopy(elements, 0, newElements, 0, index);
        System.arraycopy(elements, index + 1, newElements, index,
                         numMoved);
        setArray(newElements);
      }
      return oldValue;
    } finally {
      lock.unlock();
    }
    }
    // 读操作没有锁
    public E get(int index) {
    return get(getArray(), index);
    }
    // 写操作有锁
    public E set(int index, E element) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
      Object[] elements = getArray();
      E oldValue = get(elements, index);
    
      if (oldValue != element) {
        int len = elements.length;
        Object[] newElements = Arrays.copyOf(elements, len);
        newElements[index] = element;
        setArray(newElements);
      } else {
        // Not quite a no-op; ensures volatile write semantics
        setArray(elements);
      }
      return oldValue;
    } finally {
      lock.unlock();
    }
    }

    CopyOnWriteArrayList和Collections.synchronizedList实现线程平安有什么区别?

  • 追溯上述的源码-CopyOnWriteArrayList:执行批改操作时,会拷贝一份新的数组进行操作「Arrays.copyOf()」,空间占用量大,浪费资源,代价低廉,源码外面用ReentrantLock可重入锁来保障不会有多个线程同时拷贝一份数组
  • 追溯上述的源码-Collections.synchronizedList:线程平安的起因是因为它简直在每个办法中都应用了synchronized同步锁

    CopyOnWriteArrayList和Collections.synchronizedList应用场景

  • CopyOnWriteArrayList场景:读高性能,实用读操作远远大于写操作的场景中应用(读的时候是不须要加锁的,间接获取,删除和减少是须要加锁的,读多写少)
  • synchronizedList:写操作性能比CopyOnWriteArrayList好,读操作性能并不如CopyOnWriteArrayList

    CopyOnWriteArrayList的设计思维是怎么的,有什么毛病?

  • 设计思维:读写拆散+最终统一
  • 毛病:通过源码能够晓得,写操作复制「Arrays.copyOf()」,内存里会同时驻扎两个对象的内存,旧的对象和新写入的对象,内存占用问题突出,如果对象大则容易产生Yong GC和Full GC

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理