关于ArrayList

一、 ArrayList简介
  1. ArrayList是我们在工作中经常使用的java的集合类,它的特点是非线程安全,顺序存取速度快。
  2. ArrayList继承了AbstractList,实现了List。它是一个数组队列,相当于动态数组。提供了相关的添加、删除、修改和遍历等功能。
二、 源码分析
  1. ArrayList包含了两个重要的属性:elementData和size。 elementData:表示当前这个ArrayList实例总共可以装多少个元素。 size:当前这个ArrayList实例已经装了多少个元素。 其中elementData的声明方式为:private transient Object[] elementData;这里有一个问题,我们可以发现ArrayList是一个泛型类,但是这里没有使用 E[] elementData 来声明泛型数组?

    1.1 实验创建一个泛型数组

    	public class GenericTest<T> {
    		private T[] objArr;
    		private int index = 0;
    		public GenericTest() {
    			// Error: Cannot create a generic array of T
    			objArr = new T[10];
    		}
    	}
    

    在使用new T[10]来实例化时,编译器提示了错误 也就是说java不支持泛型数组的创建?

在Sun的一篇文档中里面提到了一种情况:

List<String>[] lsa = new List<String>[10]; // Not really allowed.
Object o = lsa;
Object[] oa = (Object[]) o;
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
oa[1] = li; // Unsound, but passes run time store check
String s = lsa[1].get(0); // Run-time error: ClassCastException.

这种情况下,由于JVM泛型的擦除机制,在运行时JVM是不知道泛型信息的,所以可以给oa[1]赋上一个ArrayList<Integer>而不会出现ArrayStoreException,但是在取出数据的时候却要做一次类型转换,所以就会出现ClassCastException,如果可以进行泛型数组的声明,上面说的这种情况在编译期将不会出现任何的警告和错误,只有在运行时才会出错。而对泛型数组的声明进行限制,对于这样的情况,可以在编译期提示代码有类型安全问题,比没有任何提示要强很多。

什么是类型擦除?类型擦除可以简单的理解为将泛型java代码转换为普通java代码,只不过编译器更直接点,将泛型java代码直接转换成普通java字节码。

  1. 构造方法

ArrayList提供了三种方式的构造器

/**
* 构造一个包含指定collection的元素的列表,
* 这些元素按照该collection的迭代器返回它们的顺序排列的
**/
public ArrayList(Collection<? extends E> c) {
	elementData = c.toArray();
	size = elementData.length;
	if (elementData.getClass() != Object[].class)
	  elementData = Arrays.copyOf(elementData, size, Object[].class);
}
/**
* 构造一个指定初始容量的空列表
**/
public ArrayList(int initialCapacity) {
	  super();
  if (initialCapacity < 0)
  throw new IllegalArgumentException("Illegal Capacity: "+
  initialCapacity);
	this.elementData = new Object[initialCapacity];
}
/**
* 构造一个默认初始容量为10的空列表
**/
public ArrayList() {
	  this(10);
}
  1. 调整数组容量

    	  /**
    	  * 当ArrayList容量不足以容纳全部元素时,ArrayList会自动扩张容量,
    	  * 新的容量 = 原始容量 + 原始容量 / 2 + 1。
    	  **/
    	  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提供了五种添加元素的方法

/**
* 用指定的元素替代此列表中指定位置上的元素,并返回以前位于该
* 位置上的元素。
**/
public E set(int index, E element) {  
    RangeCheck(index);
    E oldValue = (E) elementData[index];  
    elementData[index] = element;  
    return oldValue;  
}  
/**
* 将指定的元素添加到此列表的尾部。
**/
public boolean add(E e) {  
    ensureCapacity(size + 1);   
    elementData[size++] = e;  
    return true;  
}
/**
* 将指定的元素插入此列表中的指定位置。  
* 如果当前位置有元素,则向右移动当前位于该位置的元素以及所有
* 后续元素(将其索引加1)。
**/  
public void add(int index, E element) {  
    if (index > size || index < 0)  
        throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size);  
    // 如果数组长度不足,将进行扩容。  
    ensureCapacity(size+1);
    // 将 elementData中从Index位置开始、长度为size-index的元素,  
    // 拷贝到从下标为index+1位置开始的新的elementData数组中。  
    // 即将当前位于该位置的元素以及所有后续元素右移一个位置。  
    System.arraycopy(elementData, index, elementData, index + 1, size - index);
    elementData[index] = element;
    size++;  
}
/**
* 按照指定collection的迭代器所返回的元素顺序,将该
* collection中的所有元素添加到此列表的尾部。
**/
public boolean addAll(Collectionextends E> c) {  
    Object[] a = c.toArray();  
    int numNew = a.length;  
			// 如果数组长度不足,将进行扩容。
			ensureCapacity(size + numNew);
    System.arraycopy(a, 0, elementData, size, numNew);  
    size += numNew;  
    return numNew != 0;  
}
/**
* 从指定的位置开始,将指定collection中的所有元素插入到此列表
* 中。
**/
public boolean addAll(int index, Collectionextends E> c) {  
    if (index > size || index < 0)  
        throw new IndexOutOfBoundsException(  
            "Index: " + index + ", Size: " + size);  
    Object[] a = c.toArray();  
    int numNew = a.length;  
    // 如果数组长度不足,将进行扩容。
    ensureCapacity(size + numNew);    
    int numMoved = size - index;  
    if (numMoved > 0)  
        System.arraycopy(elementData, index, elementData, index + numNew, numMoved);  
    System.arraycopy(a, 0, elementData, index, numNew);
    size += numNew;  
    return numNew != 0;  
}
  1. 查找

    查找指定元素

