使用 System.arraycopy(...) 比使用 for 循环复制数组更好吗?
Posted
技术标签:
【中文标题】使用 System.arraycopy(...) 比使用 for 循环复制数组更好吗?【英文标题】:Is it better to use System.arraycopy(...) than a for loop for copying arrays? 【发布时间】:2013-09-05 14:15:22 【问题描述】:我想创建一个新的对象数组,将两个较小的数组放在一起。
它们不能为空,但大小可以为 0。
我无法在这两种方式之间进行选择:它们是等效的还是更有效的一种(例如 system.arraycopy() 复制整个块)?
MyObject[] things = new MyObject[publicThings.length+privateThings.length];
System.arraycopy(publicThings, 0, things, 0, publicThings.length);
System.arraycopy(privateThings, 0, things, publicThings.length, privateThings.length);
或
MyObject[] things = new MyObject[publicThings.length+privateThings.length];
for (int i = 0; i < things.length; i++)
if (i<publicThings.length)
things[i] = publicThings[i]
else
things[i] = privateThings[i-publicThings.length]
唯一的区别是代码的外观吗?
编辑:感谢链接问题,但他们似乎有一个未解决的讨论:
it is not for native types
: byte[], Object[], char[] 真的更快吗?在所有其他情况下,都会执行类型检查,这将是我的情况,因此将是等效的......不是吗?
在另一个链接的问题上,他们说the size matters a lot
,对于大小 >24 system.arraycopy() 获胜,对于小于 10,手动 for 循环更好...
现在我真的很困惑。
【问题讨论】:
arraycopy()
是本机调用,肯定更快。
您是否尝试过对两种不同的实现进行基准测试?
看这里:***.com/questions/2772152/…
你应该选择你认为最易读和将来最容易维护的那个。只有当你确定这是瓶颈的根源时,你才应该改变你的方法。
不要重新发明***!
【参考方案1】:
public void testHardCopyBytes()
byte[] bytes = new byte[0x5000000]; /*~83mb buffer*/
byte[] out = new byte[bytes.length];
for(int i = 0; i < out.length; i++)
out[i] = bytes[i];
public void testArrayCopyBytes()
byte[] bytes = new byte[0x5000000]; /*~83mb buffer*/
byte[] out = new byte[bytes.length];
System.arraycopy(bytes, 0, out, 0, out.length);
我知道 JUnit 测试并不是真正适合进行基准测试,但是 testHardCopyBytes 耗时 0.157 秒完成 和 testArrayCopyBytes 完成耗时 0.086 秒。 我认为这取决于虚拟机,但它看起来好像复制内存块而不是复制单个数组元素。这绝对会提高性能。
编辑: 看起来 System.arraycopy 的表现无处不在。 当使用字符串而不是字节,并且数组很小(大小为 10)时, 我得到了这些结果:
String HC: 60306 ns
String AC: 4812 ns
byte HC: 4490 ns
byte AC: 9945 ns
这是数组大小为 0x1000000 时的样子。看起来 System.arraycopy 肯定会以更大的数组获胜。
Strs HC: 51730575 ns
Strs AC: 24033154 ns
Bytes HC: 28521827 ns
Bytes AC: 5264961 ns
多么奇特! 感谢 Daren 指出引用的复制方式不同。它使这成为一个更有趣的问题!
【讨论】:
感谢您的努力,但您错过了看似关键的点:非本机类型(使用 anythign 创建一个随机类,以便数组包含引用)和大小...似乎较小的数组大小,手动for循环更快。想纠正这个问题吗? 哇哦,你是对的!这很有趣。在这些数组中放置字符串而不是字节会产生巨大的差异:>> >> 那你得到了什么结果?同样有趣的是尝试使用数组大小= 10 ...谢谢! (我希望我在这里有我的 IDE,我在没有编译器的情况下进行编码)。 现在我已经将它们包装在一些 System.nanoTime() 调用中并设置 size = 10 以查看每个需要多少纳秒。看起来对于小的原始数组,循环更好;对于参考,arrayCopy 更好。: >> >> >> >>跨度> 非常有趣的结果!太感谢了!您能否编辑您的答案以包含此内容,我很乐意接受它,因此它始终是所有人看到的第一个……您已经获得了我的投票。 :)【参考方案2】:Arrays.copyOf(T[], int)
更容易阅读。
它在内部使用System.arraycopy()
,这是一个本地调用。
你不能更快地得到它!
【讨论】:
看来你可以依赖很多东西,但是感谢你指出我不知道的功能,而且确实更容易阅读。 :) 是的!正如@Svetoslav Tsolov 所说,这取决于很多事情。我只是想指出 Arrays.copyOfcopyOf
不能总是替换 arraycopy
,但它适合这个用例。
NB 如果您正在考虑性能,那么它不会像 System.arraycopy() 那样快,因为它需要内存分配。如果这是在一个循环中,那么重复分配将导致垃圾收集,这将对性能造成巨大影响。
@PhilippSander 只是为了检查我是不是愚蠢,我在我的游戏循环中添加了代码来复制一个 1MB 数组,这几乎不会触发 GC。使用 Array.copyOf() 我的 DVM 每秒调用 GC 5 次,游戏变得非常滞后。我认为可以肯定地说发生了内存分配。【参考方案3】:
这取决于虚拟机,但 System.arraycopy 应该为您提供最接近本机性能。
我作为嵌入式系统(性能是重中之重)的 Java 开发人员已经工作了 2 年,并且在任何可以使用 System.arraycopy 的地方,我主要使用它/看到它在现有代码中使用。当性能成为问题时,它总是优于循环。 如果性能不是一个大问题,我会选择循环。更容易阅读。
【讨论】:
数组大小和类型(基本与继承)之类的东西似乎会影响性能。 是的,它本身不是“本机性能”,这就是为什么我说我“主要”在可能的地方使用它(你会注意到它主要胜过循环复制)。我猜原因是:当它是一个原始类型的小数组时,“调用成本”大于性能提升。出于同样的原因,使用 JNI 会降低性能 - 本机代码本身很快,但从 java 进程调用它 - 并没有那么多。 小修正,Arrays.copy 不是 JNI,它是内在的。内在函数比 JNI 快得多。 JIT 编译器如何以及何时将其转换为内在函数取决于所使用的 JVM/编译器。Arrays.copy
不存在,Arrays.copyOf
是一个库函数。【参考方案4】:
我没有依赖推测和可能过时的信息,而是使用caliper 运行了一些基准测试。事实上,Caliper 附带了一些示例,包括一个 CopyArrayBenchmark
可以准确测量这个问题!你所要做的就是运行
mvn exec:java -Dexec.mainClass=com.google.caliper.runner.CaliperMain -Dexec.args=examples.CopyArrayBenchmark
我的结果基于 Oracle 的 Java HotSpot(TM) 64 位服务器 VM,1.8.0_31-b13,在 2010 年中期的 MacBook Pro(macOS 10.11.6 和 Intel Arrandale i7,8 GiB RAM)上运行。我认为发布原始计时数据没有用。相反,我会用支持性的可视化来总结结论。
总结:
编写手动for
循环将每个元素复制到新实例化的数组中从来都不是有利的,无论是对于短数组还是长数组。
Arrays.copyOf(<em>array</em>, <em>array</em>.length)
和 <em>array</em>.clone()
都一直很快。这两种技术在性能上几乎相同;您选择哪一个是一个品味问题。
System.arraycopy(<em>src</em>, 0, <em>dest</em>, 0, <em>src</em>.length)
几乎与 Arrays.copyOf(<em>array</em>, <em>array</em>.length)
和 <em>array</em>.clone()
一样快,但并非始终如此。 (请参阅 50000 int
s 的案例。)正因为如此,以及调用的冗长,如果您需要精确控制哪些元素被复制到哪里,我建议您使用 System.arraycopy()
。
以下是时序图:
【讨论】:
int 复制有什么奇怪的地方吗?奇怪的是,它 arraycopy 在大规模整数上会很慢。【参考方案5】:Arrays.copyOf
不可能比System.arraycopy
快,因为这是copyOf
的实现:
public static int[] copyOf(int[] original, int newLength)
int[] copy = new int[newLength];
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
【讨论】:
【参考方案6】:执行像Arrays.copyOf(T[], int)
这样的本机方法确实有一些开销,但这并不意味着它不如使用JNI 执行它快。
最简单的方法是编写基准测试。
您可以检查Arrays.copyOf(T[], int)
是否比正常的for
循环更快。
来自here的基准代码:-
public void test(int copySize, int copyCount, int testRep)
System.out.println("Copy size = " + copySize);
System.out.println("Copy count = " + copyCount);
System.out.println();
for (int i = testRep; i > 0; --i)
copy(copySize, copyCount);
loop(copySize, copyCount);
System.out.println();
public void copy(int copySize, int copyCount)
int[] src = newSrc(copySize + 1);
int[] dst = new int[copySize + 1];
long begin = System.nanoTime();
for (int count = copyCount; count > 0; --count)
System.arraycopy(src, 1, dst, 0, copySize);
dst[copySize] = src[copySize] + 1;
System.arraycopy(dst, 0, src, 0, copySize);
src[copySize] = dst[copySize];
long end = System.nanoTime();
System.out.println("Arraycopy: " + (end - begin) / 1e9 + " s");
public void loop(int copySize, int copyCount)
int[] src = newSrc(copySize + 1);
int[] dst = new int[copySize + 1];
long begin = System.nanoTime();
for (int count = copyCount; count > 0; --count)
for (int i = copySize - 1; i >= 0; --i)
dst[i] = src[i + 1];
dst[copySize] = src[copySize] + 1;
for (int i = copySize - 1; i >= 0; --i)
src[i] = dst[i];
src[copySize] = dst[copySize];
long end = System.nanoTime();
System.out.println("Man. loop: " + (end - begin) / 1e9 + " s");
public int[] newSrc(int arraySize)
int[] src = new int[arraySize];
for (int i = arraySize - 1; i >= 0; --i)
src[i] = i;
return src;
System.arraycopy()
使用 JNI(Java 本地接口)来复制数组(或其中的一部分),因此速度非常快,您可以确认here
【讨论】:
这段代码使用 int[] 你可以用 String[] 试试吗(用不同的值初始化:“1”、“2”等,因为它们是不可变的 JNI is very slow。System.arraycopy
不使用它。
不,System.arraycopy
不使用 JNI,它仅用于调用第三方库。相反,它是一个原生调用,这意味着在 VM 中有一个原生实现。【参考方案7】:
System.arraycopy()
是一个本机调用,它直接在内存中进行复制操作。单个内存副本总是比你的 for 循环快
【讨论】:
我已经读过,对于非本地类型(任何创建的类,比如我的),它可能效率不高……而对于小尺寸(我的情况),手动 for 循环可能会更好。 ..愿意发表评论吗? 确实,System.arraycopy() 有一些开销,因此对于小型数组 (n = ~10),循环实际上更快以上是关于使用 System.arraycopy(...) 比使用 for 循环复制数组更好吗?的主要内容,如果未能解决你的问题,请参考以下文章