ArrayList

=======================================================================
 
Java如何实现链表?
 
set和list的区别,一些个实现类,继承关系等等?
=======================================================================
【ArrayList与LinkedList的区别】
ArrayList是可改变大小的数组,而LinkedList是双向链接串列(doubly LinkedList)
ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
1) 因为Array是基于索引(index)的数据结构,它使用索引在数组中搜索和读取数据是很快的。Array获取数据的时间复杂度是O(1),但是要删除数据却是开销很大的,因为这需要重排数组中的所有数据。
2) 相对于ArrayList,LinkedList插入是更快的。因为LinkedList不像ArrayList一样,不需要改变数组的大小,也不需要在数组装满的时候要将所有的数据重新装入一个新的数组,这是ArrayList最坏的一种情况,时间复杂度是O(n),而LinkedList中插入或删除的时间复杂度仅为O(1)。
        ArrayList在插入数据时还需要更新索引(除了插入数组的尾部)。
3) 类似于插入数据,删除数据时,LinkedList也优于ArrayList。
4) LinkedList需要更多的内存,因为ArrayList的每个索引的位置是实际的数据,而LinkedList中的每个节点中存储的是实际的数据和前后节点的位置。
 
什么情况下你会使用ArrayList?什么时候你会选择LinkedList?
        多数情况下,当你遇到访问元素比插入或者是删除元素更加频繁的时候,你应该使用ArrayList。另外一方面,当你在某个特别的索引中,插入或者是删除元素更加频繁,或者你压根就不需要访问元素的时候,你会选择LinkedList。这里的主要原因是,在ArrayList中访问元素的最糟糕的时间复杂度是”1″,而在LinkedList中可能就是”n”了。在ArrayList中增加或者删除某个元素,通常会调用System.arraycopy方法,这是一种极为消耗资源的操作,因此,在频繁的插入或者是删除元素的情况下,LinkedList的性能会更加好一点。
==============================================================================================================
1、ArrayList的大小是如何自动增加的?你能分享一下你的代码吗?arrayList底层如何扩展的?
        这是最有技巧性的的一个问题,大多数人都无法回答。事实上,当有人试图在arraylist中增加一个对象的时候,Java会去检查arraylist,以确保已存在的数组中有足够的容量来存储这个新的对象。如果没有足够容量的话,那么就会新建一个长度更长的数组,旧的数组就会使用Arrays.copyOf方法被复制到新的数组中去,现有的数组引用指向了新的数组。

public boolean add(E e){
    ensureCapacity(size+1); //Increment modCount!!
    elementData[size++] = e;
    return true;
}

public void ensureCapacity(int minCapacity) {
    modCount++;【已从结构上修改此列表的次数:protected transient int modCount】
    int oldCapacity = elementData.length;
    if (minCapacity > oldCapacity) {
        Object oldData[] = elementData;
        int newCapacity = (oldCapacity * 3)/2 + 1;
        if (newCapacity < minCapacity)
            newCapacity = minCapacity;

        elementData = Arrays.copyOf(elementData, newCapacity);
    }
}

3、当传递ArrayList到某个方法中,或者某个方法返回ArrayList,什么时候要考虑安全隐患?如何修复安全违规这个问题呢?
当array被当做参数传递到某个方法中,如果array在没有被复制的情况下直接被分配给了成员变量,那么就可能发生这种情况,即当原始的数组被调用的方法改变的时候,传递到这个方法中的数组也会改变。下面的这段代码展示的就是安全违规以及如何修复这个问题。

ArrayList被直接赋给成员变量——安全隐患:
public void setMyArray(String[] myArray){
    this.myArray = myArray;
}


修复这个安全隐患:
public void setMyArray(String[] newMyArray){
    if(newMyArray == null){
        this.myArray = myArray;
    }else{
        this.myArray = Arrays.copyOf(newMyArray,newMyArray);
    }
    
}

4、如何复制某个ArrayList到另一个ArrayList中去?写出你的代码?
下面就是把某个ArrayList复制到另一个ArrayList中去的几种技术:
使用clone()方法,比如ArrayList newArray = oldArray.clone();
使用ArrayList构造方法,比如:ArrayList myObject = new ArrayList(myTempObject);

使用Collection的copy方法。
注意1和2是浅拷贝(shallow copy)。

