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