Java ArrayList的性能
我试图了解将Java ArrayList的性能,java,performance,Java,Performance,我试图了解将ArrayList预初始化为给定容量与使用默认容量和按需扩展之间的性能差异。只是出于好奇。我发现默认容量数组代码比将数组初始化为所需容量的代码快约10%。以下是我使用的代码: public class Test { public static void main(String[] args) { long t1 = System.currentTimeMillis(); for(int j=0;j<10;++j) {
ArrayList
预初始化为给定容量与使用默认容量和按需扩展之间的性能差异。只是出于好奇。我发现默认容量数组代码比将数组初始化为所需容量的代码快约10%。以下是我使用的代码:
public class Test {
public static void main(String[] args) {
long t1 = System.currentTimeMillis();
for(int j=0;j<10;++j) {
List<Integer> list = new ArrayList<Integer>();
for(int i=0;i<1000000;++i) {
list.add(i);
}
}
long t2 = System.currentTimeMillis();
System.out.println("Time taken: " + (t2-t1)/10.0);
}
}
公共类测试{
公共静态void main(字符串[]args){
long t1=System.currentTimeMillis();
对于(int j=0;j我对此做了一些实验,我的结论是您的基准测试有缺陷。当我修复最明显的问题时,我得到了截然不同的结果。我的时间安排如下:
default list: 74ms
pre-sized list: 54ms
Integer array: 42ms
int array: 9ms
public class Clazz {
static final int N = 1000000;
interface Test {
void test();
}
static final class DfltListTest implements Test {
public void test() {
for (int j = 0; j < 10; ++j) {
List<Integer> list = new ArrayList<Integer>();
for (int i = 0; i < N; ++i) {
list.add(i);
}
}
}
}
static final class SizedListTest implements Test {
public void test() {
for (int j = 0; j < 10; ++j) {
List<Integer> list = new ArrayList<Integer>(N);
for (int i = 0; i < N; ++i) {
list.add(i);
}
}
}
}
static final class IntegerArrayTest implements Test {
public void test() {
for (int j = 0; j < 10; ++j) {
Integer[] arr = new Integer[N];
for (int i = 0; i < N; ++i) {
arr[i] = i;
}
}
}
}
static final class IntArrayTest implements Test {
public void test() {
for (int j = 0; j < 10; ++j) {
int[] arr = new int[N];
for (int i = 0; i < N; ++i) {
arr[i] = i;
}
}
}
}
static void test(Test t, String name) {
final int iter = 11;
final long timings[] = new long[iter];
for (int k = 0; k < iter; ++k) {
long t1 = System.currentTimeMillis();
t.test();
long t2 = System.currentTimeMillis();
timings[k] = t2 - t1;
System.gc();
}
Arrays.sort(timings);
System.out.printf("%s: %dms\n", name, timings[iter / 2]);
}
public static void main(String[] args) {
for (int i = 0; i < 5; ++i) {
test(new DfltListTest(), "default list");
test(new SizedListTest(), "pre-sized list");
test(new IntegerArrayTest(), "Integer array");
test(new IntArrayTest(), "int array");
}
}
}
我已经使用Java1.7.0\u09和-XX:+AggressiveOpts-XX:CompileThreshold=1对它进行了测试
当我使用Java 6测试相同的代码时,排名是相同的,但是默认列表和预先调整大小的列表之间的差异要显著得多。我没有试图理解Java 7的哪些方面使得差异如此之小
有关如何对Java代码进行基准测试的一些提示,请参见让我们做这个实验,测量不同容量列表上单个add()
所需的时间
ArrayList<Integer> list = new ArrayList<Integer>(N);
for(int i=0;i<N;++i)
list.add(new Integer(i)); // how many ns does this take?
显然,用2M容量填写4个列表要比用8M容量填写1个列表快得多
这表明您所观察到的情况是可能的—当列表以较小的容量开始时,运行速度会更快,并且节省的时间比以后的阵列复制开销还要多
但是为什么当容量增加时它会变慢呢?我不确定。也许这与二级缓存有关。也许JVM在分配更大的阵列时有更多的开销
测试代码:
static void test(int N)
{
long t0 = System.nanoTime();
long x = 0;
long t = 0;
while(true)
{
ArrayList<Integer> list = new ArrayList<Integer>(N);
for(int i=0;i<N;++i)
list.add(new Integer(i));
t = System.nanoTime()-t0;
x+=N;
if(t>1000_000_000)
break;
}
System.out.printf("%8s\t%4s%n", N, t/x);
}
public static void main(String[] args)
{
while(true)
for(int N=1000; N<20_000_000; N*=2)
test(N);
}
静态空隙试验(int N)
{
long t0=System.nanoTime();
长x=0;
长t=0;
while(true)
{
ArrayList=新的ArrayList(N);
对于(int i=0;i1000_000_000)
打破
}
System.out.printf(“%8s\t%4s%n”,n,t/x);
}
公共静态void main(字符串[]args)
{
while(true)
对于(int N=1000;首先,这不是一种很好的基准测试方法。您包括JIT预热时间,并且您使用的是currentTimeMillis
而不是nanoTime
。尝试使用-另外,我怀疑您测试的很多时间都花在装箱一百万int
值上。尝试使用不需要创建的东西在每次迭代中创建一个新对象。这只是一个有用的提示:运行两次测试,然后查看第二次运行的结果。第一次运行经常因为VM按需加载而受到污染。我将外部循环更改为运行100次迭代,发现预分配版本的速度大约是原来的两倍。他说,大部分时间都被浪费了装箱,而不是在ArrayList上调整大小。@Raze2dust:除非您已经显著地修复了基准测试的方式,否则我仍然非常怀疑。我强烈建议您改用Caliper。(我也怀疑你还在打拳击,但是如果不看代码就很难判断。只要使用列表和一些文字,就可以把它从等式中去掉。)关于微基准标记的链接不错。使用字符串而不是整数解决了这个“问题”。因此,自动装箱可能与其他基准测试缺陷一起在这里发挥主要作用。您能否更改答案以否定自动装箱效果?它将完成答案,我可以接受。@Raze2dust:完成。int[]
版本比Integer[]快4.5倍
。int更改只会影响普通数组的大小写。是否可以使用其他非自动装箱对象,如字符串或其他对象,以便在数组和列表之间公平竞争?
static void test(int N)
{
long t0 = System.nanoTime();
long x = 0;
long t = 0;
while(true)
{
ArrayList<Integer> list = new ArrayList<Integer>(N);
for(int i=0;i<N;++i)
list.add(new Integer(i));
t = System.nanoTime()-t0;
x+=N;
if(t>1000_000_000)
break;
}
System.out.printf("%8s\t%4s%n", N, t/x);
}
public static void main(String[] args)
{
while(true)
for(int N=1000; N<20_000_000; N*=2)
test(N);
}