List集合
List是一个接口,它继承于Collection的接口,代表着有序的队列(其中的元素可以重复)
List集合下最常见的集合类有两个:ArrayList和LinkedList
不怎么用的Vector(与ArraList一样通过数组实现,但是Vector是线程安全的,如果不涉及线程安全问题,ArrayList更好(因为Vector使用synchronized,必然会影响效率))。
工作中,基本无脑用ArrayList
原因:
- 在工作中,遍历的需求比增删多,即便是增加元素旺旺也只是从尾部插入元素,而ArrayList在尾部插入元素也是O(1)
- ArrayList增删没有想象中慢,ArrayList的增删底层调用的copyOf()**被优化过,加上现代CPU对内存可以块操作**,普通大小的ArrayList增删比LinkedList更快。
LinkedList本身实现了Queue接口(一般用于耍算法题)
如果考虑线程安全的问题,CopyOnWriteArrayList,它的思想(CopyOnWrite),这个思想在Linux/文件系统都有用到。
ArrayList
ArrayList属性
1 | //初始化容量为10 |
由此可知:ArrayList底层其实就是一个数组
构造方法
1 | //如果指定了容量,那么数组就初始化成对应的容量 |
add
JDK1.8与JDK1.14不同,先1.8
modCount是快速报错机制
1 | //直接添加元素,确认list容量,尝试容量加1,看看有无必要 |
remove(int index)
删除时也用了System的本地方法arraycopy,删除位置后几位数全部向前移动一位,返回被删掉的数据
1 | public E remove(int index) { |
remove(Object o)
删除Object操作分开了Null,重点是要注意的是在对非空对象进行删除的时候ArrayList是调用了equals来匹配数组中的数据。也就是说如果你的集合(不局限于ArrayList)是对类进行操作,而你的类没有重写hashCode以及equals,那么你通过该方法来删除数据都是无法成功的,总之如果你要在集合中对类对象进行操作就需要重写上述的两个方法。此外就算你ArrayList中存有多个相同的Obejct对象,执行该方法也只会删除一次。或许有人会有疑问,既然使用equals那直接重写equals不就好了何必跟着重写hashCode呢?答案是如果你只重写equals是可以完成删除操作,但是你重写equals没有重写hashCode那么你在使用散列数据结构HashMap,HashSet对该类进行操作的话会出错(jdk1.8 HashMap工作原理(Get,Put)和扩容机制)。而在Object规范中提到的第二点要求就是如果两个对象经过equals比较后相同,那么他们的hashCode一定相同。所以这就是为什么要hashCode跟euqals两者同时重写。
1 | public boolean remove(Object o) { |
iterator被内部类的方式实现,从源码可知:Iterator实际上就是在遍历集合,
1 | private class Itr implements Iterator<E> { |
从源码看出,List有它自己对应的ListIterator接口
这个接口比普通的Iterator接口多了几个方法
可知:ListIterator可以往前遍历,添加元素,设置元素
LinkedList
get
1 | public E get(int ) { |
总结
ArrayList
- 底层实现是数组
- 默认初始化容量10,每次扩容增加原来容量的1.5倍(如果还不够就按给的容量来,但不能超过2的31次方-1
- 增删的时候,需要数组的拷贝复制(navite 方法由C/C++实现)
LinkedList
- 双向链表实现
Vector
- 底层是数组,现在很少用,被ArrayList取代
- 所有方法都是同步,有性能损失
- 初始长度10,增长2倍,比ArrayList更耗内存