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;
    }

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章