Java:制作初始 ArrayList 的本地副本的最快方法?

标签 java arrays arraylist

我的代码要求我在每次调用函数时创建一个大的(301x301x19 项)ArrayList,它有一些初始值(有些是 0,有些是 1,等等)。起始值总是相同的,每次调用函数时都需要将其加载到数组中,以便函数有自己的这些初始值副本来处理。

最初我在每次调用该函数时都重新计算数组,但事实证明那慢得可笑;相反,我现在只计算一次初始数组,并在每次调用该函数时制作它的本地副本(这样我就可以在不更改初始数组值的情况下更改值)。

但是,事实证明,复制数组的速度仍然非常慢(超过 3/4 的计算时间都花在了复制这个数组上)。我尝试了以下方法:

// oldList is an ArrayList<Byte>
ArrayList<Byte> newList = new ArrayList<Byte>(oldList);

// oldList is an ArrayList<Byte>
ArrayList<Byte> newList = new ArrayList<Byte>();
newList.addAll(oldList);

// oldList is a Byte[]
ArrayList<Byte> newList = new ArrayList<Byte>(Arrays.asList(oldList));

所有这些方法对我的应用来说都太慢了;有没有更快的技术来做到这一点,还是我运气不好?

最佳答案

总结:

  1. 旨在设计出复制如此多大型数据结构的需求(我知道这是一个难题)
  2. 避免指针追逐,使用数组而不是 ArrayLists。如果您的对象包含其他对象,请尝试用基元替换它们。这里的最终目的是减少到一个原始数组,例如一个字节数组
  3. 压缩你的数据结构,使用数组,更小的类型;目标是通过复制更少的实际字节获得相同数量的好处
  4. 使用System.arrayCopy
  5. 如果您仍然想走得更快,那么将内存布局和责任从 JVM 中移开并直接使用 sun.misc.Unsafe(也称为“用剪刀运行”)

更改为更容易复制的数据结构,并使用 System.arraycopy 将与您在问题中概述的方法一样快。

System.arraycopy 作为 native 调用实现。大多数 JVM 供应商都会准备一个原生版本,使用原生指令来加速内存复制。

不幸的是,复制大内存区域会在 JVM 中产生意想不到的副作用,主要是围绕垃圾收集器。

  1. 在内存复制期间,JVM 无法访问安全点,这会阻止 STW GC 启动
  2. GC 未启动会导致注意安全点的线程等待更长时间,从而在与此工作无关的线程上造成奇怪的停顿
  3. 大数组可能无法容纳在 TAB(用于加速对象分配的线程本地缓冲区)中,这意味着对象分配在进入特殊情况代码时会减慢
  4. 大对象会增加 GC 周期中过早使用的可能性,这会增加更昂贵的老一代/完整 GC 的频率(与更便宜的年轻一代 GC 相对)

    • 注意:要看到上述效果,我们必须谈论非常高的分配率和丢弃率。大多数在这里和那里进行一些分配和复制的算法不会看到这些问题;现代 JVM 甚至可以处理相当高的速率,这些问题不会发生,直到超过阈值并且我们一直在杆子上旋转的盘子开始落到地板上。

关于Java:制作初始 ArrayList 的本地副本的最快方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26003767/

相关文章:

java - 使用其他类的数组列表和适配器

java - 无法使用 Spring Boot 将 ArrayList 中超过 2 个元素保存到 MySQL?

java - 自定义 JComboBox 上箭头的位置(位于底部中心)

java - 如何从我的 Android 设备中发现 UUID?

java - python 服务器上的 API 参数中提供的 appsecret_proof 无效

C++ .dll 的 Java 等价物?

c - 给定 char *p = "string",为什么修改会导致未定义的行为?

javascript - 使用 NgRepeat 访问匿名数组

java - 将字节数组转换为 IntStream 的最佳方法是什么?

java - 在 Java 中关联两个数组列表中的项目