理解集合ArrayList的源码

307 阅读3分钟

该文章仅是本人查看源码分析的,能力有限,如有发现错误之处,还请指出,感激不尽!

介绍内容:

  • 无参构造的创建
  • 扩容机制
  • 删,改,查
  • 内部类 Itr,ListItr,SubList 的使用
  • 支持 lambda 表达式

ArrayList源码分析

案例介绍:

// 扩容案例:
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(4, 5);
list.forEach(System.out::println);
List<Integer> list = new ArrayList<>(3);

//提问:超过了设置的容量大小,会不会报错?
// fast-fail案例:
List<Integer> list = new ArrayList<>(3);
list.add(1);
list.add(2);
list.add(3);
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()) {   
  Integer next = iterator.next(); 
  list.add(6);   
  System.out.println("next = " + next);
}

// 提问:在迭代器遍历时,添加元素时,会不会受到影响?
变量:
transient Object[] elementData  //存储ArrayList元素

private int size;  //ArrayList的大小

1、未设置初始值时的构造
例如:给 list 分配一个空数据的对象数组 List<Integer> list = new ArrayList<>();

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; 
public ArrayList() {    
     this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

2、扩容机制
空数据数组添加元素时,默认分配10的容量

private static final int DEFAULT_CAPACITY = 10;
private static int calculateCapacity(Object[] elementData, int minCapacity) {    
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {        
        return Math.max(DEFAULT_CAPACITY, minCapacity);   
    }    
    return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
private void grow(int minCapacity) {  
   int oldCapacity = elementData.length;   
   int newCapacity = oldCapacity + (oldCapacity >> 1);  
   if (newCapacity - minCapacity < 0)     
       newCapacity = minCapacity;    
   if (newCapacity - MAX_ARRAY_SIZE > 0)       
       newCapacity = hugeCapacity(minCapacity);     
   elementData = Arrays.copyOf(elementData, newCapacity);
}
  1. 单个添加元素,当 size 大于 elementData 长度时,会扩容成原来容量大小的1.5倍
  2. 添加多个(n)元素,当 size 大于 elementData 长度的1.5倍时,会扩容成 size 大小的容量
  3. 添加元素扩容后,如果扩容后的大小超过了 MAX_ARRAY_SIZE(2^31-1-8)时,会判断当前 size 是否超过 MAX_ARRAY_SIZE
private static int hugeCapacity(int minCapacity) {    
   if (minCapacity < 0)      
      throw new OutOfMemoryError();    
    return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}

3、查,改,删
指定 index 做操作时,会对 index 做范围校验,使用 rangeCheck(),存在数据越界异常 -- IndexOutOfBoundsException

  • 查找元素,get(int index)
  • 修改元素,set(int index, E element)
  • 删除元素,remove(int index),remove(Object o)
 补充知识点
   /**
    * @param src 源数组
    * @param srcPos 源数组要复制的起始位置
    * @param dest 目的数组
    * @param destPos 目的数组放置的起始位置
    * @param length 复制的长度
    */
System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length);

4、内部类 Itr,ListItr,SubList 的使用

  • Itr 与 ListItr 都是迭代器,源于 Iterator 与 ListIterator,区别是 ListIterator 增加了部分方法,迭代器中包含属性 expectedModCount = modCount,在调用 next() 时需要判断 modCount 是否改变,如果有改变则会抛异常,modCount 用于记录集合被修改的次数
 final void checkForComodification() {
     if (modCount != expectedModCount)
         throw new ConcurrentModificationException();
 }

请参考文档:blog.csdn.net/weixin_3924…

  • SubList 是 ArrayList 中的视图,使用subList(int fromIndex, int toIndex)获取数组中的 [fromIndex,toIndex)的视图,但是调用该视图中的方法依然可以操作原数组中的数据,但是对 ArrayList 进行添加、删除的操作之后,在操作 subList 会抛出异常
  1. 支持stream的方法(后续文章详细介绍)
    jdk1.8 引入了 stream,掌握集合中的流方法有助于开发高质量代码,简单介绍几个常用的方法
  • forEach():集合元素遍历,代替 for 循环 例如:
List<Integer> list = Arrays.asList(1,2,3);
list.forEach(System.out::println);
  • filter():对流中的数据做筛选,从流中排查某些元素 例如:
list = list.stream().filter(item -> Objects.equals(item, 2)).collect(Collectors.toList()); 
  • map():将元素转换成其他形式或提取信息 例如:
List<Person> list = new ArrayList<>();
list.add(new Person("zhangsan", 11));
list.add(new Person("lisi", 13));
list.add(new Person("wangwu", 15));
List<Integer> ageList = list.stream().map(Person::getAge).collect(Collectors.toList());
  • flatMap():流的扁平化
List<Integer> list1 = Arrays.asList(1,2,3);
List<Integer> list2 = Arrays.asList(4,5,6);
List<int[]> collect = list1.stream().flatMap(i -> list2.stream().map(j -> new int[]{i, j})).collect(Collectors.toList());

其他补充:

writeObject(java.io.ObjectOutputStream s)
readObject(java.io.ObjectInputStream s)用于ArrayList的序列化与反序列化, 处理非statictransient修饰字段