源码基于jdk 1.7.81
Java 集合可分为 Collection 和 Map 两种体系,下面这是Collection 的继承树,我们可以看出 List 接口和 Set 接口继承自Collection 接口.
List 接口 : 存储有序的,可以重复的元素。
Set 接口:存储无序的,不可重复的元素,相当于我们高中学过的集合。
Vector ArrayList LinkedList 是 List 的主要实现类。下面我们看一下ArrayList 和 LinkedList.
boolean add(E e) | 确保此 collection 包含指定的元素 |
boolean add(Collection extends E> c) | 将指定 collection 中所有元素都添加到此 collection 中 |
void clear() | 移除此 collection 中的所有元素 |
boolean contains(Object o) | 如果此 collection 包含所指定元素,则返回true |
boolean equals(Object o) | 比较此collection 与指定对象是否相等 |
boolean remove(Object o) | 从此 collection 中移除指定元素的单个实例 |
removeAll (Collection> c) | 移除此 collection 中那些也包含在 collection 的元素 |
Object [ ] toArray() | 返回包含此 collection 中所有元素的数组 |
ArrayList 本质上是一个动态数组,可以灵活的设置数组的大小。ArrayList并不是线程安全的,因此一般建议在单线程中使用ArrayList。
ArrayList 继承了 AbstractList 类,实现了List
public class ArrayList extends AbstractList
implements List, RandomAccess, Cloneable, java.io.Serializable
ArrayList 实现了java.io.Serializable 接口,意味着ArrayList 支持序列化,能通过序列化去传输。
ArrayList 实现了Cloneable 接口,说明它重写了clone()方法。
从下面的源码来看,ArrayList本质上是数组来存储元素的,所以也使得ArrayList 具有数组的特性,具有随机存取的特点。
private static final long serialVersionUID = 8683452581122892189L;
private transient Object[] elementData;//这个数组用来存储元素。
private int size;//size 表示当前长度。
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
}
//默认构造函数
public ArrayList() {
this(10);//初始容量为10
}
public ArrayList(Collection extends E> c) {
elementData = c.toArray();//把 C 集合里面的元素传给elementData 数组。
size = elementData.length;
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
ArrayList 有三个构造函数,从 ArrayList() 我们可以看出 集合默认情况下的初始容量为10
调用 ArrayList(int initialCapacity) 构造函数我们可以自定义数组的 初始容量。
ArrayList(Collection extends E> c) 构造函数是将容器数组化处理并将这个数组值赋给Object数组。
1. 增加一个元素
/**
* 增加一个元素
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); 扩容
elementData[size++] = e;
return true;
}
//给指定位置增加一个元素
public void add(int index, E element) {
if (index > size || index < 0) //越界检查
throw new IndexOutOfBoundsException(
"Index: "+index+", Size: "+size);
ensureCapacity(size+1); //判断扩容
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
ArrayList 增加元素和数组增加元素一样,因为 ArrayList 是一个动态数组,所以它的容量也必须随着元素的增加而增加,ensureCapacityInternal(size + 1) 方法就是判断数组容量是否不够,如果数组满了即扩容。
2. 获得指定位置的元素
/**
* 返回指定位置的元素
*/
public E get(int index) {
rangeCheck(index);//合法性检查
return elementData(index);
}
private void RangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(
"Index: "+index+", Size: "+size);
}
ArrayList 可以通过数组的下标来随机的存取元素。当下标小于0或者大于数组的当前元素时抛出一个异常。
3. 返回某元素在集合中第一次出现的位置。
/**
*判断某元素是否包含在集合中
*/
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
public int indexOf(Object o) {
if (o == null) { //元素为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;
}
判断元素是否包含在集合中是通过遍历数组来实现的。对于indexof方法做几点说明:ArrayList中可以存放null元素,indexof是返回elementData数组中值相同的首个元素的下标,indexof中比较方法是equals,而equals是比较元素的值,因此必须对null单独查找。如果未找到该元素则返回-1 。
4. 扩容
public void ensureCapacity(int minCapacity) {
modCount++;
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity) {
Object oldData[] = elementData;
int newCapacity = (oldCapacity * 3)/2 + 1; //新的容量
if (newCapacity < minCapacity)
newCapacity = minCapacity;
//返回一个新的数组,容量为 newCapacity
elementData = Arrays.copyOf(elementData, newCapacity);
}
}
ArrayList 中扩容是元来的 3/2+1 倍,通过Arrays. copyOf()方法返回一个新的数组来实现的。
LinkedList 继承了AbstractSequentialList 的双向链表,实现了List
public class LinkedList
extends AbstractSequentialList
implements List, Deque, Cloneable, java.io.Serializable
LinkedList 继承了Cloneable 接口,重写了clone()方法,可以被克隆。
LinkedList 实现java.io.Serializable接口,这意味着LinkedList支持序列化,能通过序列化去传输。
LinkedList 本质上是用一个双向链表来存储元素的,它比较适合于频繁的插入或者删除元素。
private transient Entry header = new Entry(null, null, null);//头结点
private transient int size = 0;//当前元素个
private static class Entry {
E element;
Entry next;
Entry previous;
Entry(E element, Entry next, Entry previous) {
this.element = element;//元素
this.next = next;//后继
this.previous = previous;//前驱
}
}
header是双向链表的表头,它是双向链表节点所对应的类Entry的实例。Entry中包含成员变量: previous, next, element。其中,previous是该节点的上一个节点,next是该节点的下一个节点,element是该节点所包含的值。 size是双向链表中节点的个数。
//默认构造函数
public LinkedList() {
header.next = header.previous = header;
}
//保存Collection中的全部元素
public LinkedList(Collection extends E> c) {
this();
addAll(c);
}
LinkedList 有两个构造函数,默认构造函数LinkedList() 使得header 节点自己构成了一个双向链表。 LinkedList(Collection extends E> c) 保存了Collection 接口中所有的元素。
1. 增加一个元素在第一位
//在第一位增加一个元素
public void addFirst(E e) {
addBefore(e, header.next);
}
private Entry addBefore(E e, Entry entry) {
//一个新的节点,后继是头结点的后继,前驱是头结点
Entry newEntry = new Entry(e, entry, entry.previous);
newEntry.previous.next = newEntry;
newEntry.next.previous = newEntry;
size++;
modCount++;
return newEntry;
}
2. 增加一个元素在最后一位
/**
* 增加元素在最后一位
*/
public void addLast(E e) {
linkLast(e);
}
void linkLast(E e) {
final Node l = last;
final Node newNode = new Node<>(l, e, null);//一个新的节点,它的前驱是last
last = newNode; //last 指向新的节点
if (l == null) //当前集合为空
first = newNode;//这个新结点就是头结点
else
l.next = newNode;//集合不为空,把last 放在最后。
size++;
modCount++;
}
3. 获取最后一个元素
/**
* 获取最后一位元素
*/
public E getLast() {
if (size==0)
throw new NoSuchElementException();
return header.previous.element;//头结点的前驱
}
4. 获取第一位元素
/**
* 获取第一位元素
*/
public E getFirst() {
if (size==0)
throw new NoSuchElementException();
return header.next.element;//头结点的后继
}
5. 删除第一个元素
/**
* 删除第一个元素
*/
public E removeFirst() {
return remove(header.next);//删除头结点的后继
}
6. 删除最后一个元素
/**
* 删除最后一个元素
*/
public E removeLast() {
return remove(header.previous);//删除头结点的前驱
}
因为 LinkedList 是用链表实现的所以不用考虑扩容的问题。
底层使用双向链表实现的,所有 LinkedList 拥有链表的所有特点。基于LinkedList 的特点,LinkedList 适合于频繁的插入删除.
利用LinkedList 的基本操作
LinkedList 可以作为先进先出的队列
LinkedList 可以作为先进后出的栈
1.通过迭代器来遍历
for(Iterator iter = list.iterator(); iter.hasNext();)
iter.next();
2.通过快速访问来遍历
int size = list.size();
for (int i=0; i
3. 通过 for 循环来遍历
for (Integer integ:list)
;