赞
踩
List集合代表一个有序集合,集合中每个元素都有其对应的顺序索引。List集合允许使用重复元素,可以通过索引来访问指定位置的集合元素。
List接口继承于Collection接口,它可以定义一个允许重复的有序集合。因为List中的元素是有序的,所以我们可以通过使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。
List接口为Collection直接接口。List所代表的是有序的Collection,即它用某种特定的插入顺序来维护元素顺序。用户可以对列表中每个元素的插入位置进行精确地控制,同时可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。
实现List接口的集合主要有:ArrayList、LinkedList、Vector、Stack。
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 == 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
类中的属性:
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { /** * 版本号 */ private static final long serialVersionUID = 8683452581122892189L; /** * 初始化长度 */ private static final int DEFAULT_CAPACITY = 10; /** * 空数组 */ private static final Object[] EMPTY_ELEMENTDATA = {}; /** * 空实例的共享空数组 */ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; /** * 元素数组 */ transient Object[] elementData; // non-private to simplify nested class access /** * 长度 默认为0 * @serial */ private int size; }
构造解析:
/** * 构造具有指定初始容量的空列表。 */ public ArrayList(int initialCapacity) { //初始容量 大于0则 elementData 如上设置成 定长数组 if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; // 如果等于 0 则默认空数组 } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA; //如果为负数 抛出异常 java.lang.IllegalArgumentException: Illegal Capacity: -1 } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } }
/**
* 构造一个初始容量为10的空列表。如何分配10 下面介绍
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/** * 构造一个包含指定元素的列表 */ public ArrayList(Collection<? extends E> c) { // 是将c直接转为Object[] 数组 赋值给elementData elementData = c.toArray(); //如果 elementData 等于0的情况初始化空数组 if ((size = elementData.length) != 0) { // 每个集合的toarray()的实现方法不一样,所以需要判断一下,如果不是Object[].class类型,那么久需要使用ArrayList中的方法去改造一下。 if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else { // replace with empty array. this.elementData = EMPTY_ELEMENTDATA; } }
核心方法:
/**
* 将指定的元素追加到此列表的末尾。
*/
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) { //因为数组如果是空的话,minCapacity=size+1;其实就是等于1,空的数组没有长度就存放不了,所以就将minCapacity变成10,也就是默认大小, if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; } private void ensureExplicitCapacity(int minCapacity) { modCount++; //如上所诉,因为数组如果是空的话,minCapacity=size+1;其实就是等于1,空的数组没有长度就存放不了,所以就将minCapacity变成10,第二次 elementData不是空数组了,也就是minCapacity代表着elementData中增加之后的实际数据个数,拿着它判断elementData的length是否够用,如果length不够用,那么肯定要扩大容量,不然增加的这个元素就会溢出。 //好比我第一次进来我是需要10哥容量,你给我0个 我需要扩容 if (minCapacity - elementData.length > 0) //arrayList能自动扩展大小的关键方法就在这里了 grow(minCapacity); } private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; //获取扩容前elementData的长度 int newCapacity = oldCapacity + (oldCapacity >> 1); //新数组的大小=老数组大小+老数组大小的一半 相当于1.5倍 // 判断上面的扩容之后的大小newCapacity是否够装minCapacity个元素,不够就将数组长度设置为需要的长度 if (newCapacity - minCapacity < 0) newCapacity = minCapacity; //判断新数组容量是否大于最大值 如果新数组容量比最大值(Integer.MAX_VALUE - 8)还大,那么交给hugeCapacity()去处理 if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); //copy数组,扩容机制的核心 // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); } //判断是否抛异常 取最大值还是最大值-8 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 void add(int index, E element) {
rangeCheckForAdd(index);
//跟上面的分析一样
ensureCapacityInternal(size + 1); // Increments modCount!!
//这个方法就是用来在插入元素之后,要将index之后的元素都往后移一位,
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//在目标位置上存放元素
elementData[index] = element;
size++;
}
//校验下标合理 插入的位置肯定不能大于size 和小于0
//如果是,就报这个越界异常
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
无参构造
public static void main(String[] args) {
List arrayList = new ArrayList();
arrayList.add("13");
}
第二次
// 通过删除指定位置上的元素 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); //将--size上的位置赋值为null,让gc(垃圾回收机制)更快的回收它。 elementData[--size] = null; // clear to let GC do its work return oldValue; }
// 通过删除指定位置上的元素 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); //将--size上的位置赋值为null,让gc(垃圾回收机制)更快的回收它。 elementData[--size] = null; // clear to let GC do its work return oldValue; }
//通过遍历元素删除,通过(o == null) 发现arrayList是可以存放null值得。 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; }
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, false);
}
//这个方法,用于两处地方,如果complement为false,则用于removeAll如果为true,则给retainAll()用,retainAll()是用来检测两个集合是否有交集的。 private boolean batchRemove(Collection<?> c, boolean complement) { final Object[] elementData = this.elementData; //将原集合,记名为A int r = 0, w = 0; //r用来控制循环,w是记录有多少个交集 boolean modified = false; try { for (; r < size; r++) //参数中的集合C一次检测集合A中的元素是否有, if (c.contains(elementData[r]) == complement) //有的话,就给集合A elementData[w++] = elementData[r]; } finally { // Preserve behavioral compatibility with AbstractCollection, // even if c.contains() throws. //如果contains方法使用过程报异常 if (r != size) { //将剩下的元素都赋值给集合A, System.arraycopy(elementData, r, elementData, w, size - r); w += size - r; } if (w != size) { //这里有两个用途,在removeAll()时,w一直为0,就直接跟clear一样,全是为null。 //retainAll():没有一个交集返回true,有交集但不全交也返回true,而两个集合相等的时候,返回false,所以不能根据返回值来确认两个集合是否有交集,而是通过原集合的大小是否发生改变来判断,如果原集合中还有元素,则代表有交集,而元集合没有元素了,说明两个集合没有交集。 // clear to let GC do its work for (int i = w; i < size; i++) elementData[i] = null; modCount += size - w; size = w; modified = true; } } return modified; }
//将elementData中每个元素都赋值为null,等待垃圾回收将这个给回收掉,所以叫clear
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
public E get(int index) {
// 检验索引是否合法
rangeCheck(index);
return elementData(index);
}
public E set(int index, E element) {
// 检验索引是否合法
rangeCheck(index);
// 旧值
E oldValue = elementData(index);
// 赋新值
elementData[index] = element;
// 返回旧值
return oldValue;
}
public int indexOf(Object o) {
if (o == null) { //查找元素为空
for (int i = 0; i < size; i++) //循环,找到null返回
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++) // 遍历数组,找到第一个和指定元素相等的元素,返回下标
if (o.equals(elementData[i]))
return i;
}
return -1;
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。