1.ArraryList是什么?
- ArrayList就是数组列表,主要用来装载数据,当我们装载的是基本类型的数据int,long,boolean,short,byte…的时候我们只能存储他们对应的包装类,它的主要底层实现是数组Object[] elementData。
- 与它类似的是LinkedList,和LinkedList相比,它的查找和访问元素的速度较快,但插入,删除的速度较慢。
- ArrayList是线程不安全的,会出问题的部分在于: 1. 增加元素 2. 扩充数组长度;
- 小结:ArrayList底层是用数组实现的存储。查询效率高,增删效率低,线程不安全。
2.ArraryList的大小与扩容
- 有参构造方法(通过构造方法在初始化的时候指定底层数组的大小,实际上并没有初始化数组大小)
/**
* Constructs an empty list with the specified initial capacity.
*
* @param initialCapacity the initial capacity of the list
* @throws IllegalArgumentException if the specified initial capacity
* is negative
*/
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);
}
}
- 无参构造方法(赋值底层数组Object[] elementData为一个默认空数组Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}所以数组容量为0,只有真正对数据进行添加add时,才分配默认DEFAULT_CAPACITY = 10的初始容量。)
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);//这里设置默认容量值
}
return minCapacity;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
数组扩容后长度为原来的1.5倍
/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
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);
}
3.为什么说ArraryList插入删除慢
- 插入位置为n时,会复制数组n后面的数据放到n+1后面,然将插入的元素放到n的位置上。所以当数据量特别大时候,效率就会底下。
/**
* Inserts the specified element at the specified position in this
* list. Shifts the element currently at that position (if any) and
* any subsequent elements to the right (adds one to their indices).
*
* @param index index at which the specified element is to be inserted
* @param element element to be inserted
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);//这里进行复制
elementData[index] = element;
size++;
}
例子:在5的位置插入9
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|
会把从5开始的数据复制为一个数组放到6的位置上
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|
将9插入到5的位置上
1 | 2 | 3 | 4 | 9 | 5 | 6 | 7 | 8 |
---|
- 删除同理,要删除这个数组中的5这个位置,复制一个从6开始到最后的数组,然后把它放到5的位置,5的位置就成功被”删除“了其实就是被覆盖了,给了你被删除的感觉。同理他的效率也低,因为数组如果很大的话,需要复制和移动的位置就大了
4.关于ArrayList的那些事儿
- 都知道ArrayList在foreach中不能对集合进行增加删除操作,否则会抛出异常。
- 在遍历过程中使用一个 modCount 变量(表示修改次数)。集合在被遍历期间如果内容发生变化,就会改变modCount的值。每当迭代器使用hashNext()/next()遍历下一个元素之前,都会检测modCount变量是否为expectedModCount值(在迭代器初始化过程中会将modCount这个值赋给迭代器的expectedModCount),是的话就返回遍历;否则抛出异常,终止遍历。(这就是快速失败机制)
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
- 一个有趣的现象,执行下面代码,程序并不会抛出异常(很神奇吧),这是为什么呢?
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
for (Integer integer : list) {
if(integer.equals(1)){
list.remove(integer);
}
}
这是判断是否进入循环 ,cursor代表当前下标,size代表集合长度
public boolean hasNext() {
return cursor != size;
}
这是因为当元素只有两个,且删除第一个元素后,导致cursor和size刚好相同,循环结束,也就没有异常抛出。
- 循环删除的方法
- 方法1(用fori正序,注意某些情况下会删除漏,只适合删除一个元素的情况)
- 方法2 (fori倒序)
- 方法3 Iterator迭代器删除
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
Iterator iterator = list.iterator();
while (iterator.hasNext()){
if (iterator.next().equals(2)){
iterator.remove();
}
}
- 方法4 用removeif(jdk1.8 Collection以及其子类新加入了removeIf方法)
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.removeIf(s->s.equals(1));
- 数组转list的方法
- 方法1(注意,此方法转换后的list不能进行增删操作,因为实际返回的ArrayList是Arrays的一个静态内部类,该类没有提供增删方法)
Integer[] a =new Integer[]{1,2};
List<Integer> list = Arrays.asList(a);
- 方法2 (支持增删,适用于数据小)
Integer[] a =new Integer[]{1,2};
List<Integer> list = new ArrayList<>(Arrays.asList(a));
- 方法3(最高效)
Integer[] a =new Integer[]{1,2};
List<Integer> list = new ArrayList<>(a.length);
Collections.addAll(list,a);
ArraryList常用方法
- boolean add(E e)
将指定的元素添加到此列表的尾部。
- void add(int index, E element)
将指定的元素插入此列表中的指定位置。
- boolean addAll(Collection c)
按照指定 collection 的迭代器所返回的元素顺序,将该 collection 中的所有元素添加到此列表的尾部。
- boolean addAll(int index, Collection c)
从指定的位置开始,将指定 collection 中的所有元素插入到此列表中。
- void clear()
移除此列表中的所有元素。
- Object clone()
返回此 ArrayList 实例的浅表副本。
- boolean contains(Object o)
如果此列表中包含指定的元素,则返回 true。
- void ensureCapacity(int minCapacity)
如有必要,增加此 ArrayList 实例的容量,以确保它至少能够容纳最小容量参数所指定的元素数。
- E get(int index)
返回此列表中指定位置上的元素。
- int indexOf(Object o)
返回此列表中首次出现的指定元素的索引,或如果此列表不包含元素,则返回 -1。
- boolean isEmpty()
如果此列表中没有元素,则返回 true
- int lastIndexOf(Object o)
返回此列表中最后一次出现的指定元素的索引,或如果此列表不包含索引,则返回 -1。
- E remove(int index)
移除此列表中指定位置上的元素。
- boolean remove(Object o)
移除此列表中首次出现的指定元素(如果存在)。
- protected void removeRange(int fromIndex, int toIndex)
移除列表中索引在 fromIndex(包括)和 toIndex(不包括)之间的所有元素。
- E set(int index, E element)
用指定的元素替代此列表中指定位置上的元素。
- int size()
返回此列表中的元素数。
- Object[] toArray()
按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组。
- T[] toArray(T[] a)
按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组;返回数组的运行时类型是指定数组的运行时类型。
- void trimToSize()
将此 ArrayList 实例的容量调整为列表的当前大小。
- boolean removeIf(Predicate<? super E> filter)
按照一定规则过滤集合中的元素