Java中List实现类的性能分析和应用场景(基于JDK1.8)
来源:互联网 发布:手机麻将源码带房卡 编辑:程序博客网 时间:2024/05/17 17:43
List的四个重要实现类
Stack是栈。它的特性是:先进后出(FILO, First In Last Out)。
java工具包中的Stack是继承于Vector(矢量队列)的,由于Vector是通过数组实现的,这就意味着,Stack也是通过数组实现的,而非链表。当然,我们也可以将LinkedList当作栈来使用。
Stack是线程安全的。
Vector 是矢量队列,它是JDK1.0版本添加的类。继承于AbstractList,实现了List, RandomAccess, Cloneable这些接口。
Vector 继承了AbstractList,实现了List;所以,它是一个队列,支持相关的添加、删除、修改、遍历等功能。
Vector 实现了RandmoAccess接口,即提供了随机访问功能。RandmoAccess是java中用来被List实现,为List提供快速访问功能的。在Vector中,我们即可以通过元素的序号快速获取元素对象;这就是快速随机访问。
Vector 实现了Cloneable接口,即实现clone()函数。它能被克隆。
和ArrayList不同,Vector中的操作是线程安全的,Vector存储底层原理是数组。
ArrayList 是一个数组队列,相当于动态数组。与Java中的数组相比,它的容量能动态增长。它继承于AbstractList,实现了List, RandomAccess, Cloneable, java.io.Serializable这些接口。
ArrayList 继承了AbstractList,实现了List。它是一个数组队列,提供了相关的添加、删除、修改、遍历等功能。
ArrayList 实现了RandmoAccess接口,即提供了随机访问功能。RandmoAccess是java中用来被List实现,为List提供快速访问功能的。在ArrayList中,我们即可以通过元素的序号快速获取元素对象;这就是快速随机访问。
ArrayList 实现了Cloneable接口,即覆盖了函数clone(),能被克隆。
ArrayList 实现java.io.Serializable接口,这意味着ArrayList支持序列化,能通过序列化去传输。
和Vector不同,ArrayList中的操作不是线程安全的!所以,建议在单线程中才使用ArrayList,而在多线程中可以选择Vector或者CopyOnWriteArrayList。
LinkedList 是一个继承于AbstractSequentialList的双向链表。它也可以被当作堆栈、队列或双端队列进行操作。
LinkedList 实现 List 接口,能对它进行队列操作。
LinkedList 实现 Deque 接口,即能将LinkedList当作双端队列使用。
LinkedList 实现了Cloneable接口,即覆盖了函数clone(),能克隆。
LinkedList 实现java.io.Serializable接口,这意味着LinkedList支持序列化,能通过序列化去传输。
LinkedList 是非同步的。
List实现类的应用场景
测试demo
package com.anyco.listrush;
import java.util.*;
import java.lang.Class;
/*
* @desc 对比ArrayList、LinkedList、Vector、Stack的插入、随机读取效率、删除的效率
*/
public class ListCompareTest {
private static final int COUNT = 100000;
private static LinkedList linkedList = new LinkedList();
private static ArrayList arrayList = new ArrayList();
private static Vector vector = new Vector();
private static Stack stack = new Stack();
public static void main(String[] args) {
// 换行符
System.out.println();
// 插入
insertByPosition(stack) ;
insertByPosition(vector) ;
insertByPosition(linkedList) ;
insertByPosition(arrayList) ;
// 换行符
System.out.println();
// 随机读取
readByPosition(stack);
readByPosition(vector);
readByPosition(linkedList);
readByPosition(arrayList);
// 换行符
System.out.println();
// 删除
deleteByPosition(stack);
deleteByPosition(vector);
deleteByPosition(linkedList);
deleteByPosition(arrayList);
}
// 获取list的名称
private static String getListName(List list) {
if (list instanceof LinkedList) {
return "LinkedList";
} else if (list instanceof ArrayList) {
return "ArrayList";
} else if (list instanceof Stack) {
return "Stack";
} else if (list instanceof Vector) {
return "Vector";
} else {
return "List";
}
}
// 向list的指定位置插入COUNT个元素,并统计时间
private static void insertByPosition(List list) {
long startTime = System.currentTimeMillis();
// 向list的位置0插入COUNT个数
for (int i=0; i<COUNT; i++)
list.add(0, i);
long endTime = System.currentTimeMillis();
long interval = endTime - startTime;
System.out.println(getListName(list) + " : insert "+COUNT+" elements into the 1st position use time:" + interval+" ms");
}
// 从list的指定位置删除COUNT个元素,并统计时间
private static void deleteByPosition(List list) {
long startTime = System.currentTimeMillis();
// 删除list第一个位置元素
for (int i=0; i<COUNT; i++)
list.remove(0);
long endTime = System.currentTimeMillis();
long interval = endTime - startTime;
System.out.println(getListName(list) + " : delete "+COUNT+" elements from the 1st position use time:" + interval+" ms");
}
// 根据position,不断从list中读取元素,并统计时间
private static void readByPosition(List list) {
long startTime = System.currentTimeMillis();
// 读取list元素
for (int i=0; i<COUNT; i++)
list.get(i);
long endTime = System.currentTimeMillis();
long interval = endTime - startTime;
System.out.println(getListName(list) + " : read "+COUNT+" elements by position use time:" + interval+" ms");
}
}
控制台输出:
Stack : insert 100000 elements into the 1st position use time:1566 ms
Vector : insert 100000 elements into the 1st position use time:1101 ms
LinkedList : insert 100000 elements into the 1st position use time:16 ms
ArrayList : insert 100000 elements into the 1st position use time:1097 ms
Stack : read 100000 elements by position use time:5 ms
Vector : read 100000 elements by position use time:3 ms
LinkedList : read 100000 elements by position use time:6769 ms
ArrayList : read 100000 elements by position use time:2 ms
Stack : delete 100000 elements from the 1st position use time:1103 ms
Vector : delete 100000 elements from the 1st position use time:992 ms
LinkedList : delete 100000 elements from the 1st position use time:8 ms
ArrayList : delete 100000 elements from the 1st position use time:979 ms
可以看到LinkedList插入元素和删除元素时效率极高,而读取元素时效率远低于其他三个集合。
而读取元素时,ArrayList效率达到2ms的级别,但是其插入和删除元素的效率远低于LinkedList。
基于此,我们可以先给出结论。
对于需要快速插入,删除元素,应该使用LinkedList。
对于需要快速随机访问元素,应该使用ArrayList。
对于“单线程环境” 或者 “多线程环境,但List仅仅只会被单个线程操作”,此时应该使用非同步的类(如ArrayList),加锁会降低效率。
对于“多线程环境,且List可能同时被多个线程操作”,此时,应该使用同步的类(如Vector)。
LinkedList和ArrayList性能差异分析
为什么LinkedList修改元素时效率高?
LinkedList添加元素的第一种方法
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
当使用public boolean add(E e)向LinkedList添加方法时,总是在链表末尾将新节点链接上去。
LinkedList添加元素的第二种方法
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
关键在于linkBefore(element, node(index));
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;
}
}
通过add(int index, E element)向LinkedList插入元素时。先是在双向链表中找到要插入节点的位置index;找到之后,再插入一个新节点。
双向链表查找index位置的节点时,有一个加速动作:若index < 双向链表长度的1/2(即判断index < (size >> 1)),则从前向后查找; 否则,从后向前查找。
而ArrayList(再次声明,ArrayList的底层实现是数组)向特定位置添加元素时,使用以下方法
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++;
}
第一个耗时操作 ensureCapacityInternal(size + 1);
先确保数组有足够的空间加入新的元素。关于ArrayList的扩容机制,在ArrayList源码探讨(基于JDK1.8)已有探讨。
扩容函数如下。
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扩容时,必须要经过一次数组的复制过程。
第二个耗时操作System.arraycopy(elementData, index, elementData, index + 1,size - index);这段代码依然来自ArrayList的方法public void add(int index, E element)。这其中又是一次数组的拷贝过程。
为什么ArrayList随机访问效率高?
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
E elementData(int index) {
return (E) elementData[index];
}
可以看到,获取特定位置的元素时,ArrayList直接通过数组下标获取,不需要经过一次遍历过程。
Vector和ArrayList比较
相同点:
①它们都继承于AbstractList,并且实现List接口。
② 它们都实现了RandomAccess和Cloneable接口。
实现RandomAccess接口,意味着它们都支持快速随机访问;
实现Cloneable接口,意味着它们能克隆自己。
③它们都是通过数组实现的,本质上都是动态数组。
④ 它们的默认数组容量是10
不同点:
① 线程安全性不一样
ArrayList是非线程安全;
而Vector是线程安全的,它的函数都是synchronized的,即都是支持同步的。
ArrayList适用于单线程,Vector适用于多线程。
②对序列化支持不同
ArrayList支持序列化,而Vector不支持;即ArrayList有实现java.io.Serializable接口,而Vector没有实现该接口。
③ 容量增加方式不同
ArrayList扩容时是在原来容量的基础上再扩容0.5倍,下面是ArrayList的扩容函数。
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);
}
Vector是在原来的基础上增大capacityIncrement个位置。如果一开始未指定capacityIncrement,那么在原来的基础上增大一倍容量。
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 0
- Java中List实现类的性能分析和应用场景(基于JDK1.8)
- 基于jdk1.8的简单性能监控分析
- java集合框架之List实现类的性能分析
- Java List浅谈(基于jdk1.7)
- 冒泡排序的Java实现、性能分析以及适用场景
- 希尔排序的Java实现、性能分析以及适用场景
- 快速排序的Java实现、性能分析以及适用场景
- 堆排序的Java实现、性能分析以及适用场景
- 归并排序的Java实现、性能分析以及适用场景
- Java 集合系列08之 List总结(LinkedList, ArrayList等使用场景和性能分析)
- Java 集合系列08之 List总结(LinkedList, ArrayList等使用场景和性能分析)
- Java 集合系列之 List总结(LinkedList, ArrayList等使用场景和性能分析)
- Java 集合系列之 List总结(LinkedList, ArrayLis,vector,stack等使用场景和性能分析)
- java集合--List总结(LinkedList, ArrayList等使用场景和性能分析)
- Java 集合系列08之 List总结(LinkedList, ArrayList等使用场景和性能分析)
- Java 集合系列08之 List总结(LinkedList, ArrayList等使用场景和性能分析)
- Java 集合系列08之 List总结(LinkedList, ArrayList等使用场景和性能分析)
- Java【集合系列】-08-List总结(LinkedList, ArrayList等使用场景和性能分析)
- Attempt to invoke virtual method 'boolean java.lang.String.equals(java.lang.Object)' on a null objec
- 深入浅出程序化交易核心技术
- scripts(1-13) of 笨办法学Python
- 51Nod 1126 求递推序列的第N项 矩阵快速幂
- JSON数据格式
- Java中List实现类的性能分析和应用场景(基于JDK1.8)
- 2017.12.05 数据库联表查询
- Python 从入门到实践 6-1 课后习题
- 输入和输出
- url-pattern / 与 /* 的区别
- 深度神经网络全面概述:从基本概念到实际模型和硬件基础
- 策略模式
- 高数 06.03 积分习题课03测试题
- 百度地图