一:ArrayList
1、ArrayList与数组的联系
数组
- 优点:在内存中时连续的,速度较快,操作简单。
- 缺点:定义数组时要定义其长度,不是很灵活,过长过短都会造成问题。不方便进行数据的添加、插入和移除。
ArrayList
- 优点:ArrayList 底层基于数组,数组在内存中是连续分配的地址空间,是对数组的升级,容量可以动态变化,ArrayList继承了List接口,可以很方便的进行数据的添加、插入和移除。ArrayList中数据可以重复且有序(保证了数据的有序性)、可以向其中存储null值。
- 缺点:当向集合插入不同类型的数据后(ArrayList将数据当作object存储),在进行数据处理时容易出现类型不匹配的错误,使用时需要进行类型转换处理,并且存在装箱与拆箱操作,造成性能大量损耗的现象。
2、ArrayList的特点
ArrayList底层是通过维护了一个Object数组实现的,查询速度快,在中间插入或者删除时,后面的元素需要逐个移动速度比较慢,时间复杂度为 O(n);在尾部插入或者删除时速度比较快,时间复杂度为O(1);但并不是所有的删除都是慢的。
-
ArrayList中数据可以重复且有序(保证了数据的有序性)、可以向其中存储null值。
-
ArrayList实际上是通过一个数组去保存数据的,当我们构造ArrayList时,如果使用默认构造函数,ArrayList的默认容量大小是10。
-
ArrayList实现java.io.Serializable的方式。当写入到输出流时,先写入“容量”,再依次写出“每一个元素”;当读出输入流时,先读取“容量”,再依次读取“每一个元素”。
3、ArrayList源码分析
(1)定义及构造
- 成员变量及其构造方法
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {
};
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
- ArrayList还提供了两种构造方法
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
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;
}
}
- 扩容:当ArrayList容量不足以容纳全部元素时,ArrayList会自动扩张容量,新的容量 = 原始容量 + 原始容量 / 2
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);
}
- 克隆:ArrayList的克隆函数,即是将全部元素克隆到一个数组中
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
(2)具体操作
- 尾部添加元素
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
- 指定下标添加元素
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++;
}
- 向一个集合的末尾增加另一个集合中的全部元素
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
- 向一个集合中的指定位置增加另一个集合中的全部元素
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;
}
- 删除指定位置的元素
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;
}
- 在一个集合中删除与另一个集合的数据相同的元素
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, false);
}
- 在一个集合中删除与另一个集合的数据不相同的元素
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, true);
}
- 更改元素
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
(3)遍历元素
- for循环遍历( get()方法 )
//普通for循环
for(int i = 0;i < list.size();i++){
System.out.println(list.get(i));
}
- foreach循环遍历
//foreach
for(Integer i: list){
System.out.println(i);
}
- Iterator迭代器方法(只能从前向后遍历)
//使用迭代器生成迭代器对象完整对集合的遍历(只能从前向后遍历)
Iterator<Integer> iterator = list.iterator();
//hasNext()方法判断是否还有下一个元素
while (iterator.hasNext()){
System.out.println(iterator.next()); //next()方法获取当前下标元素,并且将下标向后移动
}
- ListIterator迭代器方法(既可以从前向后遍历,也可以从后向前遍历,一般用于后者)
//使用listIterator进行从后向前遍历
ListIterator<Integer> listIterator = list.listIterator(list.size()); //需要给定从哪一个下标开始向前遍历
//hasPrevious()方法判断前面是否还存在元素
while (listIterator.hasPrevious()){
System.out.println(listIterator.previous()); //previous()方法获取当前下标元素,并且将下标向前移动
}
(4)MyArrayList
public class MyArrayList <E>{
private E[] elements;//存放元素的容器
private int size; //实际的元素个数
private final static int defaultCapacity = 5; //默认的初始化参数
public MyArrayList(){
this(defaultCapacity);
}
public MyArrayList(int capacity){
elements = (E[])new Object[capacity];
}
public void add(E value){
//判断是否需要扩容
if(size == elements.length){
//扩容
this.elements = Arrays.copyOf(this.elements, 2*elements.length);
}
//插入元素到数组尾
elements[size++] = value;
}
public boolean remove(E value){
if(size == 0){
return false;
}
for(int i=0; i<size; i++){
if(value.equals(this.elements[i])){
//i位置是所要删除的元素
System.arraycopy(this.elements, i+1, this.elements, i, size-1-i);
this.elements[--size] = null;
return true;
}
}
return false;
}
public boolean set(int index, E value){
try{
checkIndex(index);
elements[index] = value;
return true;
} catch (UnsupportedOperationException e){
System.out.println(e.getMessage());
return false;
}
}
public void checkIndex(int index){
if(index < 0 || index >= elements.length){
throw new UnsupportedOperationException("the index is illegal");
}
}
public E get(int index){
try{
checkIndex(index);
return elements[index];
}catch (UnsupportedOperationException e){
System.out.println(e.getMessage());
return null;
}
}
public boolean contains(E value){
if(size == 0){
return false;
}
for(int i=0; i<size; i++){
if(this.elements[i].equals(value)){
return true;
}
}
return false;
}
public int size(){
return this.size;
}
public boolean isEmpty(){
return this.size == 0;
}
public String toString(){
StringBuilder strs = new StringBuilder();
for(int i=0; i<size; i++){
strs.append(elements[i]+" ");
}
return strs.toString();
}
}
二: LinkedList
1、LinkedList和ArrayList的区别和联系
联系:
- ArrayList和LinkedList都是List接口的实现类,这两个类都是对List进行操作。
- 都是线程不安全的,都是基于fail-fast机制。
区别:
- ArrayList的实现是基于数组,LinkedList的实现是基于双向链表。
- 对于随机访问的get()和set()方法,ArrayList的效率要高于LinkedList,因为LinkedList要移动指针。
- 对于增加和删除操作的add()和remove()方法,LinkedList效率要高于ArrayList,因为ArrayList要移动数据
- LinkedList比ArrayList更占内存,因为LinkedList的节点除了存储数据,还存储了两个引用,一个指向前一个元素,一个指向后一个元素。
2、LinkedList的特点
LinkedList 使用双向链表实现存储(将内存中零散的内存单元通过附加的引用关联起来,形成一个可以按序号索引的线性结构,这种链式存储方式与数组的连续存储方式相比,其实对内存的利用率更高),按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。