5、在索引中ArrayList的增加或者删除某个对象的运行过程?效率很低吗?解释一下为什么?
在ArrayList中增加或者是删除元素,要调用System.arraycopy这种效率很低的操作,如果遇到了需要频繁插入或者是删除的时候,你可以选择其他的Java集合,比如LinkedList。


 
 
 
=======================================================================
  1. public void ensureCapacity(int minCapacity) {//注意是public类型,也就是说可以我门自己来重新分配空间  
  2. modCount++;   
  3. int oldCapacity = elementData.length;//老的容量   
  4. if (minCapacity > oldCapacity) {   
  5.     Object oldData[] = elementData;//把当前数组临时保存起来   
  6.     int newCapacity = (oldCapacity * 3)/2 + 1;//加1是为了保证oldCapacity为1或者0的情况下   
  7.         if (newCapacity < minCapacity)//是按1.5被来增加容量的   
  8.     newCapacity = minCapacity;   
  9.            // minCapacity is usually close to size, so this is a win:   
  10.            elementData = Arrays.copyOf(elementData, newCapacity);   
  11. }   
  12.    }  
=======================================================================
通过下标得到一个元素
  1.  public E get(int index) {  
  2. RangeCheck(index);//先检查是否越界  
  3.   
  4. return (E) elementData[index];//返回的是数组中的下标    
  5. }
=======================================================================
添加一个新的元素到末尾,前面说道新增方法都要先调用ensureCapacity方法:
 
  1. public boolean add(E e) {  
  2. ensureCapacity(size + 1); //大小加一 // Increments modCount!!  
  3. elementData[size++] = e;//size默认是0所以是从0开始赋值  
  4. return true;  
  5.    } 

=======================================================================

API文档中的说明是:将指定的元素插入此列表中的指定位置。向右移动当前位于该位置的元素(如果有)以及所有后续元素(将其索引加 1)。通俗的说法是在指定位置插入元素,指定元素和后面的元素后移

这个方法和set(int index, E element) 不一样,set只是把元素赋值给指定的下标同时返回下标的原先值.

add(int index, E element)的判断越界是通过元素的大小来判断的

  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. //源数组中位置在 srcPos 到 srcPos+length-1 之间的组件被分别复制到  
  10. //目标数组中的 destPos 到 destPos+length-1 位置  
  11. elementData[index] = element;  
  12. size++;//元素加一  
  13.    } 


删除指定位置的元素,返回被删除的元素,由于ArrayList采用一个对象数组存储元素,所以在删除一个元素时需要把后面的元素前移。删除一个元素时只是把该元素在elementData数组中的引用置为null,具体的对象的销毁由垃圾收集器负责

Java代码  收藏代码
  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.    }  

 

内部删除方法,跳过越界检查,不返回删除元素的值:ArrayList内部调用的删除方法

Java代码  收藏代码
  1. /* 
  2.   * Private remove method that skips bounds checking and does not 
  3.   * return the value removed. 
  4.   */  
  5.  private void fastRemove(int index) {  
  6.      modCount++;  
  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.  }  

 

 

删除指定元素:

