JDK中ArrayList的实现

JDK中ArrayList的实现

ArrayList底层是一个Object数组,通过维护这个数组结构来改变ArrayList的结构。

transient Object[] elementData;

ArrayList实现比较简单,就是对数组进行操作,ArrayList定义了以下常量和实例变量。

private final static int DEFAULT_CAPACITY = 10;//默认容量
private final static Object[] EMPTY_ELEMENTDATA = {};//定义的一个空Object数组
private final static Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//默认容量的空数组
transient Object[] elementData;//存放数据的Object数组
private int size;//容量大小
private staitc final int MAX_ARRAY_SZIE = Integer.MAX_VALUE - 8;//最大数组容量 2^31 - 8

先来初始化一个ArrayList,调用了ArrayList的构造方法。

List<String> arrayList = new ArrayList<String>();
arrarList.add("a");

//ArryayList的构造方法,将elementData赋值成默认容量的空数组
public ArrayList(){
  this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

先来看看ArrayList的最常用的add方法具体是怎么实现,add方法有两个重载方法,一个是参数是一个对象的add(E e),一个c参数一个下标一个对象的add(int index, E e)。我们先看看add(E e)这个方法,另外一个方法和这个差不多,大同小异。核心方法是add里面的ensureCapacityInterval方法,这个方法是用来确保内部容量充足的。

public boolean  add(E e){
  ensureCapacityInternal(size+1);//确保容量充足
  elementData[size++]  = e;//将elementData数组赋值,下标对应值,并将size自增
  return true;
}

确保容量充足ensureCapacityInternal方法:

public void ensureCapacityInternal(int minCapacity){
  if(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA){//判断elementData是否是空数组
    minCapacity = Math.max(DEFAULT_CAPACITY,minCapacity);//如果是空数组,赋值默认的容量大小10
  }
  ensureExplicitCapacity(minCapacity);//检查容量是否充足
}

//Math.max方法
public static int max(int a, int b){
  return a > b? a : b;
}

检查容量是否充足方法ensureExplicitCapacity方法:

public void sensureExplicitCapacity(int minCapacity){
  modCount++;//这个modCount是父类AbstractList的,用来记录ArrayList结构的变化次数,在迭代器中用到
  if(minCapacity - elementData.length > 0){//如果容量超过了默认容量10,进行扩容操作
    grow(minCapacity);
  }
}

扩容方法grow:

public void grow(int minCapacity){
  int oldCapacity = elementData.length;//原容量为elementData的length
  int newCapacity = oldCapacity + (oldCapacity >> 1);//新容量 = 原容量 + 原容量右位移1位
  if(newCapacity - minCapacity < 0 ){
    newCapacity = minCapacity;
  }
  if(minCapacity - MAX_ARRAY_SIZE < 0){//
    newCapacity = hugeCapacity(minCapacity);//当扩容容量大于最大容量时进行巨大容量扩容,最大为2^31
  }
  elementData = Arrays.copyOf(elementData, newCapacity);//通过Arrays.copyOf方法进行动态扩容
}

巨大容量hugeCapacity方法:

public int hugeCapacity(int minCapacity){
  if(minCapacity < 0){
    throw new OutOfMemoryError();
  }
  return (minCapacity > MAX_ARRAY_SIZE)? Integer.MAX_VALUE : MAX_ARRAY_SIZE
}

以上就是ArrayList的add(E e)方法的实现,另外的重载方法add(int index, E e)大同小异,这个方法表示从指定位置插入指定元素,都是先进行确保容量充足操作,然后再给elementData进行赋值和size自增,只不过就是赋值操作不一样。add(int index , E e)方法:

public void add(int index, E e){
  rangeCheckForAdd(index);//检查下标是否越界
  ensureCapacityInternal(size+1);//确保容量充足,和add(E e)是一样
  System.arraycopy(elementData, index, elementData, index+1, size-index);//通过数组复制方法实现从																	   指定位置插入元素
  elemtentData[index] = e;
  size++;
}

public void rangeCheckForAdd(int index){
  if(index > size || index < 0){
     throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
  }
}
private String outOfBoundsMsg(int index) {
  return "Index: "+index+", Size: "+size;
}

再来看看ArrayList的get(int index)方法,get方法用来获取指定位置的元素。get方法比较简单,就是将elementData数组的index下标的元素返回。

public E get(int index){
  rangeCheck(index);//检查下标是否越界
  return elementData[index];
}

private void rangeCheck(int index) {
  if (index >= size)
    throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

private void rangeCheckForAdd(int index) {
  if (index > size || index < 0)
    throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

再来看看ArrayList的set(int index, E e)方法,set方法是一个替换操作,将指定位置的元素替换成指定的元素。set方法也很简单,就是获取elementData数组index下标元素,然后赋值成指定元素,并将原元素返回。

public E set(int index, E element){
  rangeCheck(index);//判断下标是否越界
  E oldValue = elementData[index];
  elementData[index]  = element;
  return oldValue;
}

再来看看ArrayList的remove方法,remove有两个重载方法,一个是remove(int index),一个是remove(Object o),前者表示删除指定位置的元素,后者表示删除指定元素,看完底层代码后你会发现,后者和前者实现是一样的,只不过后者是先获取指定元素的下标,然后通过下标进行删除元素,其实就是删除指定位置的元素。

先来看看remove(int index)方法,这个方法主要是通过数组复制实现元素的删除,删除后并将删除的元素返回。

public E remove(int index){
  rangeCheck(index);//判断下标是否越界
  modCount++;//记录ArrarList结构变化的次数
  E oldValue = elementData[index];//需要删除的元素
  int numMoved = size -index -1;//复制时复制的元素个数
  if(numMoved > 0){
    System.arraycopy(elementData, index+1, elementData, index, numMoved);
  }
  elementData[--size] = null;//交给GC去处理
  return oldValue;
}

上面的System.copyof方法的作用就是,比如原ArrayList的是{1,2,3,4},你要删除index=1的元素就是2,删除完之后变成{1,3,4},这个方法执行完之后ArrayList就变成了{1,3,4,4},然后通过elementData[–size] = null,将最后一个元素赋值成null,ArrarList就成了{1,3,4}。

再看来来remove(Object o)方法,这个方法返回的是一个boolean值。

public boolean remove(Object o){
  if(o == null){
    for(int index = 0; index < size; index++){
      if(elementData[index] == null){
        fastRemove(index);
        return true;
      }
    }
  }else{
    for(int index = 0; index < size; index++){
      if(o.equals(elementData[index]){
        fastRemove(index);
        return true;
      }
    }
  }
  return false;
}

fastRemove(int index)方法:

public void fastRemove(int index){
  modCount++;
  int numMoved = size -index -1;
  System.arraycopy(elementData, index+1, elementData, index, numMoved);
  elementData[--size]=null;// clear to let GC do its work
}

说到了remove方法,要注意一个坑,先看下面一段代码。

public static void main(String[] args) {
		
		List<String> list = new ArrayList<String>();
		list.add("a");
		list.add("b");
		list.add("c");
			
		for(String str : list) {
			if("a".equals(str)) {
				list.remove(str);
			}
		}
	}

这段代码会报一个java.util.ConcurrentModificationException的错误。

Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
	at java.util.ArrayList$Itr.next(ArrayList.java:851)

为什么会报这个错误,新的for循环底层是通过迭代器实现的,这里就要说到了ArrayList的迭代器接口了,ArrayList中定义了一个内部类Itr,当调用iterarot()方法时,生成一个Itr对象返回。

//iterator方法
public Iterator<E> iterator() {
  return new Itr();
}

//私有内部类
private class Itr implements Iterator<E> {
        ......
}

内部类Itr定义了expectedModCount这个属性,将modCount的值赋值给了expectedModCount,这里说到了modCount这个属性,这个属性就是记录ArrayList结构的变化次数比如add、remove操作时modCount就会自增。

int expectedModCount = modCount;

当创建一个迭代器时就会创建Iterator对象,expectedModCount就会被赋值。当调用迭代器的next()方法时,会调用checkForComodification()方法,进行对修改的同步检查。

public E next() {
  checkForComodification();
  ......
}

final void checkForComodification() {
  if (modCount != expectedModCount)
	 throw new ConcurrentModificationException();
  }
}

再来看上面报错的代码,当调用ArrayList的remove方法时,modCount会自增,但是迭代器expectedModCount没有被重新赋值,这个时候modCount和expectedModCount是不一样的,所以当迭代器调用next()方法时,再调用checkForComodification()方法时就会报错了。

避免上面的错误,可以将代码修改成以下的样子。主要是两种方法,一种是通过下标删除,使用传统的for循环。一种是调用迭代器的remove()方法。迭代器的remove()方法会将expectedModCount重新赋值成modCount。

public void remove() {
	if (lastRet < 0)
		throw new IllegalStateException();
		checkForComodification();

	try {
		ArrayList.this.remove(lastRet);
		cursor = lastRet;
		lastRet = -1;
		expectedModCount = modCount;
	} catch (IndexOutOfBoundsException ex) {
		throw new ConcurrentModificationException();
	}
}

下面是修改后的代码:

public static void main(String[] args) {
		List<String> list = new ArrayList<String>();
		list.add("a");
		list.add("b");
		list.add("c");
			
		for(int i = 0; i < list.size(); i++ ) {
			if("a".equals(list.get(i))) {
				list.remove(i);
			}
		}
		System.out.println(list);//输出[b, c]
	}
public static void main(String[] args) {
		
		List<String> list = new ArrayList<String>();
		list.add("a");
		list.add("b");
		list.add("c");
		
		for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
			String string = iterator.next();
			if("a".equals(string)) {
				iterator.remove();	
			}
		}
		System.out.println(list);
	}

以上就是ArrayList的常用方法的源码实现,其他的方法继续学习。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值