基于jdk1.8
先来一张集合的图片:
集合的关系都在图上面,可以先熟悉一下。
接下来的内容都是基于这张图吧。
当然我们学习要有重点,接下来就看下哪几点?
1、是否允许空
2、是否允许重复数据
3、是否有序,有序的意思是读取数据的顺序和存放数据的顺序是否一致
4、是否线程安全
ArrayList
好的ArrayList是我们常见的集合类,它是一个以数组形式存储数据的集合,接下来看下重要的元素:
元 素 | 作 用 |
private transient Object[] elementData; | ArrayList是基于数组的一个实现,elementData就是底层的数组 |
private int size; | ArrayList里面元素的个数,这里要注意一下,size是按照调用add、remove方法的次数进行自增或者自减的,所以add了一个null进入ArrayList,size也会加1 |
四个关注点在ArrayList上的答案
以后每篇文章在讲解代码前,都会先对于一个集合关注的四个点以表格形式做一个解答:
关 注 点 | 结 论 |
ArrayList是否允许空 |
允许 |
ArrayList是否允许重复数据 | 允许 |
ArrayList是否有序 | 有序 |
ArrayList是否线程安全 | 非线程安全 |
添加元素
有这么一段代码:
public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("000"); list.add("111"); }
看下底层会做什么,进入add方法的源码来看一下:
- public boolean add(E e) {
- ensureCapacityInternal(size + 1); // Increments modCount!!
- elementData[size++] = e;
- return true;
- }
第二步是扩容,先不管,第三步是增加一个元素。
扩容
构造ArrayList的时候,默认的底层数组大小是空
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
那么有一个问题来了,底层数组的大小不够了怎么办?答案就是扩容,这也就是为什么一直说ArrayList的底层是基于动态数组实现的原因,动态数组的意思就是指底层的数组大小并不是固定的,而是根据添加的元素大小进行一个判断,不够的话就动态扩容,扩容的代码就在ensureCapacity里面:
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
如果为空那么初始化为大小为10的数组。
private void ensureExplicitCapacity(int minCapacity) { modCount++;//数据结构发生改变,和fail-fast机制有关,在使用迭代器过程中,只能通过迭代器的方法(比如迭代器中add,remove等),修改List的数据结构, 如果使用List的方法(比如List中的add,remove等),修改List的数据结构,会抛出ConcurrentModificationException //所以循环删除的时候用迭代器。 // overflow-conscious code 当前数组容量不足时进行扩容 if (minCapacity - elementData.length > 0) grow(minCapacity); }
private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); 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); }
如果现在大小不够则会扩容。找到最小的,然后调用到的是Arrays的copyOf方法,将元素组里面的内容复制到新的数组里面去。
删除元素
接着我们看一下删除的操作。ArrayList支持两种删除方式:
1、按照下标删除
2、按照元素删除,这会删除ArrayList中与指定要删除的元素匹配的第一个元素
其实做的事情就是两件:
1、把指定元素后面位置的所有元素,利用System.arraycopy方法整体向前移动一个位置
2、最后一个位置的元素指定为null,这样让gc可以去回收它
技巧:要将list中去重,把对象放入Set中,然后重新赋值就好了。