Java代码  收藏代码
  1.    public boolean remove(Object o) {  
  2. if (o == null) {  
  3.            for (int index = 0; index < size; index++)  
  4.     if (elementData[index] == null) {  
  5.         fastRemove(index);  
  6.         return true;  
  7.     }  
  8.  
  9. else {  
  10.     for (int index = 0; index < size; index++)  
  11.     if (o.equals(elementData[index])) {  
  12.         fastRemove(index);  
  13.         return true;  
  14.     }  
  15.        }  
  16. return false;  
  17.    }  

 



清空列表:

Java代码  收藏代码
  1.    public void clear() {  
  2. modCount++;  
  3.   
  4. // Let gc do its work  
  5. for (int i = 0; i < size; i++)  
  6.     elementData[i] = null;  
  7.   
  8. size = 0;//设定元素大小为0  
  9.    }  

 

 

添加集合c中的元素到ArrayList的末尾,添加成功返回true,如果集合c为空,返回false。

Java代码  收藏代码
  1.   public boolean addAll(Collection<? extends E> c) {  
  2. Object[] a = c.toArray();  
  3.        int numNew = a.length;  
  4. ensureCapacity(size + numNew);  // Increments modCount  
  5.        System.arraycopy(a, 0, elementData, size, numNew);//添加到列表的末尾  
  6.        size += numNew;  
  7. return numNew != 0;  
  8.    }  

 

 

在指定位置插入集合中的所有元素,和上面一个方法基本差不多,指定位置元素和以后的都要后移

Java代码  收藏代码
  1.    public boolean addAll(int index, Collection<? extends E> c) {  
  2. if (index > size || index < 0)//判断越界   
  3.     throw new IndexOutOfBoundsException(  
  4.     "Index: " + index + ", Size: " + size);  
  5.   
  6. Object[] a = c.toArray();  
  7. int numNew = a.length;  
  8. ensureCapacity(size + numNew);  // Increments modCount  
  9.   
  10. int numMoved = size - index;  
  11. if (numMoved > 0)//两种情况  
  12.     System.arraycopy(elementData, index, elementData, index + numNew,  
  13.              numMoved);  
  14.   
  15.        System.arraycopy(a, 0, elementData, index, numNew);//这里是等于0的情况也就是说直接在数组后面加  
  16. size += numNew;  
  17. return numNew != 0;  
  18.    }  

 

 

删除指定范围的元素

    1. protected void removeRange(int fromIndex, int toIndex) {   
    2. modCount++;   
    3. int numMoved = size - toIndex;   
    4.        System.arraycopy(elementData, toIndex, elementData, fromIndex,   
    5.                         numMoved);   
    6.   
    7. // Let gc do its work   
    8. int newSize = size - (toIndex-fromIndex);   
    9. while (size != newSize)   
    10.     elementData[--size] = null;   
    11.    }  

     

     

      

     

       结合API文档和网上搜索来的ArrayList的特效来总结下:

       API文档是如此介绍ArrayList的:

    接口的大小可变数组的实现。实现了所有可选列表操作,并允许包括 null 在内的所有元素。除了实现 List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。(此类大致上等同于 Vector 类,除了此类是不同步的。)

     Vector由于使用了synchronized方法(线程安全)所以性能上比ArrayList要差。

     

    List允许有相同的元素

    ArrayList的方法都没有同步,所以在多线程中是不安全的,必须自己同步

    toArray()方法返回的是和原列表相同的对象,也就是说:
    arrayList.toArray()[0]==arrayList.get(0)返回的是true(假定arrayList不空)。

    clone()方法是一个浅拷贝。

    API文档:

    在添加大量元素前,应用程序可以使用 ensureCapacity 操作来增加 ArrayList 实例的容量。这可以减少递增式再分配的数量
    可以通过自己调用ensureCapacity()提高效率

    创建同步线程的ArrayList

    List list = Collections.synchronizedList(new ArrayList(...));

     

    ArrayList提供了4种添加方法:

    Java代码  收藏代码
    1. list.add(e);  
    2. list.add(index, element);  
    3. list.addAll(c);  
    4. list.addAll(index, c);  

     每次添加都调用ensureCapacity()方法,扩大容量是每次是1.5倍

     

    Java代码  收藏代码
    1. list.set(index, element);  
    2. list.add(index, element);  

     set是用指定的元素替代此列表中指定位置上的元素

    add是将指定的元素插入此列表中的指定位置。向右移动当前位于该位置的元素

     

    对于新增和删除操作add和remove,ArrayList要移动数据。所以性能不是很好

    而访问数据是直接根据数组下标来获得数据的,所以速度很快

     

     移除ArrayList内重复数据:

    Java代码  收藏代码
    1. public static void removeDuplicate(List arlList) {  
    2.     HashSet h = new HashSet(arlList);  
    3.     arlList.clear();  
    4.     arlList.addAll(h);  
    5. }  

     除了具有Collection接口必备的iterator()方法外,List还提供一个listIterator()方法,返回一个 ListIterator接口,和标准的Iterator接口相比,ListIterator多了一些add()之类的方法,允许添加,删除,设定元素,还能向前或向后遍历。  

     

 
 
=======================================================================
ArrayList不是线程安全的。concurrent并发包下的CopyOnWriteArrayList类。
 
ArrayList实现了Serializable接口,因此它支持序列化,能够通过序列化传输,实现了RandomAccess接口,支持快速随机访问,实际上就是通过下标序号进行快速访问,实现了Cloneable接口,能被克隆。
 
 
 
private transient Object[] elementData;  
private int size;
 
 
 
被标记为transient的属性在对象被序列化的时候不会被保存。
 
 
 
// ArrayList带容量大小的构造函数。    
    public ArrayList(int initialCapacity) {    
        super();    
        if (initialCapacity < 0)    
            throw new IllegalArgumentException("Illegal Capacity: "+    
                                               initialCapacity);    
        // 新建一个数组    
        this.elementData = new Object[initialCapacity];    
    }    
   
    // ArrayList无参构造函数。默认容量是10。    
    public ArrayList() {    
        this(10);    
    }    
   
    // 创建一个包含collection的ArrayList    
    public ArrayList(Collection<? extends E> c) {    
        elementData = c.toArray();    
        size = elementData.length;    
        if (elementData.getClass() != Object[].class)    
            elementData = Arrays.copyOf(elementData, size, Object[].class);    
    }
 
 
set(int index, E element)、add(E e)、add(int index, E element)、addAll(Collection<? extends E> c)、addAll(int index, Collection<? extends E> c)这些添加元素的方法。
 
【未完成】
posted @ 2015-07-09 00:35  Uncle_Nucky  阅读(165)  评论(0)    收藏  举报