注:详解核心源码 = 逐句解释源码 + 分情况举例说明 + 必要辅助图解
目录
Vector实现类
Vector方法及案例实现
public class VectorTest {
public static void main(String[] args) {
//很多方法和ArrayList一样
Vector vector=new Vector();
vector.add("草莓");
vector.add("香蕉");
vector.add("西瓜");
System.out.println("-----遍历-----");
//Vector 特别的遍历方法是使用枚举器
Enumeration el = vector.elements();
while (el.hasMoreElements()){
String str=(String) el.nextElement();
System.out.printf("%s ",str);
}
System.out.println();
System.out.println("-----firstElement/lastElement-----");
System.out.println((String)vector.firstElement());
System.out.println((String)vector.lastElement());
System.out.println("-----elementAt-----");
System.out.println(vector.elementAt(1));
}
}
//输出结果:
-----遍历-----
草莓 香蕉 西瓜
-----firstElement/lastElement-----
草莓
西瓜
-----elementAt-----
香蕉
Vector的源码分析(JDk1.8)
继承结构和层次关系
//Vector的继承关系和层次结构和ArrayList中的一模一样
public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
......
}
类中的属性
public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
/**
* 保存vector中元素的数组,vector的容量是数组的长度,数组的长度最小值为vector的元素个数。
* The array buffer into which the components of the vector are
* stored. The capacity of the vector is the length of this array buffer,
* and is at least large enough to contain all the vector's elements.
* 任何在vector最后一个元素之后的数组元素是null
* <p>Any array elements following the last element in the Vector are null.
*
* @serial
*/
protected Object[] elementData;
/**
* vector实际元素的个数
* The number of valid components in this {@code Vector} object.
* Components {@code elementData[0]} through
* {@code elementData[elementCount-1]} are the actual items.
*
* @serial
*/
protected int elementCount;
/**
* vector需要自动扩容时增加的容量
* 当vector的实际容量elementCount大于它的容量时,vector自动增加容量
* 当capacityIncrement小于或等于0,vector的容量需要增长时将会成倍增长
* The amount by which the capacity of the vector is automatically
* incremented when its size becomes greater than its capacity. If
* the capacity increment is less than or equal to zero, the capacity
* of the vector is doubled each time it needs to grow.
*
* @serial
*/
protected int capacityIncrement;
/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -2767605614048989439L;
}
构造方法
无参构造 Vector()
//构造一个指定容量为10,自增容量为0的空vector
public Vector() {
this(10);
}
有参构造 Vector(int initialCapacity)
//构造一个指定容量为initialCapacity,自增容量为0的空vector
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
有参构造 Vector(int initialCapacity, int capacityIncrement)
//构造一个指定容量为initialCapacity,自增容量为capacityIncrement的空vector
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
有参构造 Vector(Collection<? extends E> c)
//使用指定的Collection构造vector
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);
}
add方法的源码分析
- Vector和ArrayList的数据结构都是数组,所以操作起来差不多,这里以add方法为例。
add(E)
情况1 无参初始化并添加第1个元素
/*
Vector vector = new Vector();
//调用了这个构造函数函数后
//this.elementData = new Object[initialCapacity]; 即initialCapacity=10,elementData.length=10
//this.capacityIncrement = capacityIncrement; 即capacityIncrement=0
vector.add(xxx);
*/
//方法前面加了synchronized关键字,给该方法加锁了,哪个线程先调用它,其它线程就得等着,属于线程安全
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
/*
进入ensureCapacityHelper(int minCapacity)函数
其中initialCapacity=10,capacityIncrement=0,minCapacity=elementCount +1=0+1=1
*/
//这个方法是异步(也就是能被多个线程同时访问)的,原因是为了让同步方法都能调用到这个检测容量的方法,比如add的同时,另一个线程调用了add的重载方法,那么两个都需要同时查询 容量够不够,所以这个就不需要用synchronized修饰了。因为不会发生线程不安全的问题
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
// minCapacity - elementData.length=1-10 < 0 所以不扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
/*
返回add(E e)函数 elementData[0] = e; elementCount自增 返回true
*/
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
情况2 无参初始化并添加第11个元素
/*
Vector vector = new Vector();
//调用了这个构造函数函数后
//this.elementData = new Object[initialCapacity]; 即initialCapacity=10,elementData.length=10
//this.capacityIncrement = capacityIncrement; 即capacityIncrement=0
vector.add(xxx);
:
:
vector.add(xxx);
vector.add(xxx);当添加第11个元素时
*/
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
/*
添加第11个元素,进入ensureCapacityHelper(int minCapacity)函数
其中initialCapacity=10,capacityIncrement=0,minCapacity=elementCount +1=10+1=11
*/
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
// minCapacity - elementData.length=11-10 > 0 所以grow函数扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
/*
添加第11个元素,进入grow(int minCapacity)函数
此时initialCapacity=10,capacityIncrement=0,minCapacity=11
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;//oldCapacity=10
// 因为capacityIncrement=0 所以newCapacity=10+10=20
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
// newCapacity - minCapacity >0
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
//扩容数组
elementData = Arrays.copyOf(elementData, newCapacity);
}
/*
返回add(E e)函数 elementData[0] = e; elementCount自增 返回true
*/
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
情况3 有参初始化Vector(8,6)并添加第9个元素
/*
Vector vector = new Vector(8,6);
//调用了这个构造函数函数后
//this.elementData = new Object[initialCapacity]; 即initialCapacity=8,elementData.length=8
//this.capacityIncrement = capacityIncrement; 即capacityIncrement=6
vector.add(xxx);
:
:
vector.add(xxx);
vector.add(xxx);当添加第9个元素时
*/
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
/*
添加第9个元素,进入ensureCapacityHelper(int minCapacity)函数
其中initialCapacity=8,capacityIncrement=6,minCapacity=elementCount +1=8+1=9
*/
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
// minCapacity - elementData.length=9-8 > 0 所以grow函数扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
/*
添加第9个元素,进入grow(int minCapacity)函数
此时initialCapacity=8,capacityIncrement=6,minCapacity=9
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;//oldCapacity=8
// 因为capacityIncrement=6>0 所以newCapacity=8+6=14
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
// newCapacity - minCapacity >0
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
//扩容数组
elementData = Arrays.copyOf(elementData, newCapacity);
}
remove方法的源码分析
- Vector的removeAll和retainAll方法有点不一样。
removeAll(Collection<?> c)
//removeAll和retainAll差不多,主要说removeAll
public synchronized boolean removeAll(Collection<?> c) {
return super.removeAll(c);
}
/*
把集合c传给父类的removeAll函数
*/
public boolean removeAll(Collection<?> c) {
//检查该对象是不是空的
Objects.requireNonNull(c);
//默认false
boolean modified = false;
//创建iterator迭代器
Iterator<?> it = iterator();
//while循环一个个元素进行遍历,每次循环如果集合c中包含这个元素就remove掉,modified设置为true
while (it.hasNext()) {
if (c.contains(it.next())) {
it.remove();
modified = true;
}
}
return modified;
}
LinkedList实现类
LinkedList方法及案例实现
public class LinkedListTest {
public static void main(String[] args) {
LinkedList ll=new LinkedList();
Dog dog1=new Dog("小花",3);
Dog dog2=new Dog("跳跳",4);
ll.add(dog1);
ll.add(dog2);
System.out.println("-----addFirst-----");
//addFirst 在列表的首部添加元素
System.out.println("添加前:"+ll);
ll.addFirst(new Dog("小宝",2));
System.out.println("添加后:"+ll);
System.out.println("-----addLast-----");
//addLast 在列表的尾部添加元素
ll.addLast(new Dog("虎仔",6));
System.out.println("添加后:"+ll);
System.out.println("-----getFirst/getLast-----");
//返回列表中第一个/最后一个元素
System.out.println(ll.getFirst());
System.out.println(ll.getLast());
System.out.println("-----removeFirst/removeLast-----");
//删除并返回列表中的第一个/最后一个元素
ll.removeFirst();
System.out.println(ll);
ll.removeLast();
System.out.println(ll);
}
}
class Dog{
private String name;
private int age;
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public String getName() {
return name;
}
}
//输出结果:
-----addFirst-----
添加前:[Dog{name='小花', age=3}, Dog{name='跳跳', age=4}]
添加后:[Dog{name='小宝', age=2}, Dog{name='小花', age=3}, Dog{name='跳跳', age=4}]
-----addLast-----
添加后:[Dog{name='小宝', age=2}, Dog{name='小花', age=3}, Dog{name='跳跳', age=4}, Dog{name='虎仔', age=6}]
-----getFirst/getLast-----
Dog{name='小宝', age=2}
Dog{name='虎仔', age=6}
-----removeFirst/removeLast-----
[Dog{name='小花', age=3}, Dog{name='跳跳', age=4}, Dog{name='虎仔', age=6}]
[Dog{name='小花', age=3}, Dog{name='跳跳', age=4}]
LinkedList的数据结构
- LinkedList底层使用的双向链表结构,双向链表意味着我们可以从头开始正向遍历,或者是从尾开始逆向遍历,并且可以针对头部和尾部进行相应的操作。
LinkedList的源码分析(JDK1.8)
继承关系和层次关系
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
......
}
为什么LinkedList比ArrayList多了一层AbstractSequentialList的抽象类
-
首先,我们先要区分顺序存取和随机存取。
- 随机存取,当存储器中的数据被读取或写入时,所需要的时间与该数据所在的物理地址无关,常见的就是我们平时在用的数组。
- 顺序存取,所需要的时间与该数据所在的物理地址有关,表现为存取第N个数据时,必须先访问前(N-1)个数据,常见的就是我们平时在用的链表。
-
其次,我们要知道抽象的概念,越高层的类比如说Object类就越抽象,越低层的类就越有属于自己的独特的特性。
-
所以,使用AbstractSequentialList抽象类目的是抽象出类似LinkedList这种类的一些共同的方法,方便操作实现。
LinkedList实现了哪些接口
- List接口:列表,add、set、等一些对列表进行操作的方法。
- Deque接口:实现队列的各种特性。
- Cloneable接口:实现了该接口,就可以使用Object.Clone()方法了。
- Serializable接口:能够序列化。
类的属性
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;
//transient 不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会被序列化
}
构造方法
无参构造方法 LinkedList()
public LinkedList() {}
有参构造方法 LinkedList(Collection<? extends E> c)
//将集合c中的各个元素构建成LinkedList链表
public LinkedList(Collection<? extends E> c) {
//调用无参构造函数
this();
//添加集合中所有的元素
addAll(c);
}
内部类(Node)
//Node 就相当于结点
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;
}
}
add方法的源码分析
add(E)
情况1 无参构造并添加第1个元素
/*
LinkedList ll=new LinkedList();
ll.add(“苹果”);
*/
public boolean add(E e) {
linkLast(e);
return true;
}
/*
进入linkLast(e)这个函数
*/
void linkLast(E e) {
//创建一个为l的结点指针,指向last指向的位置
final Node<E> l = last;
//创建一个新的结点,这个结点的值为e,前驱指向l指向的位置,后继指向null
final Node<E> newNode = new Node<>(l, e, null);
//last结点指针指向新结点newNode
last = newNode;
//如果l指向null,first结点指针指向新结点newNode,否则l结点指针的后继指向新结点newNode
if (l == null)
first = newNode;
else
l.next = newNode;
//添加一个节点,size自增
size++;
modCount++;
}
情况2 无参构造并添加第2个元素
/*
LinkedList ll=new LinkedList();
ll.add(“苹果”);
ll.add(“香蕉”);
*/
public boolean add(E e) {
linkLast(e);
return true;
}
/*
添加第2个元素进入linkLast(e)这个函数
*/
void linkLast(E e) {
......
}
addAll(Collection<? extends E>)/addAll(int, Collection<? extends E> )
情况1 将集合c中的各个元素构建成链表添加到空集合linked尾部
/*
LinkedList newLinked=new LinkedList();
newLinked.add("苹果");
newLinked.add("香蕉");
LinkedList linked=new LinkedList();
linked.addAll(newLinked);
*/
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
/*
进入addAll(int index, Collection<? extends E> c)
其中index=size 集合c就是newLinked
*/
public boolean addAll(int index, Collection<? extends E> c) {
//检查index这个是否为合理
checkPositionIndex(index);
//将集合c转换为Object数组 a
Object[] a = c.toArray();
//数组a的长度numNew,也就是由多少个元素
int numNew = a.length;
if (numNew == 0)
return false;//集合c是个空的,直接返回false,什么也不做
//创建两个指针结点pred和succ
Node<E> pred, succ;
//这里index=size succ = null; pred = last = null;
if (index == size) {
succ = null;
pred = last;
} else {
succ = node(index);
pred = succ.prev;
}
//增强for遍历数组a中的元素
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
//将每个元素封装为结点
Node<E> newNode = new Node<>(pred, e, null);
//第一次循环:将"苹果"封装成结点,因为pred=null,所以first指向数据域是"苹果"这个newNode
//第二次循环:将"香蕉"封装成结点,因为pred != null,所以pred的后继指向数据域是"香蕉"这个newNode
if (pred == null)
first = newNode;
else
pred.next = newNode;
//第一次循环:然后pred指向数据域是"苹果"这个newNode
//第二次循环:然后pred指向数据域是"香蕉"这个newNode
pred = newNode;
}
//succ=null,所以pred指向就是last指向,在这里就是指向数据域是"香蕉"这个结点
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}
//增加了几个元素,就增加多少size
size += numNew;
modCount++;
return true;
}
情况2 将集合c中的各个元素构建成链表添加到已经有一个元素的linked集合尾部
/*
LinkedList newLinked=new LinkedList();
newLinked.add("苹果");
newLinked.add("香蕉");
LinkedList linked=new LinkedList();
linked.add("西瓜");
linked.addAll(newLinked);
*/
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
/*
进入addAll(int index, Collection<? extends E> c)
其中index=size 集合c就是newLinked
*/
public boolean addAll(int index, Collection<? extends E> c) {
//检查index这个是否为合理
checkPositionIndex(index);
//将集合c转换为Object数组 a
Object[] a = c.toArray();
//数组a的长度numNew,也就是由多少个元素
int numNew = a.length;
if (numNew == 0)
return false;//集合c是个空的,直接返回false,什么也不做
//创建两个指针结点pred和succ
Node<E> pred, succ;
//这里index=size succ = null; pred = last = 最后一个结点(即数据域为西瓜的结点);
if (index == size) {
succ = null;
pred = last;
} else {
succ = node(index);
pred = succ.prev;
}
//增强for遍历数组a中的元素
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
//将每个元素封装为结点
Node<E> newNode = new Node<>(pred, e, null);
//第一次循环:将"苹果"封装成结点,因为pred != null,所以pred的后继指向数据域是"苹果"这个newNode
//第二次循环:将"香蕉"封装成结点,因为pred != null,所以pred的后继指向数据域是"香蕉"这个newNode
if (pred == null)
first = newNode;
else
pred.next = newNode;
//第一次循环:然后pred指向数据域是"苹果"这个newNode
//第二次循环:然后pred指向数据域是"香蕉"这个newNode
pred = newNode;
}
//succ=null,所以pred指向就是last指向,在这里就是指向数据域是"香蕉"这个结点
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}
//增加了几个元素,就增加多少size
size += numNew;
modCount++;
return true;
}
情况3 将集合c中的各个元素构建成结点依次添加到已经有三个元素的linked集合内部
/*
LinkedList newLinked=new LinkedList();
newLinked.add("苹果");
LinkedList linked=new LinkedList();
linked.add("西瓜");
linked.add("葡萄");
linked.add("香蕉");
linked.addAll(1,newLinked);
*/
//此时index=1
public boolean addAll(int index, Collection<? extends E> c) {
//检查index这个是否为合理
checkPositionIndex(index);
//将集合c转换为Object数组 a
Object[] a = c.toArray();
//数组a的长度numNew,也就是由多少个元素
int numNew = a.length;
if (numNew == 0)
return false;//集合c是个空的,直接返回false,什么也不做
//创建两个指针结点pred和succ
Node<E> pred, succ;
//这里index!=size succ = null; pred = last = 最后一个结点(即数据域为西瓜的结点);
if (index == size) {
succ = null;
pred = last;
} else {
succ = node(index); //node函数见最下面,返回索引为index的这个结点,然后让succ指向这个结点
pred = succ.prev; //pred指向succ前一个结点
}
//增强for遍历数组a中的元素
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
//将每个元素封装为结点
Node<E> newNode = new Node<>(pred, e, null);
//第一次循环:将"苹果"封装成结点,因为pred != null,所以pred的后继指向数据域是"苹果"这个newNode
if (pred == null)
first = newNode;
else
pred.next = newNode;
//第一次循环:然后pred指向数据域是"苹果"这个newNode
pred = newNode;
}
//succ!=null,所以pred的后继指向succ指向的位置,succ的前驱指向pred指向的位置
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}
//增加了几个元素,就增加多少size
size += numNew;
modCount++;
return true;
}
//node函数
Node<E> node(int index) {
// assert isElementIndex(index);
// 判断插入的位置在链表前半段或者是后半段
// index < (size >> 1)就是index<size/2
if (index < (size >> 1)) { //插入位置在前半段
Node<E> x = first;
for (int i = 0; i < index; i++) //从头结点开始正向遍历
x = x.next;
return x; //返回该结点
} else { //插入位置在后半段
Node<E> x = last;
for (int i = size - 1; i > index; i--) //从尾结点开始反向遍历
x = x.prev;
return x; //返回该结点
}
}
在addAll函数中,为什么要先转化为数组再进行遍历,而不是直接遍历集合呢?
- 如果直接遍历集合的话,那么在遍历过程中需要插入元素,在堆上分配内存空间,修改指针域,这个过程中就会一直占用着这个集合,考虑同步的话,其他线程只能一直等待。
- 如果转化为数组,只需要遍历集合,而遍历集合过程中不需要额外的操作,所以占用的时间相对是较短的,这样就利于其他线程尽快的使用这个集合。说白了,就是有利于提高多线程访问该集合的效率,尽可能短时间的阻塞。
remove方法的源码分析
remove(Object)
public boolean remove(Object o) {
//这里可以看到,linkedList也能存储null
if (o == null) {
//循环遍历链表,直到找到null值,然后使用unlink移除该值。下面的这个else中也一样
//下面for循环的遍历,如果我们要移除的值在链表中存在多个一样的值,
//那么我们会移除index最小的那个,也就是最先找到的那个值
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
unlink(x); //调用unlink函数删除该结点
return true;
}
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
//unlink函数
E unlink(Node<E> x) {
// assert x != null;
// 拿到节点x的三个属性
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
//这里开始往下就进行移除该元素之后的操作
if (prev == null) {
//如果prev为空,说明删除的是头结点,就把first结点指针指向后一个结点
first = next;
} else {
//如果prev不为空,就把要删除结点的前一个结点的后继指向要删除结点的后一个结点
//比如说有1、2、3,就将1.next指向3
prev.next = next;
//让x的前驱指向null
x.prev = null;
}
if (next == null) {
//如果next为空,说明删除的是尾结点,就把last结点指针指向前一个结点
last = prev;
} else {
//如果next不为空,就把要删除结点的后一个结点的前驱指向要删除结点的前一个结点
//比如说有1、2、3,就将3.prev指向1
next.prev = prev;
//让x的后继指向null
x.next = null;
}
//把x节点的值赋为空
x.item = null;
//size减1
size--;
modCount++;
return element;
}
ArrayList、LinkedList、Vector的区别
从存储数据结构分析
-
ArrayList:数组;Vector:数组;LinkedList:双向链表。
-
数组可以根据下标快速查找,所以大部分情况下,查询快,但是如果要进行增删操作的时候,会需要移动修改元素后面的所有元素,所以增删的开销比较大,数组的对增删操作的执行效率低。
-
链表增加和删除元素方便,增加或删除一个元素,仅需处理结点间的引用即可,但是查询不方便,需要一个个对比,无法根据下标直接查找。
从并发安全上分析
- Vector:线程安全;ArrayList:非线程安全;LinkedList:非线程安全。
从继承上分析
- ArrayList和Vector一样,LinkedList比前面两者多了一层AbstractSequentialList的抽象类,抽象出类似LinkedList这种类的一些共同的方法,方便操作实现。
从数据增长分析
-
Vector:默认情况下增长为原数组长度的1倍。
-
ArrayList:默认增长为原数组的1.5倍。
Hi, welcome to JasperのBlog!