zoukankan      html  css  js  c++  java
  • Java中ArrayList相关的5道面试题

    本文参考了 关于ArrayList的5道面试题  

    1、ArrayList的大小是如何自动增加的?

    这个问题我想曾经debug过并且查看过arraylist源码的人都有印象,它的过程是:当试图在一个arraylist中增加一个对象时,Java会去检查arraylist,确保已存在的数组中有足够的容量(默认是10),如果没有足够的容量,那么就会新建一个长度更长(是原来数组长度的1.5倍)的数组,旧的数组就会使用Arrays.copyOf()方法被复制到新的数组中。

    来看源代码:

     /**
         * Appends the specified element to the end of this list.
         *
         * @param e element to be appended to this list
         * @return <tt>true</tt> (as specified by {@link Collection#add})
         */
        public boolean add(E e) {
        ensureCapacity(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
        }
    
    /**
         * Increases the capacity of this <tt>ArrayList</tt> instance, if
         * necessary, to ensure that it can hold at least the number of elements
         * specified by the minimum capacity argument.
         *
         * @param   minCapacity   the desired minimum capacity
         */
        public void ensureCapacity(int minCapacity) {
        modCount++;
        int oldCapacity = elementData.length;
        if (minCapacity > oldCapacity) {
            Object oldData[] = elementData;
            int newCapacity = (oldCapacity * 3)/2 + 1;
                if (newCapacity < minCapacity)
            newCapacity = minCapacity;
                // minCapacity is usually close to size, so this is a win:
                elementData = Arrays.copyOf(elementData, newCapacity);
        }
        }

    2、什么情况下使用ArrayList,什么情况下使用LinkedList?

    首先来看LinkedList是什么:

    private transient Entry<E> header = new Entry<E>(null, null, null);
        private transient int size = 0;
    
        /**
         * Constructs an empty list.
         */
        public LinkedList() {
            header.next = header.previous = header;
        }

    没错,它就是一个链表,每一个节点都是Entry,而ArrayList是一个数组,初始化的大小是10:

      public ArrayList(int initialCapacity) {
        super();
            if (initialCapacity < 0)
                throw new IllegalArgumentException("Illegal Capacity: "+
                                                   initialCapacity);
        this.elementData = new Object[initialCapacity];
        }
    
        /**
         * Constructs an empty list with an initial capacity of ten.
         */
        public ArrayList() {
        this(10);
        }

    好,搞清楚这个之后,我们可以知道:①、对于数组的访问,即使是最糟糕的情况下,时间复杂度也是O(1),而对于链表来说,最糟糕的情况下,时间复杂度是O(N)。②、对于数组来说,当旧容量无法容下新增对象时,会将就的数组复制到一个新的数组里面,这里需要消耗时间,也就是说增加一个对象可能会耗时比较久,而对于链表来说,新增一个对象只是初始化一个entry,然后将其插入到链表表尾,耗时并不会太长。

    看LinkedList的add方法:

     public boolean add(E e) {
        addBefore(e, header);
            return true;
        }
    
     private Entry<E> addBefore(E e, Entry<E> entry) {
        Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
        newEntry.previous.next = newEntry;
        newEntry.next.previous = newEntry;
        size++;
        modCount++;
        return newEntry;
        }

    所以,引用原文的话:多数情况下,当你遇到访问元素比插入或者是删除元素更加频繁的时候,你应该使用ArrayList。另外一方面,当你在某个特别的索引中,插入或者是删除元素更加频繁,或者你压根就不需要访问元素的时候,你会选择LinkedList。

    3、当传递一个ArrayList到某个方法,或者某个方法返回ArrayList,什么时候要考虑安全隐患?如何修复这个安全违规问题?

    当array被当做参数传递到某个方法中,如果array在没有被复制的情况下直接被分配给了成员变量,那么就可能发生这种情况,即当原始的数组被调用的方法改变的时候,传递到这个方法中的数组也会改变。

    来看例子:

    public class Array {
        String []myArray = {};
        public void SetMyArray(String []myArray){
            this.myArray = myArray;//这里有安全隐患
        }
        
        public String[] getMyArray(){
            return this.myArray;//这里有安全隐患
        }
        
        public static void main(String []args){
            Array a = new Array();
            String b[] = {"i "," am ","from ","china"};
            a.SetMyArray(b);
            System.out.println(a.getMyArray()[0]);
            
            b[0]="you";
            System.out.println(a.getMyArray()[0]);
        }
    }

    输出结果:

    i 
    you

    很明显,我们只修改了b[0],但是却影响到a[0](假设我们的目的是修改b并不会影响a),既然出现这个问题,那么我们对set方法做一次改进:

    public void SetMyArray(String []myArray){//修改后的set方法
            if(myArray == null){
                this.myArray = new String[0];
            }else{
                this.myArray = Arrays.copyOf(myArray, myArray.length);
            }
        }

    此时的运行结果:

    i 
    i 

    所以此时的b和a都是分开的,互不影响的。

    4、如何复制某个ArrayList到另一个Arraylist中去?用原文的话来说:

    1. 使用clone()方法,比如ArrayList newArray = oldArray.clone();
    2. 使用ArrayList构造方法,比如:ArrayList myObject = new ArrayList(myTempObject);
    3. 使用Collection的copy方法。

      注意1和2是浅拷贝(shallow copy)。

    关于浅拷贝,深拷贝,参考这篇文章。

    5、在索引中ArrayList的增加或者删除某个对象的运行过程?效率很低吗?解释一下为什么?

    在ArrayList中增加或者是删除元素,要调用System.arraycopy这种效率很低的操作,如果遇到了需要频繁插入或者是删除的时候,你可以选择其他的Java集合,比如LinkedList。看一下下面的代码:

    在某个索引处增加一个元素:

     1  public void add(int index, E element) {
     2     if (index > size || index < 0)
     3         throw new IndexOutOfBoundsException(
     4         "Index: "+index+", Size: "+size);
     5 
     6     ensureCapacity(size+1);  // Increments modCount!!
     7     System.arraycopy(elementData, index, elementData, index + 1,
     8              size - index);
     9     elementData[index] = element;
    10     size++;
    11     }

    在某个索引处删除某个元素:

     1  public E remove(int index) {
     2     RangeCheck(index);
     3 
     4     modCount++;
     5     E oldValue = (E) elementData[index];
     6 
     7     int numMoved = size - index - 1;
     8     if (numMoved > 0)
     9         System.arraycopy(elementData, index+1, elementData, index,
    10                  numMoved);
    11     elementData[--size] = null; // Let gc do its work
    12 
    13     return oldValue;
    14     }
  • 相关阅读:
    部分和问题
    区间k大数训练
    蓝桥杯基础练习-数列排序
    蓝桥杯基础练习-特殊的回文数
    蓝桥杯基础练习-回文数
    蓝桥杯基础练习-特殊的数字
    蓝桥杯基础练习-杨辉三角
    蓝桥杯基础练习-查找整数
    蓝桥杯题库基础训练-数列特征
    三道半平面交测模板题 Poj1474 Poj 3335 Poj 3130
  • 原文地址:https://www.cnblogs.com/iceseal/p/3601719.html
Copyright © 2011-2022 走看看