java 声明数组大小吗_关于数组:Java Arraylist的大小声明和性能

考虑以下Java代码(完整,可编译且运行良好)。

该代码创建一个包含5,000,000个整数(1到5百万)的数组,在其上循环,并创建找到的理想平方的ArrayList。使用天真的技术(而不是位操作)可以检测出完美的平方,但这并不是当前问题的重点。

数学上,介于1到5M之间,有2236个完美正方形。因此,要放入完美正方形的ArrayList的最终大小为2236。

import java.util.ArrayList;

public class PerfSquares {

public static ArrayList perfectSquares(int[] arr) {

ArrayList al = new ArrayList();

//  ArrayList al = new ArrayList(arr.length);

for (int i = 0; i < arr.length; i++) {

double root = Math.sqrt(arr[i]);

int irt = (int) Math.floor(root);

if (irt * irt == arr[i]) {

al.add(arr[i]);

}

}

return al;

}

public static void main(String[] args) {

int[] arr = new int[5000000];

for (int i = 0; i < arr.length; i++) {

arr[i] = i + 1;

}

long s = System.currentTimeMillis();

perfectSquares(arr);

long e = System.currentTimeMillis();

System.out.println(e - s);

}

}

我想重点介绍ArrayList的声明。这两行,其中之一被注释掉:

ArrayList al = new ArrayList();

//ArrayList al = new ArrayList(arr.length);

当我运行第一个声明(没有明确提供的大小)时,我看到的timediff为:

~96 milliseconds.

当我运行第二个声明(显式提供大小)时,timediff增加到:

~105 milliseconds

题:

为什么会这样呢?第二种情况(提供的尺寸)是否应该更快?

根据我的理解,在第一种情况下,当我们在ArrayList创建中省略size参数时,将在后台初始化长度为10的数组。并且当超出此容量时,将分配一个具有更大容量(不确定多少)的新数组,并且将复制以前的元素。

对于2236个元素且未指定初始大小,此"上限已超过-分配新的-复制完-追加更多直到上限"周期应重复多次,从而减慢其速度。

因此,我期望提供的size声明会更快-由于分配只会发生一次,因此不会出现容量超出/创建新阵列和复制的情况。

还是这基本上是因为2236追加到ArrayList上,即使在所有cap-exceeds-copy-over循环之后,仍然比创建大小为5,000,000的ArrayList还要快吗?

仅以System.currentTimeMillis()如此低的精度运行一次该脚本,并获得如此小的差异,将导致毫无意义的结果。执行1000次,然后使用System.nanoTime()开始测量接下来的1000次执行,然后取平均结果。

而不是创建具有完整数组大小的ArrayList,请尝试使用(int) Math.sqrt(arr.size()),它大约是理想平方的数量。如果要确定,请添加一些。

我会考虑在这里使用LinkedList。

@SidCool不好的主意。当我们谈论性能时,LinkedList绝不是一个选择。

@leventov该声明没有任何意义。何时从列表中删除元素呢? LinkedList还慢吗? ArrayList可以更快地访问第n个元素,但除此之外,它几乎没有什么区别,并且将删除或插入项目的链表好处。在没有上下文的情况下说一个比另一个要快是不正确的。

@Aaron:使用LinkedList时,即使追加和迭代也要慢得多(是2加上GC开销)。仅执行一项有益的操作:在Iterator位置移除。那几乎没有。尽管LinkedList有很好的用例,但比大多数人期望的要少得多。

@maaartinus好的插入和插入是两个不同的鱼缸。我只是说" LinkedList绝不是一个选择",这有点笼统的说法:D

@maaartinus尽管我一直在阅读,也许我的反对意见实际上是无效的! kjellkod.wordpress.com/2012/02/25/感谢您的指导。

@Aaron我什至认为将LinkedList添加到标准Java API中是一个错误。 LinkedList仅在非常罕见和特定的情况下才适用。在我6年的Java经验中,我从未使用过它。与TreeMap / Set,Deque和其他数据结构不同。在Github上,LinkedList的使用频率高于TreeMap,TreeSet,Deque和ArrayDeque的总和。真令人失望。

@leventov Ive比使用数组/列表更自然地将其与域模型匹配。 [Java]性能在我倾向于做的工作中很少出现问题,因为数据库访问/网络延迟通常是瓶颈。不过,感谢您的对话-现在让我更清晰了,因为只有领域模型认为它很自然才有意义。

