Java容器----ArrayList简单分析
java容器可以存放任意类型对象,取出时需要进行强转(通过泛型可以解决此类问题)。
容器类经常使用到的有三种:Map,List,Set;
先记录下ArrayList(最简单)
ArrayList是List类的一种,继承AbstractList,并实现List<E>, RandomAccess, Cloneable, java.io.Serializable四个接口。
List<E>接口实现了Collection接口,实现了容器共有的方法,例如:size(),isEmpty(),add(),remove()等,而list和Collecton的区别是:
1.添加:list支持按照index添加add(index,object)
2.修改,list具有set(index,object)
3.删除,list是按照索引进行删除的remove(123)
4.判断,list中含有lastIndexOf()
5.转换,collection转换成的数组存储的是集合中元素的地址,而list转换成的数组存储的是集合中元素的值
RandomAccess接口:用于支持快速(通常为恒定时间)随机访问。该接口的主要目的是允许通用算法更改其行为以在应用于随机访问或顺序访问列表时提供良好的性能。
Cloneable接口则是为了克隆而准备的接口。
Serializable接口:实现序列化其实可以看成是一种机制,按照一定的格式将 Java 对象的某状态转成介质可接受的形式,以方便存储或传输。其实想想就大致清楚基本流程,序列化时将 Java 对象相关的类信息、属性及属性值等等保存起来,反序列化时再根据这些信息构建出 Java 对象。而过程可能涉及到其他对象的引用,所以这里引用的对象的相关信息也要参与序列化。虽然接口没有代码,但它具体是通过JVM来实现。具体的抽象思维可以看作是把实现这个接口的类冻起来等需要时再进行解冻。
代码分析:
private static final long serialVersionUID = 8683452581122892189L; 代码第一行就出现了serialVersionUID,JAVA序列化的机制是通过判断类的serialVersionUID来验证的版本一致的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID于本地相应实体类的serialVersionUID进行比较。如果相同说明是一致的,可以进行反序列化,否则会出现反序列化版本一致的异常,即是InvalidCastException。如果我们不希望通过编译来强制划分软件版本,即实现序列化接口的实体能够兼容先前版本,就需要显示的定义一个serialVersionUID,类型为long的变量。不修改这个变量值的序列化实体,都可以相互进行序列化和反序列化。 private static final int DEFAULT_CAPACITY = 10; 定义了初始容器的大小 private static final Object[] EMPTY_ELEMENTDATA = {}; 空数组,当调用无参数构造函数的时候默认给个空数组 transient Object[] elementData; 存储ArrayList的元素的数组缓冲区。ArrayList的容量是此数组缓冲区的长度 private int size; arrayList的实际元素数量 ArrayList底层其实就是一个数组,ArrayList中有扩容这么一个概念,正因为它扩容,所以它能够实现“动态”增长 public ArrayList(int initialCapacity) { if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } } 构造方法传入默认的capacity来设置大小,设置默认数组大小0 public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } 空构造方法,默认大小为10 public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0) { // c.toArray might (incorrectly) not return Object[] (see 6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else { // replace with empty array. this.elementData = EMPTY_ELEMENTDATA; } } //构造方法传入一个Collection, 则将Collection里面的值copy到arrayList public void trimToSize() { modCount++; if (size < elementData.length) { elementData = (size == 0) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size); } } 修正大小,ArrayList每次增长会预申请多一点空间,1.5倍+1,而不是两倍,这样就会出现当size() = 1000的时候,ArrayList已经申请了1200空间的情况,trimToSize 的作用只是去掉预留元素位置,就是删除多余的200,改为只申请1000,内存紧张的时候会用到。 private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1);扩大1.5X 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); } 首先去检查一下数组的容量是否足够扩容到原来的1.5倍,第一次扩容后,如果容量还是小于minCapacity,就将容量扩充为minCapacity。足够:直接添加,不足够:扩容
下面主要看ArrayList中的具体操作。
增加
add( int index, E element ) :在指定的位置添加元素
先检查了index的范围,然后通过ensureCapacityInternal来进行扩容,最后通过复制的方式来实现增加元素。
addAll( Collection<? extends E> c ):添加一个集合到元素的末尾.以上返回类型是boolean。
将要增加的c进行转型,确认长度,增加容量,复制,添加。
get方法:
先是检查index的范围,然后根据index去实现值的返回
set方法:
检查index范围,替换旧元素,返回旧元素。
remove方法:
检查角标,删除元素,计算出需要移动的个数,移动,设置为null,回收。
modcount作用:
在使用迭代器遍历的时候,如果使用ArrayList中的remove(int index) remove(Object o) remove(int fromIndex ,int toIndex) add等方法的时候都会修改modCount,在迭代的时候需要保持单线程的唯一操作,如果期间进行了插入或者删除,就会被迭代器检查获知,从而出现运行时异常。
其余都大同小异,具体记录一下常用方法。
1.增加
add(E e):添加一个元素到列表的末尾 平均时间复杂度为O(1)
add( int index, E element ) :在指定的位置添加元素 平均时间复杂度为O(n)
addAll( Collection<? extends E> c ):添加一个集合到元素的末尾.以上返回类型是boolean
2.删除
remove(Object o):删除列表中第一个出现O的元素 时间复杂度为O(n)
remove( int index):删除列表中指定位置的元素 时间复杂度为O(n)
removeAll(Collection<?> c):删除列表中包含C的所有元素
removeIf(Predictcate<? super E> filter):删除列表中给定谓词的所有元素
removeRange( int from,int to ):删除从from到to的所有元素
clear():清除所有的元素。返回类型为void
3.更改
retainAll( Collection<?> c ):仅仅保留列表中和C相同的元素,相当于&运算
set(int index,E element):用element替换index位置上的元素。
size():返回此列表的元素数
sort(Comparator<? super E> c):按照指定的排序规则排序
subList( int from , int to ):返回从from到to之间的列表
toArray():将列表转化为数组
4.查
contains(Object o):如果包含元素o,则返回为true
get(int index):返回指定索引的元素 时间复杂度为O(1)
indexOf( Object o ):返回此列表中指定元素的第一次出现的索引,如果列表不包含此元素,返回-1
lastindexOf( Object o ):返回此列表中指定元素的最后一次出现的索引,如果列表不包含此元素,返回-1
isEmpty():如果列表为空,返回true.
iterator():返回列表中元素的迭代器
listIterator():返回列表的列表迭代器(按适当的顺序)
listIterator(int index):从适当的位置返回列表的列表迭代器(按照正确的顺序)
5.其他
size() : 获取集合长度,通过定义在ArrayList中的私有变量size得到
isEmpty():是否为空,通过定义在ArrayList中的私有变量size得到
contains(Object o):是否包含某个元素,通过遍历底层数组elementData,通过equals或==进行判断
clear():集合清空,通过遍历底层数组elementData,设置为null
ArrayList注意点:
ArrayList非线程安全,底层是一个Object[],添加到ArrayList中的数据保存在了elementData属性中。
当调用new ArrayList<>(new HashSet())时,根据源码,我们可知,可以传递任何实现了Collection接口的类,将传递的集合调用toArray()方法转为数组内赋值给elementData;
扩容规则为“数组当前足够的最小容量 + (数组当前足够的最小容量 / 2)”,即数组当前足够的最小容量 * 1.5,当然有最大值的限制。
删除ArrayList中的值对象,其实和通过下标删除很相似,只是多了一个步骤,遍历底层数组elementData,通过equals()方法或 == (特殊情况下)来找到要删除的元素,获取其下标,调用remove(int index)一样的代码即可。
相关推荐
fengyun 2020-06-14
xiaouncle 2020-07-31
源码物语 2020-07-18
XGQ 2020-06-21
OldBowl 2020-06-11
zagnix 2020-06-04
xhao 2020-06-04
付春杰Blog 2020-05-31
ilovewqf 2020-05-30
XGQ 2020-05-26
zhuyonge 2020-05-09
zcpHappy 2020-04-30
luohui 2020-04-29
fraternityjava 2020-04-29
willluckysmile 2020-04-24
shayuchaor 2020-04-20
fengyun 2020-04-17
少年阿涛 2020-04-11