1.List
接口
该接口继承了Collection
接口的方法,除此还有一些特殊的扩展接口。
- List是Collection体系中,一个子接口分支,该接口允许插入重复元素,支持通过索引,随机的访问集合中的元素。
- 列表通常允许重复的元素。 更正式地,列表通常允许元素
e1
和e2成对
使得e1.equals(e2)
,并且如果它们允许空元素,它们通常允许多个空元素。 List
接口提供了两种方法来搜索指定的对象。 从性能角度来说,谨慎使用这些方法。 在许多实现中,它们将执行昂贵的线性搜索。List
接口提供了一个特殊的迭代器,称为ListIterator,
其允许元件插入和更换,并且除了该Iterator
接口提供正常操作的双向访问。 提供了一种方法来获取从列表中的指定位置开始的列表迭代器。
public interface List<E> extends Collection<E> {
//返回此列表中指定位置的元素。
E get(int index);
//替换指定位置的元素,index索引是[0,size());
E set(int index, E element);
//将指定的元素插入此列表中的指定位置(可选操作)。 将当前位于该位置的元素(如果有)和任何后续元素(向其索引添加一个)移动。索引是[0,size()],当index=size()时,就是在尾部插入
void add(int index, E element);
//指定元素的下标,没有返回-1
int indexOf(Object o);
//返回列表中的列表迭代器(按适当的顺序)。
ListIterator<E> listIterator();
//从列表中的指定位置开始,返回列表中的元素(按正确顺序)的列表迭代器。 指定的索引表示初始调用将返回的第一个元素为next 。 初始调用previous将返回指定索引减1的元素。
ListIterator<E> listIterator(int index)
}
2.ListIterator<E>
接口
接口是Iterator<E>
的子接口,该接口除了定义实现了接口的类除了有后续迭代和移除的功能外,还支持向前遍历.
public interface ListIterator<E> extends Iterator<E> {
boolean hasNext();
E next();
//是针对于当前元素位置进行判断
boolean hasPrevious();
//返回由后续调用返回的元素的索引next() 。 (如果列表迭代器位于列表的末尾,则返回列表大小。)
E previous();
//返回由后续调用previous()返回的元素的索引。 (如果列表迭代器位于列表的开头,则返回-1)
int previousIndex();
//用指定的元素(可选操作)替换由next()返回的最后一个元素或previous() 。
void set(E e);
}
3.AbstractList<E>
的实现
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
//逆序查找指定元素,支持o为null
public int lastIndexOf(Object o) {
//返回一个指定遍历到指定位置ListIterator对象,然后向前遍历
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;
}
//返回一个元素迭代器
//此实现返回一个简单的实现Iterator接口,依托后台列表的size(),get(int)和remove(int)方法。
//(protected) modCount字段的规范中所描述的那样,这种实现可以面对并发修改来抛出运行时异常。
public Iterator<E> iterator() {
return new Itr();
}
//迭代器的简单实现
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.
* 返回下标,下表是最近一次的next()返回元素的下标。
*/
int lastRet = -1;
//集合中的元素删除,则会改变modCount的数值
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();//fast-fail 检查
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();
}
}
//返回一个可以任意方向遍历的迭代器对象
public ListIterator<E> listIterator() {
return listIterator(0);
}
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();
}
}
}
}
4.ArrayList<E>
的实现
ArrayList<E>
是List接口的具体实现子类,实现了List的所有接口,存储的数据是有序的,并且允许元素重复,以及null值的添加。每一个ArrayList
实例都有一个容量。容量是用于存储列表中的元素的数组的大小。除此之外,该类是非线程同步的。
4.1 成员变量
/**
* Default initial capacity.默认的初始化容量
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* Shared empty array instance used for empty instances.
* 表示空集合
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 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.
* 空list集合元素,如果空集合,将此作为返回元素,表示是元素个数为0,不是集合为null。
* 此时,也有表示空构造方法调用默认的初始赋值。
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
* 默认被赋值为DEFAULTCAPACITY_EMPTY_ELEMENTDATA,当第一次添加元素时,会进行扩容
*/
transient Object[] elementData; // non-private to simplify nested class access
//元素大小
private int size;
/**
* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
* 最多允许存储的元素个数
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
//AbstractList中的变量,for fast-fail
protected transient int modCount = 0;
4.2 构造函数
关于Arrays
和System
参见 :基本使用
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
//表示当前集合大小是0,当集合扩容时,扩容大小选择是 oldCapacity + (oldCapacity >> 1)
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
public ArrayList() {
//表示使用默认的构造函数,集合中的数组是空数组,集合扩容大小是 10
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
在构造函数中,有一个地方需要注意,EMPTY_ELEMENTDATA
和DEFAULTCAPACITY_EMPTY_ELEMENTDATA
使用的区别。这两个常量都表示的是{}
是Object类型的数组,他们这是两个不同的数组对象。对于这两个常量,有不同的作用,常量EMPTY_ELEMENTDATA
纯粹表示的是集合中元素是null,集合中存储数据的elementData
大小是0,如果需要添加元素,则需要按指定扩容规则扩容。DEFAULTCAPACITY_EMPTY_ELEMENTDATA
表示创建集合使用的是默认的构造方法,当第一次添加元素时,扩容的大小是采用默认的大小,就是10。关于扩容的,后面会详细讲解到,下面显示的是哪儿用到了DEFAULTCAPACITY_EMPTY_ELEMENTDATA
。
private static int calculateCapacity(Object[] elementData, int minCapacity) {
//elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA只有采用默认的构造方法成立
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//在数组默认大小和要求最小之间选择较大的
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
4.3 基本方法
//通过size字段维护
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
//集合中是否包含o元素,判断通过 o==null?e==null:o.equals(e);
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
//返回指定元素的下标,不存在返回-1,重写了父类方法
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
//如果a的size够大,存储到a中,否则创建一个新的数组返回
public <T> T[] toArray(T[] a) {
if (a.length < size)
//创建一个新的数组返回,内容是 elementData
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
//elementData元素拷贝到数组a中,从0开始,元素个数为size
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
//获取值,会进行范围检查
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
//范围检查
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
4.4 添加和修改元素
//添加元素
public boolean add(E e) {
//会进行容量检查,modCount也会发生更改
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
//指定位置添加元素,可知index及其以后的元素要后移一位
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
//将下标index的元素和往后元素,拷贝到数组中,从index+1位置开始,长度是移动位数
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;//插入元素
size++;
}
//批量添加元素
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
int numMoved = size - index;
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
4.5 扩容
扩容主要是调用方法 ensureCapacityInternal(size + numNew); // Increments modCount
,分析方法调用。集合是用的扩容是原size的1.5倍。
//确保容量够元素添加使用,保证元素添加,所需要minCapacity最小容量
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
//区分是空集合,还是调用了默认的构造方法,如果是默认的构造方法,指定容量应该为10
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)//检测是否找过int最大值
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;
}
4.7 删除元素
//可以知道数组中删除元素是通过数组前移实现的,list实现也是如此,是通过数组拷贝
//将index+1及其之后的元素,复制到index位置
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;
}
//自行分析
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
}
/*** 批量删除 ***/
//将集合中c的元素删除
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, false);
}
//将非集合c中的元素移除
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, true);
}
//批量删除
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
for (; r < size; r++)
//complement为false,则是移除元素调用的,elementData[w++]存储的是不在数组中元素
//complement为false,则是保留元素调用的,elementData[w++]存储的是在数组中的元素
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
//让GC工作,回收空闲位置
if (w != size) {
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
4.8 迭代元素
都是采用的迭代器的设计模式,实现思路和AbstractList
的iterator()
方法一样,可以参看以前的实现博客。
public ListIterator<E> listIterator() {
return new ListItr(0);
}
public Iterator<E> iterator() {
return new Itr();
}
/****************新增修改 2019年9月3日00:07:51*******************/
5. 补充内容: RandomAccess 接口
public interface RandomAccess {
}
RandomAccess
是一个空接口,这种空接口是起标识作用的,标识实现了这个接口的类具有随机访问功能。
在Collections.binarySearch()
方法中,它要判断传入的list是否是RamdomAccess
的实例,如果是,调用indexedBinarySearch()
方法,如果不是,那么调用iteratorBinarySearch()
方法。
为什么要这样做:
RandomAccess
的实例是支持随机访问的,通过下标获取元素的值复杂度是O(1) , 所以ArrayList实现了随机访问接口,根据下标获取的元素。但是对于LinkedList
,他也是List的子类,也具有get(index)
方法,但是这种获取的复杂度确实O(n)(是通过遍历元素,直到第index个元素),所以如果排序的时候,通过get获取元素比较,则时间复杂度太大。
public static <T>
int binarySearch(List<? extends Comparable<? super T>> list, T key) {
if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
//RandomAccess实例,通过下标获取元素比较。
return Collections.indexedBinarySearch(list, key);
else
//不支持随机访问,则通过迭代获取元素进行比较。
return Collections.iteratorBinarySearch(list, key);
}
//用于支持随机存储的二分查找,获取中间值数值是通过get(mid)
private static <T>
int indexedBinarySearch(List<? extends Comparable<? super T>> list, T key) {
int low = 0;
int high = list.size()-1;
while (low <= high) {
int mid = (low + high) >>> 1;
Comparable<? super T> midVal = list.get(mid);
int cmp = midVal.compareTo(key);
if (cmp < 0)
low = mid + 1;
else if (cmp > 0)
high = mid - 1;
else
return mid; // key found
}
return -(low + 1); // key not found
}
//不支持随机访问,为避免get()查找浪费,使用ListIterator从头开始查找中间元素
private static <T>
int iteratorBinarySearch(List<? extends Comparable<? super T>> list, T key)
{
int low = 0;
int high = list.size()-1;
ListIterator<? extends Comparable<? super T>> i = list.listIterator();
while (low <= high) {
int mid = (low + high) >>> 1;
Comparable<? super T> midVal = get(i, mid);
int cmp = midVal.compareTo(key);
if (cmp < 0)
low = mid + 1;
else if (cmp > 0)
high = mid - 1;
else
return mid; // key found
}
return -(low + 1); // key not found
}
/**
* Gets the ith element from the given list by repositioning the specified
* list listIterator.
*/
private static <T> T get(ListIterator<? extends T> i, int index) {
T obj = null;
int pos = i.nextIndex();
if (pos <= index) {
do {
obj = i.next();
} while (pos++ < index);
} else {
do {
obj = i.previous();
} while (--pos > index);
}
return obj;
}
总结List遍历方式的选择:
-
实现了
RandomAccess
接口的list,可以通过普通for循环,其次是foreach。 -
未实现
RandomAccess
接口的list,通过迭代器去遍历,或者foreach(也是迭代器),但不可通过for,思考复杂度是O(n2),思考原因 ?