当操作涉及大量插入时,@ leventov LinkedList很重要,因为它是一个双重链接列表。

仅需注意:ArrayList始终使用与2的幂次方相对应的大小进行初始化。当您指定初始大小时,它将实际初始化为下一个更高的2的幂。

您要创建一个500万个数组列表,所以显然它要慢一些。您只需要2236。这很浪费。

例如,如果将数组列表的大小更改为10k,则时差会缩小。

为简化起见,只需以不同的顺序多次尝试此测试-

public static void main(String[] args) {

long timea = System.currentTimeMillis();

// ArrayList al = new ArrayList();

ArrayList al = new ArrayList(5000000);

System.out.println(System.currentTimeMillis() - timea);

}

您会看到将一个数组列表初始化为500万个大约需要10毫秒(在我的Macbook上),而没有默认大小的数组列表几乎是瞬时的。这与您要测试的顺序相同。

这一点也不慢,如果您正确地进行基准测试,最终将导致2%以下的差异。

@ThomasJungblut速度较慢-运行测试看看。

运行卡尺基准测试并亲自查看结果:gist.github.com/thomasjungblut/7196135结果:99.6 ms与101 ms。 拷贝这么小的数字与直接分配更大的数组一样昂贵。

是否所有这些迭代都在同一VM中运行? 如果是这样的话,那就是另一种测试-一次运行一次以上代码,很明显,无论您测试哪个顺序(首先是初始化列表,还是仅是普通列表),大小为5000000的那个代码至少要花至少9倍的时间 十。 OP认为它不直观-原因是分配过多。

首先,您的测量方法存在缺陷。但是,在这些情况下,测量并不容易,因为对于如此大的阵列分配,每次新的分配可能会变慢。

至于您的问题:内存分配(甚至释放)是一项昂贵的操作。并非在您使用new时-如今vm已针对许多小型短期对象进行了优化-但大多数情况下,JVM必须在较低的系统级别上保留/分配(aka malloc())内存。此外,内存分配时间还取决于分配的内存量-您需要的内存越多,花费的时间就越长。

在您的情况下:理想平方的数量很容易计算。只需使用(Math.sqrt(arr.length) + 1)来确定初始ArrayList大小,然后立即获得正确的大小。

因为内存分配通常是一个缓慢的操作。我计算了两种情况下的分配数量和新元素。

在这种情况下

ArrayList al = new ArrayList();

您总共只能分配15个分配给8317个元素。在这种情况下

ArrayList al = new ArrayList(arr.length);

您只分配了5000000个元素。

看起来分配的相对大小很昂贵,而不是分配本身。

在已满的ArrayList上调用add()时,它会自动增长50%。因此它将很快足够大,并且不会有太多的内存分配。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中的数组是一种线性数据结构,它由相同类型的元素组成并按照顺序存储在内存中。Java中的数组可以使用new关键字动态地创建,并且长度是固定的,一旦创建后就不能再改变它的大小Java中的ArrayList是一个动态数组,它可以自动扩容以适应元素的添加和删除。ArrayList实际上是基于数组实现的,它内部维护了一个Object类型的数组来存储元素。当ArrayList的容量不足时,它会根据一定的策略自动扩容数组大小,以容纳更多的元素。 ArrayList的实现原理主要包括以下几个方面: 1. 初始容量和扩容因子:当创建一个ArrayList时,可以指定一个初始容量和一个扩容因子。初始容量指定了ArrayList内部数组的初始大小,扩容因子指定了数组增长的比例。默认情况下,初始容量为10,扩容因子为1.5。 2. 自动扩容:当ArrayList内部数组的容量不足时,它会自动扩容。具体实现是创建一个新的数组,将原数组中的元素复制到新数组中,并将新元素添加到新数组中。 3. 随机访问:由于ArrayList是基于数组实现的,因此它支持随机访问。可以通过索引来快速访问元素,时间复杂度为O(1)。 4. 插入和删除:在ArrayList中插入和删除元素的时间复杂度取决于操作的位置。如果在末尾插入或删除元素,时间复杂度为O(1),否则需要将后面的元素都向后移动,时间复杂度为O(n)。 总之,ArrayListJava中常用的动态数组,它的实现基于数组,并且支持随机访问、自动扩容等特性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值