目录
1.1 List
1.1.1 ArrayList
1.1.2 Vector
1.1.3 LinkedList
1.1 List
List是常用的数据类型,是一种有序的集合
1.1.1 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);
}
}
当数组长度不能满足存储要求时,ArrayList会创建一个更大的数组并将数组中已有的数据复制到新数组中
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
提供了add()、remove()、get()、size()等功能
ArrayList list = new ArrayList();
list.add("1"); //添加元素
list.add("4");
list.add("2");
list.add("3");
list.add("1");
System.out.println(list);
list.remove(3); //移除第i+1位元素
System.out.println(list);
list.size(); //获得大小
Object o = list.get(2);//获得第i+1位元素的值
System.out.println(o);
[1, 4, 2, 3, 1]
[1, 4, 2, 1]
2
从以上输出结果可以得知ArrayList的元素必须连续存储,所以当需要在某一特定位置插入或删除元素时,需要将待插入(删除)的节点后所有元素向后移动,修改代价高,并不适合随机插入或删除操作,更适合随机查找和遍历。
同时我们查看ArrayList的源码
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
发现,他并不是线程安全的。
综上所述:ArrayList底层由数组实现,实现自动扩容,增删快,查询慢,线程不安全
1.1.2 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;
}
同ArrayList一样同样拥有add()、remove()、get()、size()等功能,扩容也同ArrayList一样。
与ArrayList不同的是,他的方法中用了synchronized关键字修饰,例如
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
说明Vector是线程安全的,保证了多线程下数据的一致性,但由于需要频繁地对Vector实例进行加锁和释放锁,所以Vector的读写效率整体上比ArrayList效率低。
综上所述,Vector和ArrayList一样底层由数组实现,自动扩容,增删慢,查询快,不一样的是Vector线程安全。
1.1.3 LinkedList
底层由双向链表实现,双向链表的每个节点用内部类Node表示。LinkedList通过first和last引用分别指向链表的第一个和最后一个元素。
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;
}
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()、remove()方法
LinkedList list = new LinkedList();
list.add("5");
list.add("4");
list.add("3");
System.out.println(list);
list.add(0,"1");
System.out.println(list);
list.remove("1");
System.out.println(list);
list.remove(1);
System.out.println(list);
[5, 4, 3]
[1, 5, 4, 3]
[5, 4, 3]
[5, 3]
通过上述代码以及结果可见,在对LinkedList进行插入和删除时,数据改动小,因此随机插入和删除的效率高。
LinkedList 底层基于链表结构,无法向 ArrayList 那样随机访问指定位置的元素。LinkedList 查找过程需要从链表头结点(或尾节点)向后查找
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
Node<E> node(int index) {
// assert isElementIndex(index);
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;
}
}
上述代码通过遍历的方式定位目标节点的位置,通过比较index与size/2决定从头节点开始还是从尾结点开始。因此随机访问速度慢。
同时LinkedList提供了List中未定义的方法,用于操作链表头部和尾部,有可能被当作堆栈和对列使用
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return l.item;
}
综上所述LinkedList由双向链表实现,增删快,查询慢,线程不安全