*本文只是备忘录。
JAVA中List的实现主要有ArrayList、vector、stack、LinkedList,以及一个抽象的类AbstractList。
1、AbstractList
AbstractList实现了List接口的一些实现,子类可以覆盖,也可以直接使用。更多的是提供了一些思维和实现方式。
如:
/**
* {@inheritDoc}
*
* <p>This implementation always throws an
* {@code UnsupportedOperationException}.
*
* @throws UnsupportedOperationException {@inheritDoc}
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E remove(int index) {
throw new UnsupportedOperationException();
}
// Search Operations
/**
* {@inheritDoc}
*
* <p>This implementation first gets a list iterator (with
* {@code listIterator()}). Then, it iterates over the list until the
* specified element is found or the end of the list is reached.
*
* @throws ClassCastException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public int indexOf(Object o) {
ListIterator<E> it = listIterator();
if (o==null) {
while (it.hasNext())
if (it.next()==null)
return it.previousIndex();
} else {
while (it.hasNext())
if (o.equals(it.next()))
return it.previousIndex();
}
return -1;
}
/**
* {@inheritDoc}
*
* <p>This implementation first gets a list iterator that points to the end
* of the list (with {@code listIterator(size())}). Then, it iterates
* backwards over the list until the specified element is found, or the
* beginning of the list is reached.
*
* @throws ClassCastException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public int lastIndexOf(Object o) {
ListIterator<E> it = listIterator(size());
if (o==null) {
while (it.hasPrevious())
if (it.previous()==null)
return it.nextIndex();
} else {
while (it.hasPrevious())
if (o.equals(it.previous()))
return it.nextIndex();
}
return -1;
}
remove方法的含义是让子类来实现该方法,同时通过抛出 UnsupportedOperationException() 来告诉使用者该方法未实现。
indexOf(),lastIndexOf(),提供了具体的实现,当然子类也可以覆盖该方法。
#在来看看集合中一支出现的Iterator和List中的ListIterator,Abstractor也给出了实现方式-----通过私有的内部类。
private class Itr implements Iterator<E> {
/**
* Index of element to be returned by subsequent call to next.
*/
int cursor = 0;
/**
* Index of element returned by most recent call to next or
* previous. Reset to -1 if this element is deleted by a call
* to remove.
*/
int lastRet = -1;
/**
* The modCount value that the iterator believes that the backing
* List should have. If this expectation is violated, the iterator
* has detected concurrent modification.
*/
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size();
}
public E next() {
checkForComodification();
try {
int i = cursor;
E next = get(i);
lastRet = i;
cursor = i + 1;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
private class ListItr extends Itr implements ListIterator<E> {
ListItr(int index) {
cursor = index;
}
public boolean hasPrevious() {
return cursor != 0;
}
public E previous() {
checkForComodification();
try {
int i = cursor - 1;
E previous = get(i);
lastRet = cursor = i;
return previous;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public int nextIndex() {
return cursor;
}
public int previousIndex() {
return cursor-1;
}
public void set(E e) {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.set(lastRet, e);
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
public void add(E e) {
checkForComodification();
try {
int i = cursor;
AbstractList.this.add(i, e);
lastRet = -1;
cursor = i + 1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
通过私有的内部类来实现迭代器,可以避免其他类的使用。
ArayyList、vector、LinkedList都是通过如此实现自己的迭代器。
2、ArrayList
ArrayList提供了一种快速的集合方式,通过索引获取元素速度会很快,插入和删除会比较慢。
为什么索引快,插入、删除慢?下面慢慢分析。
其实ArrayList可以说是对普通数组的封装,提供了一个可以扩容的数组对象。
为什么这么说?我们来看看ArrayList中的数据结构,元素是如何保存的。
/**
* 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 == 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;
ArrayList中提供了一个数组elementData用来保存所有的对象。其实是存在数组中的,所有对ArrayList的操作可以说都是对elementData数组的操作。
这样ArrayList通过索引获取元素实际是通过 数组直接获取元素,这当然快。
get()方法如下:
public E get(int index) {
rangeCheck(index);
return (E) elementData[index];
}
#我们知道数组的大小在创建的时候是需要指定的,但是我们的ArrayList可以不用指定大小,而可以自己根据元素的数量来自动定义大小。这是如何实现的呢?
ArrayList通过动态扩容来实现上面的问题。如何扩容?
首先看看我们new 一个ArrayList的时候发生了什么。看看2个构造方法。
/**
* 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) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
}
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
super();
this.elementData = EMPTY_ELEMENTDATA;
}
上面的2中构造方法,一个提供了初始化的大小,一个没有。如果提供了大小,则初始化存储元素的数组大小为给定的大小;否则,初始化为空数组。
所以当添加元素的时候,当已经超过数组的大小的时候。这个时候就会动态扩容。
如何扩容:
/**
* 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;
}
add方法在数组的最后一位添加元素。
添加的第一步就是扩容,ensureCapacityInternal(size + 1);当然是判断是否需要扩容,如果数组长度大于当前元素的数量,是不需要扩容的。
private void ensureCapacityInternal(int minCapacity) {
if (elementData == EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
minCapacity代表插入元素后,数组最小的容量。
在来看看扩容的方法 grow(minCapacity).
/**
* 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) { int newCapacity = oldCapacity + (oldCapacity >> 1);
// 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;
}
可以看到每次扩容 默认是扩容50%。
int newCapacity = oldCapacity + (oldCapacity >> 1);
而且当容量很小的时候是直接扩容到10(这个10是上面ensureCapacityInternal()方法计算得出的。),很大的时候计算大数组的容量。
默认的最大值是MAX_ARRAY_SIZE ,超过则设置为int最大值。
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
所以当创建ArryayList的时候 最好指定一个大小(可以减少扩容操作,不知道大小的时候,给个默认值值10吧)。
要注意,每次扩容后之后会将对象复制到新的数组。
elementData = Arrays.copyOf(elementData, newCapacity);
#另外一个add方法:在指定位置添加元素。
/**
* 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++;
}
这个方法首先进行的是扩容。然后将源数组中index之后的元素,放到新数组index+1之后,这样index的位置就空出来了。然后elementData[index] = element; 在index位置插入数据。
总结:新增的时候包括以下步骤,扩容、数组的copy,赋值。所以说插入数据和删除比较慢。(删除同样涉及到数组的copy)
#删除方法:
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;
}
/**
* Removes the first occurrence of the specified element from this list,
* if it is present. If the list does not contain the element, it is
* unchanged. More formally, removes the element with the lowest index
* <tt>i</tt> such that
* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>
* (if such an element exists). Returns <tt>true</tt> if this list
* contained the specified element (or equivalently, if this list
* changed as a result of the call).
*
* @param o element to be removed from this list, if present
* @return <tt>true</tt> if this list contained the specified element
*/
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;
}
注意:list循环的时候,删除元素之后,注意索引的值,避免出现数组越界。
当然for each也会出现问题(一开始以为不会出现问题,应为forEach是使用的Iterator来处理)。但是iterator.next在获取的时候需要检验iterator的改变次数和list的改变次数进行对比。如下代码
public Iterator<E> iterator() {
return new Itr();
}
/**
* An optimized version of AbstractList.Itr
*/
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;//创建iterator的时候已经赋值。
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
}
结论:使用Iterator的时候,是不能在对ArrayLIst进行add,remove操作。不应该将ArrayList和Iterator混合使用。
而且foreach可以删除倒数第二个元素而不出错。应为没for循环的时候,会通过hasNext来判断是否有下个元素。代码如上。
public boolean hasNext() {
return cursor != size;
}
倒数第二个元素删除之后,size正好等于cursor。
int length = list.size();
//错误的写法,数组越界
for(int i=0;i<length;i++){
list.remove(i);
}
//错误,
for(Object o : list){
list.remove(o);
}