目录
特点:元素有序,且可重复
遍历:下标,foreach,迭代器
扩容:
- 初始容量10,负载因子0.5,扩容增量0.5倍
- 新容量 = 原容量 + 原容量 * 0.5 , 如 ArrayList的容量为10,那么一次扩容后容量为15
- 简单数据结构,超出容量自动扩容,动态数组
- ArrayList的扩容机制是效率和空间的之间的平衡,
- 内部实现是基于基础的对象数组的
- 不适合随机增加或删除(有位移现象)
- 适用于不确定数据的最大个数的情况
- 随机访问快、遍历快
- 线程不安全(在作为类的成员变量时)
- LinkedList提供额外的get,remove,insert方法在LinkedList的首部或尾部
- LinkedList可被用作堆栈(stack)【包括了push,pop方法】,队列(queue)或双向队列(deque)
- 以双向链表实现,链表无容量限制,允许元素为null,线程不安全
线程安全,但是并行性能慢(因为上锁了),不建议使用
- 写时复制
- 线程安全
- (版本的)最终一致性,无法做到实时版本一致
- 比Vector性能高
- 适合于读多,写少的场景
- 实现了List接口,使用方式与ArrayList类似
- 写时复制出一个新的数组,完成插入、修改或者移除操作后将新数组赋值给原来的数组
remove特性:
- 传入整数类型时删除下标
- 传入对象类型时删除对应的元素(如Integer.parseInt(null))
数据准备工作:为方便演示,需要有紧挨在一起的两个或多个相同的元素
List<Integer> list=new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
list.add(3);
list.add(4);
使用ArrayList中remove方法的几种不同的写法:
错误写法示例
for(int i=0;i<list.size();i++){
if(list.get(i)==3) list.remove(i);//list.get(i)得到下标为i的元素
}
👆👆错误原因:ArrayList在增加和删除时有位移现象,当两个一样的元素3相邻时,第一个3在判断并删除后,第二个3及其后面的所有元素的下标都会向前移动一位,这样第二个3就到了第一个3下标所在的位置,但是指针已经判断过第一个3所在位置的元素是否为3了,故不会删除掉第二个3
for(Integer i:list){
if(i==3) list.remove(i);
}
👆👆错误原因:因为ArrayList中有一个变量(modCount=原数组的元素个数)还在内部封装了一个内部类(Itr),这个内部类实现了迭代器,当使用foreach方法遍历时,使用的是ArrayList内部类的迭代器,其中内部类中定义了一个改变次数的变量(expectedModCount),这个变量被赋值为外部modcount的值,当使用内部类(Itr)发生增加或者修改操作时,抛出异常,其目的是阻止ArrayList长度发生改变。
简而言之,就是不推荐使用foreach进行集合的增加和删除操作,它更适合用来遍历数据
正确写法示例
for(int i=0;i<list.size();i++){
if(list.get(i)==3) list.remove(i--);
}
👆👆正确原因:使用了i--,即在进行删除之后指针会向前移一位,再回到删除过的下标位置进行判断,而这样就避免了因为ArrayList的位移现象所导致的判断遗漏。
注意:不可使用--i,因为--i会在remove方法在删除之前执行
for(int i=list.size()-1;i>=0;i--){
if(list.get(i)==3){
list.remove(i);
}
}
👆👆正确原因:使用了从集合中的最后一位元素向第一位元素方向进行遍历的倒序遍历方法,这样即使ArrayList的位移现象发生也无法对删除产生影响
最后一种写法:ArrayList集合在进行删除、增加等操作时,要考虑其动态位移的特性,推荐使用迭代器,会比较安全
Iterator<Integer> it=list.iterator();
while(it.hasNext()){
if(it.next()==3){
it.remove();
}
}
注意:上述代码的it.remove不要写成list.remove(i)