java面试基础三:集合框架List面试题

1. java面试基础三:集合框架List面试题

1.1. java集合框架里面List常见面试题
  1. 说一下Vector和ArrayList,LinkedList的联系和区别?分别使用场景

从两点回答:

1、线程安全

​ ArrayList:底层是数组实现,线程不安全,好处就是查询和修改非常快,但是增加和删除慢。

​ LInkedList:底层是双向链表,查询和修改速度慢,但是增加和删除速度快。

​ Vector:底层也是数组实现,线程安全的使用了Synchronized加锁。

2、使用场景

​ Vector: 已经很少用了,

​ LinkedList:增加和删除多用LinkedList。

​ ArrayList:查询和修改多,用ArrayList.

  1. 如果要保证线程安全ArrayList应该如何做?

方案一:自己写个包装类,根据业务一般是add/update/remove加锁。

方案二:用Collections.synchronizedList(new ArrayList<>());进行加锁。

方案三:CopyOnWriteArrayList<>() 使用 ReentrantLock加锁,思想是:先获取原先的数组,获取后通过ReentrantLock进行加锁,然后把原来的数组copy到一个新的数组中,再将新增的数组元素添加进去,然后再去掉锁,然后再将原来数组的地址指向新数组,再返回回去。

  1. CopyOnWriteArrayList和SynchronizedList实现线程安全有什么区别?使用场景是怎样的?

CopyOnWriteArrayList:执行修改操作,会拷贝一份新的数据,(add/set/remove).代价昂贵,修改好后,会将原来的集合指向新的集合来完成操作,使用ReentrantLock来保证不会有多个线程同时修改。

使用场景:适合读操作远远大于写操作的场景,读操作是不需要加锁的,直接获取,但是删除和增加需要加锁,读多写少。

Collections.synchronizedList:线程安全的原因就是几乎每个方法都是使用synchronized来同步加锁的,

使用场景:写操作性能比CopyOnWriteArrayList好,但是读操作性能没有CopyOnWriteArrayList好。

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

设计思想:读写分离+最终一致,

缺点:占内存,由于写时复制,内存里面会存在两个对象占用内存,如果对象大则容易发生YongGC和FullGC。

1.2. LIst的扩容机制
  1. 说一下ArrayList的扩容机制

1、这个分版本,jdk1.7之前ArrayList的默认大小是10,jdk1.7之后是0

2、未指定集合容量,默认是0,如果已经指定大小则集合大小为指定大小。

3、当集合new出来时,容量为0,当第一次添加元素时,集合扩容为10

4、当集合元素大于10时,扩容容量的大小=原始大小+原始大小/2

  1. 手写一个简单版的ArrayList(包含构造函数(有参和无参,add(obj) 、扩容机制))
import java.io.Serializable;

public class MyArrayList implements Serializable{

    //使用这个字段,来判断当前集合类是否被并发修改,即迭代器并发修改的fail-fast机制。
    private transient int modCount=0;
    //第一次扩容容量
    private static final int DEFAULT_CAPACITY=10;

    //用于初始化空的list
    private static final Object[] EMPTY_ELEMENT_DATA= {};

    //实际存储的元素
    transient Object[] elementData;//transient避免被序列化

    //实际list集合大小,从0开始
    private int size;


    //构造方法
    public MyArrayList() {

        this.elementData= EMPTY_ELEMENT_DATA;

    }

    public MyArrayList(int initialCapcity) {
        if(initialCapcity>0) {
            this.elementData=new Object[initialCapcity];

        }else if(initialCapcity==0) {
            this.elementData=EMPTY_ELEMENT_DATA;
        }else {
            throw new IllegalAccessError("参数异常");
        }
    }

    public boolean add(Object e) {

        //判断容量
        ensureCapacityInternal(size+1);
        //使用下标复制,尾部插入,这个是下标,还有一个容量。
        elementData[size++]=e;
        return true;
    }

    /**
	 * 计算容量,确保容量
	 * @Title: ensureCapacityInternal  
	 * @Description:
	 * @param minCapacity
	 * @author:kaifan·Zhang
	 * @date 2020年3月11日
	 * @version 1.0
	 */
    private void ensureCapacityInternal(int minCapacity) {
        //用于并发判断
        modCount++;
        //如果是初次扩容,则使用默认容量。
        if(elementData==EMPTY_ELEMENT_DATA) {
            minCapacity=Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        //是否需要扩容,需求的最少容量,大于现在数组的长度,则要扩容
        if(minCapacity-elementData.length>0) {
            int oldCapacity=elementData.length;
            int newCapacity=oldCapacity+(oldCapacity>>1);
            //如果新容量<最小容量,将最小
            if(newCapacity-minCapacity<0) {
                newCapacity =minCapacity;
            }
            //创建新数组
            Object[] objects=new Object[newCapacity];

            //将旧的数据cop到新的数组里面。
            System.arraycopy(elementData, 0, objects, 0, elementData.length);
            //修改引用
            elementData=objects;
        }
    }

    /**
	 * 通过下标获取对象。
	 * @Title: get  
	 * @Description:
	 * @param index
	 * @return
	 * @author:kaifan·Zhang
	 * @date 2020年3月11日
	 * @version 1.0
	 */

    public Object get(int index) {
        rangeCheck(index);
        return elementData[index];

    }
    private void rangeCheck(int index) {
        if(index>size||size<0) {
            throw new IndexOutOfBoundsException("数据越界");
        }
    }

    /**
	 * 判断对象所在的位置
	 * @Title: indexOf  
	 * @Description:
	 * @param o
	 * @return
	 * @author:kaifan·Zhang
	 * @date 2020年3月11日
	 * @version 1.0
	 */
    public int indexOf(Object o) {
        if(o==null) {
            for(int i=0;i<size;i++) {
                if(elementData[i]==null) {
                    return i;
                }
            }
        }else {
            for(int i=0;i<size;i++) {
                if(o.equals(elementData[i])) {
                    return i;
                }
            }

        }
        return -1; 
    }


    /**
	 * 修改下标对应的值
	 * @Title: set  
	 * @Description:
	 * @param index
	 * @param obj
	 * @return
	 * @author:kaifan·Zhang
	 * @date 2020年3月11日
	 * @version 1.0
	 */
    public Object set(int index,Object obj) {
        rangeCheck(index);
        Object oldValue=elementData[index];
        elementData[index]=obj;
        return oldValue;

    }
    /**
	 * 根据索引删除下标对应的元素
	 * @Title: remove  
	 * @Description:
	 * @param index
	 * @return
	 * @author:kaifan·Zhang
	 * @date 2020年3月11日
	 * @version 1.0
	 */
    public Object remove(int index) {
        rangeCheck(index);
        //用于并发判断,当迭代器操作时,会有很多操作,
        //当操作到最后会与刚进入方法时的modCount进行比较,如果不同,就会抛出异常
        modCount++;
        Object oldValue =elementData[index];
        //计算要删除的位置后面还有几个元素
        int numMoved=size-index-1;
        if(numMoved>0) {
            System.arraycopy(elementData, index+1, elementData, index, numMoved);
        }
        //将多出的位置设置为null,没有引用对象。垃圾收集器就可以回收。如果不置空,将会保存一个引用
        //可能会造成内存泄露。
        elementData[--size]=null;
        return oldValue; 
    }
    /**
	 * 获取数组实际大小
	 * @Title: size  
	 * @Description:
	 * @return
	 * @author:kaifan·Zhang
	 * @date 2020年3月11日
	 * @version 1.0
	 */
    public int size() {
        return this.size;
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值