源码学习笔记(一)ArrayList

115 阅读1分钟
  1. ArrayList 是基于数组实现的,是一个动态数组,容量支持自动自动增长
  2. ArrayList 线程不安全
  3. ArrayList 实现了 Serializable,支持序列化
  4. ** 元素对象不支持序列化。elementData 是被transient关键字修饰的,不可被序列化的,它自己实现了writeObject()和readObject()方法**

writeObject方法--序列化

readObject方法-- 反序列化

5、 elementData数组大小,大于等于列表的实际大小size。一个列表插入12个数据,数组整个长度15,实际大小size是12

6、列表默认elementData默认大小是10,new ArrayList<>()时初始化大小

7、 自动拓展容量,新容量=当前容量*1.5。参考第五点的例子,默认长度10的数组,插入12个数据后,数组长度变成了15。

8、看上图,最大容量是Integer.MAX_VALUE,但是默认会使用Integer.MAX_VALUE -8 。原因是为了减少出错的几率,注释上写了,因为虚拟机中,对象头里的markword classpointer需要占用内存。

戳我了解更多对象在内存中的存储形式

9、 如果想将element对象大小弄成跟size一样大,也就是释放多余空闲的数组,使用trimToSize方法

实例效果:modCount增加了1次,elementData数组跟列表size一致

10、contains方法是通过indexOf实现

11、toArray的两种使用

      (1)list.toArray();

  (2)list.toArray(T[] a);

  对于第一个重载方法,是将list直接转为Object[] 数组;

  第二种方法是将list转化为你所需要类型的数组,当然我们用的时候会转化为与list内容相同的类型。

  不明真像的同学喜欢用第一个,是这样写:

ArrayList<String> list=new ArrayList<String>();
for (int i = 0; i < 10; i++) {
    list.add(""+i);
}

String[] array= (String[]) list.toArray();

       结果一运行,报错:Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;

  原因一看就知道了,不能将Object[] 转化为String[],转化的话只能是取出每一个元素再转化。**java中的强制类型转换只是针对单个对象的,想要偷懒将整个数组转换成另外一种类型的数组是不行的,这和数组初始化时需要一个个来也是类似的。**像这样:

Object[] arr = list.toArray();
for (int i = 0; i < arr.length; i++) {
    String e = (String) arr[i];
    System.out.println(e);
}

       所以第一个重构方法就不是那么好使了。

  实际上,将list转化为array的时候,第二种重构方法更方便,用法如下:

String[] array =new String[list.size()];
list.toArray(array);

12、for循环的删除,为什么使用iterator,请看下文

developer.aliyun.com/article/403…

13、for循环的删除,为什么删除倒数第二个数据,不会抛异常CurrentModificationException,请看下文

戳我进入

14、Iterator 与 ListIterator 的同与异

       相同点:

              都是接口

              相同的方法有hasNext , next , removre , forEachRemaining

       不同点:

             ListIterator 继承了Iterator

             ListIterator 只可以用于List类型的集合

             ListIterator多的方法有: hasPrevious, previous , nextIndex, previousIndex , set , add。 分为几类就是,可以双向遍历(hasPrevious, previous ),可以知道下标( nextIndex, previousIndex ),可以新增对象(add),可以修改(set)

add 是在当前位置的下一次插入一个数据, set是将当前位置是数据更换

15、subList返回的是原始List的一个视图,并没有新创建一个arrayList, 所以既不能将subList转回到ArrayList,在对subList或原始List变更时,也会引起联动变更,甚至错误。

原因看下图:subList里面的数据更改函数,会将自己的modCount同步原始List的modCount;但是原始List的add或remove函数,modCount不会同步给subList, 也就导致了subList会出现CurrentModificationException;  原始List的set函数只改了数据,为改变modCount,也就不会出错,此时原始List和subList的数据都改变了

16、spliterator ,另外一个迭代器。它有一个trySplit 函数,在可能并行的操作中使用。

如果我们调用trySplit一个分隔符,它将返回一个分隔符,其中包含调用方分隔符不会覆盖的元素。这就是说,我们用它将分离器分成多个部分。这样做的主要好处trySplit是我们可以并行处理分割的元素。trySplit如果源包含有限数量的元素,则重复调用将返回null

17、 ArrayList list = Arrays.asList("1“,”2”);很方便创建一个list对象,但是该list不能add,remove这些操作,因为此时返回list是AbstracList类的一个内部类,而不是真正的ArrayList。

而AbstractList只实现了add,set等函数,对set, add未实现

实际例子

18、JDK1.8新增方法,重写Collection接口的removeIf方法,为了安全和快速,removeIf分成两步走 : 

      1.根据匹配规则找到所有符合要求的元素  

      2.移除元素并转移剩余元素位置 *补充:

输出结果,将大于等于2的都删除了

[1,2,3]
after remove = [1]

延伸学习

arrayList源码分析

为什么阿里的开发规范建议谨慎使用subList

ArrayList在JDK1.8上的更新

JDK 8 的lambad表达式