Add();
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
先调用ensureCapacityInternal确保在本身大小下是否足够添加一个元素
private void ensureCapacityInternal(int minCapacity) {
if (elementData == EMPTY_ELEMENTDATA) { //先判断数组是否为空,是则给数组一个默认的容量,
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); //给默认容量, 默认为10
}
ensureExplicitCapacity(minCapacity); //
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++; //记录数组被操作的次数 用于标记 如果使用过程中发生了结构性变化 如删除,新增等,会报错。
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); //主要的增长代码 oldCapacity >> 1=oldCapacity / 2 ,再加原本的,所以增长是原本容量的1.5倍。
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity); //把原本来的内容复制到elementData
}
Remove方法
public E remove(int index) {
rangeCheck(index); //检查index是否在数组的容量之内
modCount++;
E oldValue = elementData(index); 获取删除的元素
int numMoved = size - index - 1; 获取需要移动的位置
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved); 该方法用于将删除元素后面的全部元素需要往前移动
elementData[--size] = null; // clear to let GC do its work 将最后一个位置置为null 方便GC回收
return oldValue; 返回删除的元素
ArrayList实现了lterable接口,lterable接口有lterator方法
public interface Iterable<T> {
Iterator<T> iterator();
}
public Iterator<E> iterator()
该方法返回一个Lterator接口的对象,定义为public interface Iterator<E> { boolean hasNext(); //是否有下一个元素 E next(); //返回下一个元素 void remove(); //删除当前元素 }
lterable和lterator:
Iterable表示对象可以被迭代,它有一个方法iterator(),返回Iterator对象,实际通过Iterator接口的方法进行遍历。
如果对象实现了Iterable,就可以使用foreach语法。
类可以不实现Iterable,也可以创建Iterator对象。
关于迭代器,有个常见的陷阱:在迭代的中间调用容器的删除方法 也就是说如果我们在遍历一个list时,如果使用list的新增,插入,删除方法的时候,会发生并发异常:如
public void remove(ArrayList<Integer> list){
for(Integer a : list){
if(a<=100){
list.remove(a);
}
}
}
运行时会抛出异常:
java.util.ConcurrentModificationException
发生此异常的原因....
如果非要删除 ,可以用迭代器的remove方法,如:
public static void remove(ArrayList<Integer> list){ Iterator<Integer> it = list.iterator(); while(it.hasNext()){ if(it.next()<=100){ it.remove(); } } }
迭代器实现原理:
public Iterator iterator() {
return new Itr(); 通过实现iterator方法 返回一个iterator对象
}
新建了一个Itr对象,Itr是一个成员内部类,实现了Iterator接口,声明为:
private class Itr implements Iterator
它有三个实例成员变量,为:
int cursor; // 返回下一个元素的位置
int lastRet = -1; // 返回最后一个返回的索引位置
int expectedModCount = modCount; //记录发生结构性变化的次数,成员内部类可以访问外部类的实例变量,所以之前每次发生结构性的变化时,modCount都会记录,在遍历中expectedModCount 会和modCount比对,如果不一至,则表明发生结构性变化。public E next() { checkForComodification(); ///每次调用next方法时,都会调用该方法,检查是否发生结构性变化。 int i = cursor; if (i >= size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
迭代器的remove方法
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet); 通过list的remove方法删除,
cursor = lastRet; 同时更新了cursor
lastRet = -1; 和lastRet
expectedModCount = modCount; 和modCount ,这样在下次调用next检查的时候 ,两个一样,才不会报错。 如果直接用list的remove方法,是不会更新expectedModCount的 , 所以迭代器的remove可以用。
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
上面我们注意到
在foreach写法中,看似并没有调用next方法,那是为啥又会检测呢?那其实是调用了的了,foreach是我们的写法,在编译器中,他会转成这种类似的写法
Iterator<Integer> it = intList.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
这就对next调用了。
那如果你直接写成这样,他是不会报错的,但是这样结果不对。
for(int a =0;a< linkedList.size();a++){
if(!linkedList.get(a).equals("F")){
linkedList.remove(a);
} }
}
迭代起的好处:
1.foreach语法更简单,更重要的是,迭代器语法更为通用,它适用于各种容器类。
2.迭代器标明的是一种关注点分离的思想,将数据的实际组织方式与数据的遍历迭代相分离,是一种常见的设计模式。需要访问容器的元素 的代码只需要一个iterator接口的引用,不需要关注数据的实际组织方式,可以使用一致和统一的方式进行访问。
3.提供iterator接口的代码了解数据的组织方式,可以提供高效的实现。在Arraylist中,size/get(index)语法与迭代器性能是差不多的,但是其他容器中,比如linkedlist中,迭代器性能就会高很多。
4.从封装的思路上来讲,迭代器提供了简单和一致的接口。
ArrayList还实现了RandomAccess接口,该接口没有定义任何代码,在java中称之为标记接口。用于声明类的一种属性。
这里实现了该接口,表明该类可以随机访问,可随机访问就是表示具备类似数组那样的特性,数据在内存中是连续存放的,根据索引值就可以直接定位到具体的元素,访问效率很高。
ArrayList总结:
1.可以随机访问,根据索引位置访问效率很高,效率是O(1),简单来说就是可以一步到位。
2.除非数组已经排序,否则按照内容查找元素效率较低,具体是O(N),N为数组长度。
3.添加效率还行,重新分配和拷贝数组的开销被平摊了,具体来说,添加N个元素的效率是O(N)。
4.插入和删除效率较低,因为需要移动元素,具体为O(N)。