- LinkedList是通过双向链表去实现的。
- 从LinkedList的实现方式中可以看出,因为它底层实现是链表,所以它不存在容量不足的问题。
- LinkedList实现java.io.Serializable的方式。当写入到输出流时,先写入“容量”,再依次写出“每一个元素”;当读出输入流时,先读取“容量”,再依次读取“每一个元素”。
- LinkdedList的克隆函数,即是将全部元素克隆到一个新的LinkedList中。
- 由于LinkedList实现了Deque,而Deque接口定义了在双端队列两端访问元素的方法。提供插入、移除和检查元素的方法。
- LinkedList可以作为FIFO(先进先出)的队列,也可以作为LIFO(后进先出)的栈。
- 遍历LinkedList时,使用removeFirst()或removeLast()效率最高。但是用它们遍历会删除原始数据;若只是单纯的取数据,而不删除,建议用迭代器方式或者foreach方式。无论如何,千万不要用随机访问去遍历LinkedList!因为这样的效率非常非常低
3、LinkedList源码分析
(1)定义及构造
- 继承结构
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
- 成员变量
transient int size = 0;
/**
* Pointer to first node.
* Invariant: (first == null && last == null) ||
* (first.prev == null && first.item != null)
*/
transient Node<E> first;
/**
* Pointer to last node.
* Invariant: (first == null && last == null) ||
* (last.next == null && last.item != null)
*/
transient Node<E> last;
- 构造方法
public LinkedList() {
}
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
- 底层结构设计
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
(2)具体操作
- 判断传入的下标是否合法
public void rangeCheck(int index){
if(index < 0 || index > size){
throw new IndexOutOfBoundsException();
}
}
- 获取下标结点
public Node<T> NodeIndex(int index){
if(index < (size >> 1)){
Node<T> node = first;
for(int i = 0;i < index;i++){
node = node.next;
}
return node;
}else {
Node<T> node = last;
for(int i = size - 1;i > index;i--){
node = node.prev;
}
return node;
}
}
- 尾部插入
public void add(T item){
Node<T> l = last;
Node<T> newNode = new Node<>(l,item,null);
last = newNode;
if(l == null){
first = newNode;
}else {
l.next = newNode;
}
size++;
}
- 在指定位置插入元素
public void add(int index, T item){
rangeCheck(index);
if(index == size){
add(item);
return;
}
Node<T> pos = NodeIndex(index);
Node<T> pre = pos.prev;
Node<T> newNode = new Node<>(pre,item,pos);
pos.prev = newNode;
if(pre == null){
first = newNode;
}else {
pre.next = newNode;
}
size++;
}
- 删除指定位置的元素
public void remove(int index){
rangeCheck(index);
if(index == size){
throw new IndexOutOfBoundsException();
}
Node<T> pos = NodeIndex(index);
Node<T> pre = pos.prev;
Node<T> next = pos.next;
pos.item = null; //将元素置空
if(pre == null){
first = next;
}else {
pre.next = next;
pos.prev = null; //若有前驱则将前驱置空
}
if(next == null){
last = pre;
}else {
next.prev = pre;
pos.next = null; //若有后继则将后继置空
}
size--;
}
- 删除指定元素
public void remove(T item){
if(item == null){
int count = 0;
for(Node<T> node = first;node != null;node = node.next){
if(node.item == null){
remove(count);
}
count++;
}
}else {
int count = 0;
for(Node<T> node = first;node != null;node = node.next){
if(node.equals(item)){
remove(count);
}
count++;
}
}
}
- 更改元素
public void set(int index, T item){
rangeCheck(index);
if(index == size){
throw new IndexOutOfBoundsException();
}
Node<T> pos = NodeIndex(index);
pos.item = item;
}
- 获取元素
public T get(int index){
rangeCheck(index);
if(index == size){
throw new IndexOutOfBoundsException();
}
Node<T> pos = NodeIndex(index);
return pos.item;
}
(3)MyLinkedList
public class MyLinkedList<T> implements Iterable<T> {
private Node first;//指向头节点的指针
private Node last;//指向尾节点的指针
private int modCount = 0;//用来记录修改此链表的次数(增、删、查、改等)
private int size = 0;//此链表中元素的个数
//-----------------------------------------------------------------------
/*
*实现迭代器
*/
@Override
public Iterator<T> iterator() {
return new MyLinkedListItr();
}
private class MyLinkedListItr implements Iterator<T> {
private Node<T> cur = first.next;//记录当前位置的指针
private int expectedModCount = modCount;//记录迭代器对集合进行修改的次数
private boolean Removeflag = false;//判断能否进行Remove的标志
@Override
public boolean hasNext() {
return cur != last;
}
@Override
public T next() {
//********迭代期间不允许越过迭代器修改集合元素!
if (modCount != expectedModCount) {
throw new ConcurrentModificationException("你已越过迭代器修改集合元素!");
}
if (!hasNext()) {
throw new NoSuchElementException("没有元素了!");
}
T nextItem = cur.data;
cur = cur.next;
Removeflag = true;
return nextItem;
}
@Override
public void remove() {
if (modCount != expectedModCount) {
throw new ConcurrentModificationException("你已越过迭代器修改集合元素!");
}
if (!Removeflag) {
throw new IllegalStateException();
}
MyLinkedList.this.Remove(cur.prev);
expectedModCount++;
Removeflag = false;//确保在next()后能进行删除操作
}
}
//----------------------------------------------------------------------
//----------------------------------------------------------------------
/*
* 私有的内部类,用来构造节点
* 一个节点包含指向前一个节点的指针和到下一个节点的指针以及包含的数据
*/
private static class Node<T> {
public Node<T> prev;
public Node<T> next;
public T data;
public Node(T data, Node<T> prev, Node<T> next) {
this.prev = prev;
this.next = next;
this.data = data;
}
}
//-----------------------------------------------------------------------
//构建一个空链表
public MyLinkedList() {
clear();
}
public void clear() {
//连接头节点和尾节点,确保链表为空
first = new Node<T>(null, null, null);
last = new Node<T>(null, first, null);
first.next = last;
size = 0;
modCount++;
}
//返回链表中元素的个数
public int getSize() {
return size;
}
//判断链表是否为空
public boolean isEmpty() {
if (size == 0) {
return true;
}
return false;
}
//目标对象的第一个匹配项的索引值;没有,则返回-1
public int indexof(Object obj) {
int index = 0;
if (obj == null) {
for (Node<T> i = first; i != null; i = i.next) {
if (i.data == null) {
return index;
}
index++;
}
} else {
for (Node<T> i = first; i != null; i = i.next) {
if (obj.equals(i.data)) {
return index;
}
index++;
}
}
return -1;
}
//判断此链表是否包含目标元素
public boolean Contains(Object obj) {
return indexof(obj) != -1;
}
//在p指向的非空节点前插入元素x
private void addBefore(Node<T> p, T x) {
assert (x != null);
Node<T> newNode = new Node<>(x, p.prev, p);
newNode.prev.next = newNode;
p.prev = newNode;
size++;
modCount++;
}
//--------------------------------------------------------------------------------------
/*
* 底下的五个私有方法为别的一些方法服务
* */
//将元素x插入到链表的尾端
private void addLast(T x) {
Node<T> l = last;
Node<T> newNode = new Node<>(x, l, null);
last = newNode;
if (l == null) {
first = newNode;
} else {
l.next = newNode;
}
size++;
modCount++;
}
//返回指定元素索引处的(非空)节点。
private Node<T> getNode(int index) {
if (index < 0 || index > getSize() + 1) {
throw new IndexOutOfBoundsException();
}
Node<T> p;
//在前半边查找
if (index < (size >> 1)) {
p = first.next;
for (int i = 1; i < index; i++) {
p = p.next;
}
}
//在后半边查找
else {
p = last;
for (int i = getSize(); i >= index; i--) {
p = p.prev;
}
}
return p;
}
private T Remove(Node<T> p) {
p.next.prev = p.prev;
p.prev.next = p.next;
size--;
modCount++;
return p.data;
}
//检查目标索引是否越界
private void RangeCheck(int index) {
if (index < 0 || index > size) {
throw new IndexOutOfBoundsException("索引越界!");
}
}
public void add(int index, T x) {
addBefore(getNode(index), x);
}
//-------------------------------------------------------------------
//-------------------------------------------------------------------
/*
* 增、删、查、改操作
*/
public boolean add(T x) {
addLast(x);
return true;
}
public T Remove(int index) {
RangeCheck(index);
return Remove(getNode(index));
}
public T set(int index, T newValue) {
RangeCheck(index);
Node<T> p = getNode(index);
T oldValue = p.data;
p.data = newValue;
return oldValue;
}
}
三:Vector
1、Vector和ArrayList的区别联系
相同
- ArrayList类出现于JDK1.2,而Vector类出现于JDK1.0。两者底层的数据存储都使用的Object数组实现,所以都具有查找快,增删慢的特点。
- 继承的类和实现的接口都是一样的,都继承了AbstractList类(继承后可以使用迭代器遍历),实现了RandomAccess(标记接口,标明实现该接口的list支持快速随机访问),cloneable接口(标识接口,合法调用clone方法),serializable(序列化标识接口)。
- Vector类和ArrayList类的底层实现一摸一样,都是允许null值,允许重复,有序的集合。
不同
- 构造方法不同:
ArrayList类的无参构造会首先构造一个空数组,等到添加第一个元素时,会将数组开辟10个空间大小;有参构造则会直接根据指定大小进行开辟数组。
Vector类的无参构造会直接开辟10个空间大小的数组,并将增长因子设置为0;有参构造则可以根据指定大小开辟数组,并且可以指定增长因子的大小。 - 扩容方式不同:
ArrayList类在首次添加元素将数组大小扩大为10个空间之后,以后的扩容操作仅会进行1.5倍扩容。
Vector类中若是不指定增长因子的大小,会进行2倍扩容;若指定了增长因子后,仅仅会扩容增长因子大小个空间,这样可以大大减少空间的浪费。 - Vector类线程安全,ArrayList类线程不安全。
2、Vector源码分析
(1)定义及构造
- 继承结构
public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
}
- 成员变量
protected Object[] elementData;
protected int elementCount;
protected int capacityIncrement;
private static final long serialVersionUID = -2767605614048989439L;
- 构造方法
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
public Vector() {
this(10);
}
public Vector(Collection<? extends E> c) {
elementData = c.toArray();
elementCount = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
}
(2)具体操作
- 扩容机制
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
四:迭代器
迭代器作为一种设计模式,它提供了一种方法顺序访问一个聚合对象中的各个元素,而又无需暴露该对象的内部实现,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据。
1、Iterator源码分析
package java.util;
public interface Iterator<E> {
boolean hasNext();
E next();
void remove();
}
Iterator是作为一个接口存在的,它定义了迭代器所具有的功能。对于这三个方法所实现的功能,字面意义就是了。hasNext()
方法可以判断对象中是否还存在元素,next()
方法可以获取当前元素并移动到下一个元素位置,remove()
方法可以删除当前元素。
ArrayList迭代器源码
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;
Itr() {
}
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];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
2、快速失败机制
上文ArrayList迭代器源码中int expectedModCount = modCount;
这句代码,其实这是集合迭代中的一种“快速失败”机制,这种机制提供迭代过程中集合的安全性。阅读源码就可以知道ArrayList中存在modCount对象,增删操作都会使modCount++,通过两者的对比迭代器可以快速的知道迭代过程中是否存在list.add()类似的操作,存在的话快速失败!
对于快速失败机制而言,它的作用是: 当有线程遍历集合时,如果有线程想对集合进行添加或者删除元素的操作时,遍历集合的线程立马抛出异常,从而遍历集合的线程结束。即不允许遍历方法和添加删除方法同时执行。
3、非快速失败机制
copyOnWriteArrayList非快速失败机制其基本思路是,一开始都在共享一个内容,当要修改这个内容的时候,会把内容Copy出去形成一个新的内容然后再改,这样就不会产生ConcurrentModificationException。
- copyOnWriteArrayList在使用Iterator遍历时,可以用list.add(),list.remove()等改变list的操作,但不支持itr.remove()