zoukankan      html  css  js  c++  java
  • 集合面试题之单列集合List

    Vector和ArrayList以及LinkedList区别和联系,以及分别的应用场景
    线程安全:
    Vector:如果创建Vector时没有指定容量,则默认容量为10,底层基于数组实现,线程是安全的,底层采用synchronized同步方法进行加锁

    添加方法

     

    查询方法

     

     

     


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

    添加方法

     查询方法:

     

     

    LinkedList:底层双向链表结构,线程不安全,查询和修改效率低,但是增加和删除效率高

    添加方法

     查询方法

     

     


    使用场景:
    1.Vector很少用
    2.如果需要大量的添加和删除则可以选择LinkedList
    3.如果需要大量的查询和修改则可以选择ArrayList


    如果要保证ArraList线程安全,有几种方式?
    1 自己表写一个ArrayList集合类,根据业务一般来说,add/set/remove加锁
    2 利用List<Object> list = Collections.synchronizedList(new ArrayList<>()); //采用synchronized加锁
    3 new CopyOnWriteArrayList<>().add(""); //采用 ReentrantLock加锁

    了解CopyOnWriteArrayList底层?,CopyOnWriteArrayList与Collections.synchronizedList有什么区别
    1 CopyOnWriteArrayList底层实现:
    CopyOnWriteArrayList在执行修改操作的时候,会复制一份新的数组数据,代价昂贵,修改过后将原来的集合指向到新的集合完成操作
    使用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();			//释放锁
    	}
    }
    

      

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

    2 Collections.synchronizedList几乎底层方法都加上了synchronized的锁
    场景:写操作的性能比CopyOnWriteArrayList要好,但是读取的性能不如CopyOnWriteArrayList


    4 CopyOnWriteArrayList设计思想是怎么样的,有什么缺点?
    设计思想:读写分离,最终一致
    缺点:内存占用,由于写时复制,内存中就会出现两个对象占用空间,如果对象大则容易发生YongGC和FullGC


    5 说一下ArrayList扩容机制是怎么样的
    1.JDK1.8以前的,默认大小为10 ,jdk1.7的扩容与1.8类似,jdk1.6是1.5倍+1

     2.ArrayList集合大小如果创建时没有指定,则默认为0,若已经指定集合大小,则初始值为指定的
     当第一次添加数据的时候,集合大小扩容为10,第二次及其后续每次按照int oldCapacity = elementData.length; newCapacity = oldCapacity+(oldCapacity>>1),也就是扩容1.5倍

    走ensureCapacityInternal()方法,把创建ArrayList时指定的size传过去,如果没有指定则size为0

    默认的初始容量为10

    判断如果 当前ArrayList是空的就进入判断,将minCapacity赋值,Math.max()判断指定的和初始的那个大,就把它赋值给minCapacity

    然后走ensureExplicitCapacity()方法

     

    判断传进来的数是否大于当前ArrayList的长度,如果大于就进行扩容,走grow()方法并将minCapacity传过去

     

    将集合长度赋值给oldCapacity(旧容量)

    newCapacity(新容量)的值等于oldCapacity的值+(oldCapacity的值*0.5)

    判断newCapacity-minCapacity是否小于零,如果小于就将minCapacity的值赋值给newCapacity

    判断newCapacity-MAX_ARRAY_SIZE(集合最大容量)是否大于零 ,如果大于就将hugeCapacity()的返回值赋值给newCapacity

    将原集合复制并重新给容量赋值,在将赋值好的新集合赋值给原集合

     

    将minCapacity做判断如果小于零,就报内存不足错误

    判断minCapacity是否大于集合最大容量,如果大于就将Integer类型的最大值返回过去,如果小于集合最大容量就将集合最大容量返回过去

     

  • 相关阅读:
    Java读取resource文件/路径的几种方式
    log4j:WARN Please initialize the log4j system properly解决办法
    SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
    log4j.properties配置详解与实例-全部测试通过[转]
    testNG中dataprovider使用的两种方式
    远程仓库获取最新代码合并到本地分支
    git 冲突解决办法
    【转】JsonPath教程
    selenium及webdriver的原理【转】
    [转]Redis 数据结构简介
  • 原文地址:https://www.cnblogs.com/wishsaber/p/12490742.html
Copyright © 2011-2022 走看看