/**
* 返回此列表中指定位置上的元素。
**/  
public E get(int index) {  
    RangeCheck(index);  
    return (E) elementData[index];  
}
返回指定元素的索引
```
/**
* 返回指定元素的第一个匹配项的索引
**/
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;

} /** * 返回指定元素的最后一个匹配项的索引 **/ public int lastIndexOf(Object o) { if (o == null) { for (int i = size-1; i >= 0; i--) if (elementData[i]==null) return i; } else { for (int i = size-1; i >= 0; i--) if (o.equals(elementData[i])) return i; } return -1; } ``` 6. 删除

ArrayList提供了两种删除方法

/**
* 移除此列表中指定位置上的元素。
**/
public E remove(int index) {  
    RangeCheck(index);
    modCount++;  
    E oldValue = (E) elementData[index];  
    int numMoved = size - index - 1;  
    if (numMoved > 0)  
        System.arraycopy(elementData, index+1, elementData, index, numMoved);  
    elementData[--size] = null;
    return oldValue;  
}
/**
* 移除此列表中首次出现的指定元素(如果存在)。这是因为
* ArrayList中允许存放重复的元素。
**/
public boolean remove(Object o) {  
    // 由于ArrayList中允许存放null,因此下面通过两种情况来分别处理。  
    if (o == null) {  
        for (int index = 0; index < size; index++)  
            if (elementData[index] == null) {
									// 类似remove(int index),移除列表中指定位置上的元素。
									// 无返回值
									fastRemove(index);
                return true;
            }
    } else {
        for (int index = 0; index < size; index++)  
            if (o.equals(elementData[index])) {  
									// 类似remove(int index),移除列表中指定位置上的元素。
									// 无返回值
									fastRemove(index);
                return true;  
            }
        }
    return false;  
}
ArrayList还提供了移除所有元素的方法
```
public void clear() {
	modCount++;
	for (int i = 0; i < size; i++)
	    elementData[i] = null;
	size = 0;

} 7. 其他 /** * 修整此ArrayList实例的是列表的当前大小的容量。应用程序可以使用此操作, * 以尽量减少一个ArrayList实例的存储。 **/ public void trimToSize() { modCount++; int oldCapacity = elementData.length; if (size < oldCapacity) { elementData = Arrays.copyOf(elementData, size); } } ```

三、 遍历方式

我们可以通过3种方式来遍历ArrayList

//通过迭代器遍历
Integer value = null;
Iterator it = list.iterator;
while (it.hasNext) {
    value = (Integer)it.next;
​​​​​​​}
//随机访问,通过索引值去遍历。由于ArrayList实现了RandomAccess接口,所以它支持通过索引值去随机访问元素。
Integer value = null;
int size = list.size();
for (int i = 0; i < size; i++) {
    value = (Integer)list.get(i);        
}
//for循环遍历
Integer value = null;
for (Integer integ:list) {
    value = integ;
}
四、 遇见的错误
  1. 动态的删除ArrayList中的一些元素
for(int i = 0 , len = list.size();i < len; i++){
	if(list.get(i) == XXX){
		list.remove(i);
	}
}

这种方式会抛出异常

<!-- lang: java -->
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 3, Size: 3
at java.util.ArrayList.RangeCheck(Unknown Source)
at java.util.ArrayList.get(Unknown Source)
at ListDemo.main(ListDemo.java:20)

因为你删除了元素,但是未改变迭代的下标,这样当迭代到最后一个的时候就会抛异常。 常用的动态删除元素方法有两种:

/**
* 在删除的同时对长度也进行减少
**/
	int len = list.size();
for(int i = 0; i < len; i++){
  if(list.get(i) == XXX){
       list.remove(i);
       --len;
       --i;
     }
}
/**
* List接口内部实现了Iterator接口,提供开发者一个iterator()得
* 到当前list对象的一个iterator对象。
**/
Iterator<String> sListIterator = list.iterator();
while(sListIterator.hasNext()){
  String e = sListIterator.next();
  if(e.equals("你好")){
    sListIterator.remove();
  }
}

转载于:https://my.oschina.net/u/3292411/blog/1421866

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值