ArrayList源码分析
构造函数(有参和无参):
无参:有个被transient关键字修饰的elementData的Object类型长度为0的数组。
有参:参数的含义就是这个集合的含量,在ArrayList中如果指定了长度的话,会让元素容易指向一个新的Object数组,长度为转入的值,如果转入的长度为0,那么就默认使用Object类型长度为0的DEFAULTCAPACITY_EMPTY_ELEMENTDATA数组。
添加Add方法(对Object[]的elementData属性的操作):
如果我们一开始声明的是一个空参的构造函数,那么代码会走进第一个if语句,传入的minCapacity是1,那么最终计算得出minCapacity是等于10的也就是说,如果我们添加了一个默认的无参构造函数,在添加时,数组会将默认的数组长度变为10。
集合初始化的优化思路:
如果我们很确定的知道我们要存储元素的数量,最好在声明集合的时候传入容量值。试想,如果我们只需要存储3个元素,而我们声明了一个空参的构造函数,那么集合的长度会是10,也就是说,数组有7个长度的空间被浪费了,这就是对内存的一种浪费。
ArrayList的扩容(对Object[]的elementData属性的操作)::
声明数组长度之后会判断数组的容量是否够用,如果够用,那么不需要扩容,反之,则进行扩容,判断扩容代码没什么,很简单,但是扩容怎么办呢?数组一旦声明不是不能更改了吗?源码中是用Arrays的copyOf方法,将数组进行copy,而copyOf的底层源码,是声明了一个新的数组,然后将原有的数组内容全复制进去,这样,就在不影响原有数据的基础上进行了数组扩容。
注:在扩容过程中,如果超出了JDK对ArrayList的指定长度则会抛出数组越界异常。
集合扩容的优化思路:
我们需要保存1000个元素,而一开始默认长度是10,那么集合需要很多次扩容,每次扩容是上一次容量的1.5倍,每次扩容还要进行复制。如果不事先声明一个长度的话,使用效率会大大降低,即便是不知道具体数字,也可以指定一个大概的容量。
删除remove(对Object[]的elementData属性的操作):
删除是每次进行数组复制,然后让旧的elementData置为null进行垃圾回收,elementData[(—size)] 就是将ArrayList最后一个长度因为删除了元素向前移了,然后通过垃圾回收机制将其置为空。
ArrayList的使用总结
1.在声明时尽量指定长度。
2.ArrayList底层是数组(Object [] elementData),数组是适合查询的,因为数组每个元素的内存空间是固定的,每次查询时,只需要去查询对应位置的内存空间,就可以很快找到相应的值。而数组不擅长的是添加和删除(使用LinkedList)。
3.在开发过程中如果想让ArrayList/LinkedList变成线程安全的就需要使用Collections.synchronizedList(new ArrayList())方法会返回一个线程安全的ArrayList集合。
List
4.借助Collections—此类仅包含对集合进行操作或返回集合的静态方法。它包含对集合进行操作的多态算法,“包装器”,它返回由指定集合支持的新集合,以及其他一些可能性和结束。如果提供给它们的集合或类对象为null ,则此类的方法都抛出NullPointerException。
ArrayList使用的transient的使用总结
1)一旦变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问。
2)transient关键字只能修饰变量,而不能修饰方法和类。注意,本地变量是不能被transient关键字修饰的。变量如果是用户自定义类变量,则该类需要实现Serializable接口。
3)被transient关键字修饰的变量不再能被序列化,一个静态变量不管是否被transient修饰,均不能被序列化。
还没有评论,来说两句吧...