每个人都说应该使用矢量因为性能(导致Vector在每次操作和东西之后同步)。我写了一个简单的测试:
import java.util.ArrayList;
import java.util.Date;
import java.util.Vector;
public class ComparePerformance {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<Integer>();
Vector<Integer> vector = new Vector<Integer>();
int size = 10000000;
int listSum = 0;
int vectorSum = 0;
long startList = new Date().getTime();
for (int i = 0; i < size; i++) {
list.add(new Integer(1));
}
for (Integer integer : list) {
listSum += integer;
}
long endList = new Date().getTime();
System.out.println("List time: " + (endList - startList));
long startVector = new Date().getTime();
for (int i = 0; i < size; i++) {
vector.add(new Integer(1));
}
for (Integer integer : list) {
vectorSum += integer;
}
long endVector = new Date().getTime();
System.out.println("Vector time: " + (endVector - startVector));
}
}
结果如下:
List time: 4360
Vector time: 4103
基于此,似乎迭代和阅读的Vector
性能略好一些。也许这是一个愚蠢的问题,或者我做了错误的假设 - 有人可以解释一下吗?
答案 0 :(得分:26)
你写过一个天真的微基准。 JVM上的微博技术是一项非常棘手的业务,并不容易列举所有陷阱,但这里有一些经典的:
System.currentTimeMillis
是不精确的,但您似乎并不知道这种方法(您的new Date().getTime()
是等效的,但速度较慢)。如果您想要正确执行此操作,请查看Oracle的jmh
工具或Google的Caliper。
由于我有兴趣自己看这些数字,这里是jmh
的输出。首先,测试代码:
public class Benchmark1
{
static Integer[] ints = new Integer[0];
static {
final List<Integer> list = new ArrayList(asList(1,2,3,4,5,6,7,8,9,10));
for (int i = 0; i < 5; i++) list.addAll(list);
ints = list.toArray(ints);
}
static List<Integer> intList = Arrays.asList(ints);
static Vector<Integer> vec = new Vector<Integer>(intList);
static List<Integer> list = new ArrayList<Integer>(intList);
@GenerateMicroBenchmark
public Vector<Integer> testVectorAdd() {
final Vector<Integer> v = new Vector<Integer>();
for (Integer i : ints) v.add(i);
return v;
}
@GenerateMicroBenchmark
public long testVectorTraverse() {
long sum = (long)Math.random()*10;
for (int i = 0; i < vec.size(); i++) sum += vec.get(i);
return sum;
}
@GenerateMicroBenchmark
public List<Integer> testArrayListAdd() {
final List<Integer> l = new ArrayList<Integer>();
for (Integer i : ints) l.add(i);
return l;
}
@GenerateMicroBenchmark
public long testArrayListTraverse() {
long sum = (long)Math.random()*10;
for (int i = 0; i < list.size(); i++) sum += list.get(i);
return sum;
}
}
结果:
testArrayListAdd 234.896 ops/msec
testVectorAdd 274.886 ops/msec
testArrayListTraverse 1718.711 ops/msec
testVectorTraverse 34.843 ops/msec
请注意以下事项:
...add
方法中。 JIT编译器使用这个事实而 elides 锁定Vector
方法 - 因此几乎相同的性能; ...traverse
方法中;锁不能被省略,这就是Vector
的真正性能损失出现的地方。主要的理由应该是: JVM上的性能模型非常复杂,有时甚至是不稳定的。从微基准测量推断,即使在完全适当的情况下完成,也可能导致对生产系统性能的危险错误预测。
答案 1 :(得分:4)
我同意Marko关于使用Caliper的看法,这是一个非常棒的框架。
但如果你更好地组织基准测试,你可以自己完成一部分工作:
public class ComparePerformance {
private static final int SIZE = 1000000;
private static final int RUNS = 500;
private static final Integer ONE = Integer.valueOf(1);
static class Run {
private final List<Integer> list;
Run(final List<Integer> list) {
this.list = list;
}
public long perform() {
long oldNanos = System.nanoTime();
for (int i = 0; i < SIZE; i++) {
list.add(ONE);
}
return System.nanoTime() - oldNanos;
}
}
public static void main(final String[] args) {
long arrayListTotal = 0L;
long vectorTotal = 0L;
for (int i = 0; i < RUNS; i++) {
if (i % 50 == 49) {
System.out.println("Run " + (i + 1));
}
arrayListTotal += new Run(new ArrayList<Integer>()).perform();
vectorTotal += new Run(new Vector<Integer>()).perform();
}
System.out.println();
System.out.println("Runs: "+RUNS+", list size: "+SIZE);
output(arrayListTotal, "List");
output(vectorTotal, "Vector");
}
private static void output(final long value, final String name) {
System.out.println(name + " total time: " + value + " (" + TimeUnit.NANOSECONDS.toMillis(value) + " " + "ms)");
long avg = value / RUNS;
System.out.println(name + " average time: " + avg + " (" + TimeUnit.NANOSECONDS.toMillis(avg) + " " + "ms)");
}
}
关键部分通常是运行代码。另外,删除与您的基准测试无关的内容。重用Integers而不是创建新的。
上面的基准代码在我的机器上创建了这个输出:
Runs: 500, list size: 1000000
List total time: 3524708559 (3524 ms)
List average time: 7049417 (7 ms)
Vector total time: 6459070419 (6459 ms)
Vector average time: 12918140 (12 ms)
我会说这应该让你了解性能差异。
答案 2 :(得分:2)
正如Marko Topolnik所说,很难编写正确的微基准并正确解释结果。有关这个主题的好文章可用。
根据我的经验以及我对实施的了解,我使用了这个经验法则:
大多数收藏品不包含很多元素,花费更多精力对他们来说是浪费时间。同样在scala中有并行集合,它们并行执行一些操作。也许有一些东西可用于纯Java。
尽可能使用List界面隐藏实现细节,并尝试添加注释,说明您选择特定实现的原因。
答案 3 :(得分:1)
我进行测试,ArrayList比Vector大1000000
更快 public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<Integer>();
Vector<Integer> vector = new Vector<Integer>();
int size= 1000000;
int listSum = 0;
int vectorSum = 0;
long startList = System.nanoTime();
for (int i = 0; i < size; i++) {
list.add(Integer.valueOf(1));
}
for (Integer integer : list) {
listSum += integer;
}
long endList = System.nanoTime();
System.out.println("List time: " + (endList - startList)/1000000);
//
// long startVector = System.nanoTime();
// for (int i = 0; i < size; i++) {
// vector.add(Integer.valueOf(1));
// }
// for (Integer integer : list) {
// vectorSum += integer;
// }
// long endVector = System.nanoTime();
// System.out.println("Vector time: " + (endVector - startVector)/1000000);
}
}
输出运行不同的时间。
Code : list time 83
vector time 113