Iterator是一个迭代器接口,它提供了一种安全的遍历集合元素的方法,可以避免在遍历过程中修改集合引起的ConcurrentModificationException异常,同时还可以避免在遍历过程中删除集合元素时出现索引越界等问题。
ArrayList使用Iterator遍历集合时,可以使用hasNext()方法判断是否还有元素,使用next()方法获取下一个元素,使用remove()方法安全地删除当前元素。因此,使用Iterator遍历ArrayList既方便又安全,是一种非常推荐的遍历方式。
另外,Iterator接口是Java集合框架中的一部分,实现Iterator接口可以使ArrayList更加符合集合框架的统一标准,方便与其他集合类一起使用。
AbstractList是List接口的一个抽象实现,实现了List接口的大部分方法,包括添加、删除、获取元素、遍历等。
ArrayList作为List接口的一个具体实现,需要实现List接口中定义的所有方法。
通过继承AbstractList抽象类,ArrayList可以重用AbstractList中已经实现的方法,减少重复代码,提高代码的复用性和可维护性。
另外,AbstractList还提供了一些抽象方法,例如get()、set()、add()、remove()等,这些抽象方法需要ArrayList子类实现,从而使得ArrayList具备列表的基本操作。
AbstractList还提供了一些模板方法,例如addAll()、removeAll()等,这些方法可以通过调用抽象方法实现具体的操作。通过继承AbstractList抽象类,ArrayList可以利用这些模板方法快速实现各种列表操作,提高了开发效率。
综上所述,ArrayList实现了AbstractList抽象类是为了重用AbstractList中已经实现的方法,提高代码的复用性和可维护性,同时也能够利用AbstractList中提供的模板方法快速实现各种列表操作。
ArrayList实现了RandomAccess接口,是为了提高随机访问的效率。RandomAccess接口是一个标记接口,用于表示实现该接口的集合支持快速随机访问,即可以通过下标直接访问集合中的元素,而不需要通过迭代器进行遍历。
对于ArrayList来说,它是一个基于数组实现的列表,因此可以通过下标直接访问数组中的元素。实现RandomAccess接口可以让ArrayList在随机访问时使用高效的数组访问方式,从而提高随机访问的效率。
如果一个集合没有实现RandomAccess接口,那么在进行随机访问时,会通过迭代器遍历集合中的元素,这个过程比直接访问数组的效率要低。因此,在需要频繁进行随机访问的情况下,实现RandomAccess接口可以大大提高访问效率。
需要注意的是,实现RandomAccess接口并不一定能提高集合的效率,它只是表明该集合支持快速随机访问的特性,具体效率的提升还要看具体的实现。
Cloneable接口是一个标记接口,用于表示实现该接口的对象可以进行克隆操作。
在ArrayList中,克隆操作可以用于创建一个与原列表相同的新列表,这个新列表与原列表相互独立,对新列表的修改不会影响到原列表。这在某些场景下非常有用,例如需要对一个列表进行操作,但是又需要保留原列表不变的情况下,可以先克隆出一个新列表进行操作。
需要注意的是,ArrayList实现Cloneable接口只是表示它支持克隆操作,并不代表它的克隆操作一定是完全正确和安全的。在进行克隆操作时,需要注意可能存在的浅拷贝和深拷贝问题,以及可能会影响到对象的不变性和线程安全性问题等。
因此,在使用ArrayList的克隆操作时,需要仔细考虑其对程序的影响,并进行必要的安全性和正确性检查。
由于ArrayList底层是基于数组实现的,因此查询和随机访问速度非常快,时间复杂度为O(1)。
ArrayList支持在末尾添加和删除元素的操作,时间复杂度为O(1),因此在操作上非常高效。
ArrayList可以存储任何类型的对象,包括基本类型和自定义类型。
ArrayList可以根据需要动态地扩展容量,因此它非常灵活。
由于ArrayList底层是基于数组实现的,因此在进行频繁的插入和删除操作时,需要移动大量的元素,时间复杂度为O(n),开销较大。
由于ArrayList是基于数组实现的,因此在创建ArrayList时需要指定初始容量,如果容量设置过大,就会浪费内存空间。
由于ArrayList不是线程安全的,因此在多线程环境下需要进行额外的同步操作,否则会出现线程安全问题。
当需要查询和删除某个元素时,需要遍历整个ArrayList,时间复杂度为O(n)。
默认初始容量,没什么可说的。
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;
空实例数组。
/**
* Shared empty array instance used for empty instances.
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
默认大小的空实例数组,在第一次调用ensureCapacityInternal方法时会初始化长度为10
/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
存放元素的数组。
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
transient Object[] elementData; // non-private to simplify nested class access
当前数据中有多少元素
/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
private int size;
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
E elementData(int index) {
return (E) elementData[index];
}
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
E elementData(int index) {
return (E) elementData[index];
}
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// 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);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}
1.将modCount修改次数+1。
2. 计算需要移动的元素个数numMoved 。
3. 调用System.arraycopy,开始移动。
4. 将size-1,并将size-1位置的元素赋值为空。(否则存在重复元素)
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
ArrayList<String> list = new ArrayList<String>();
// 添加元素
for (String str : list) {
System.out.println(str);
}
ArrayList<String> list = new ArrayList<String>();
// 添加元素
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
ArrayList<String> list = new ArrayList<String>();
// 添加元素
Iterator<String> it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
当使用迭代器遍历集合时,会使用一个预期修改次数(expectedModCount)变量来记录当前集合的修改次数(modCount)。每次调用next()方法时,都会先检查expectedModCount和modCount是否相等。如果相等,则说明在遍历过程中没有发生并发修改,可以返回下一个元素;如果不相等,则说明在遍历过程中有其他线程进行了修改,就会抛出ConcurrentModificationException异常。
在集合结构发生变化时,会修改modCount的值,这样在遍历过程中就能够检测到并发修改情况。具体来说,当调用add()、remove()、clear()等方法时,会修改modCount的值,如果此时有其他线程正在遍历集合,则会在下一次遍历时检测到并抛出异常。
总的来说,Java集合中的快速失败机制是通过预期修改次数和实际修改次数的比较来检测并发修改的情况。这种机制虽然会增加一定的开销,但能够保证数据的一致性和可靠性。
java.util包下的集合类都是快速失败的,不能在多线程下发生并发修改(迭代过程中被修改)
使用fail-safe机制的集合类可以保证多个线程同时对集合进行读写操作时,不会出现并发修改异常,而且各个线程之间的访问不会相互阻塞。
由于fail-safe机制的集合类在进行读操作时不会阻塞其他线程的访问,因此适合于数据量较小且读操作较多的场景,比如缓存、配置信息等。
由于fail-safe机制的集合类使用了弱一致性迭代器,因此不能保证在迭代过程中能够看到所有修改之后的元素,适合于不要求强一致性的场景。
使用fail-safe机制的集合类可能会对性能产生一定的影响,因此在对性能要求比较高的场景下,应该谨慎选择。同时,在使用fail-safe机制的集合类时,也需要注意它们的线程安全性和一致性问题。