【问题标题】:Java - Efficient way to access an arrayJava - 访问数组的有效方法
【发布时间】:2011-11-15 17:22:12
【问题描述】:

自从我上次使用 Java 编码以来已经有一段时间了,但我需要一点提示。 我们有一个简单的函数 - 注意这是 C:

void update(double *source, double *target, int n) {
      for(int i = 0; i < n; ++i)
            target[i] = source[i] * i; // well, actually a bit more complicated, just some kind of calculation
}

所以,现在我需要用 Java 重新编码这个函数 - 高效。我的问题是:

  • Java 当然没有指针,所以我怎样才能有效地传递数组,而不会由于按值调用而产生大量的内存复制操作
  • 哪种数据结构最适合存储数组

请注意,源和目标是大数组,最多可存储 100 万个元素

【问题讨论】:

    标签: java performance data-structures pass-by-reference


    【解决方案1】:

    在 Java 中几乎是一样的:

    static void update(double[] source, double[] target, int n)
    {
        for (int i = 0; i < n; i++)
            target[i] = source[i] * i;
    }
    

    你不会复制任何记忆。当您将数组传递给此函数时,它会将 按值传递给数组的引用

    通常,Java 按值传递函数参数。但在数组和用户定义类的情况下,您处理的对象始终是引用类型。所以对类和数组的函数调用总是按值传递类/数组引用。

    所以如果你有一个看起来像这样的类:

    class Foo
    {
      int[] A; // For arguments say let's say this contains 1 million items always
    }
    

    你有一个可以调用的函数:

    static void Bar(Foo f)
    {
        ....
    }
    

    它只传递对Foo 的引用,根本不复制数据。

    【讨论】:

    • “在 Java 中它看起来完全一样”。几乎相同,但不完全相同。
    • @Rob 我会说这取决于“看起来”是什么意思......除了轻微的语法差异之外,它对我来说确实“看起来”完全一样。
    • "它通过值传递对数组的引用。" ->该死的完美!
    【解决方案2】:

    数组通过引用传递,(传递引用的值)。所以不会有任何新的数组副本。

    代码将非常相似:

    void update(double source[], double target[], int n)
    {
        for (int i = 0; i < n; i++)
            target[i] = source[i] * i;
    }
    

    “数组的数据结构”是什么意思?数组本身就是一种数据结构。无论如何,您都必须访问 每个 元素以获取您尝试执行的操作类型。所以我猜数组本身就是一个很好的数据结构。你可能想看看ArrayList

    【讨论】:

      【解决方案3】:

      正如其他人已经指出的那样,by-ref / by-value 是 C/C++ 的东西,不适用于 Java。

      现在,除非您正在做一些真正的本地编码,否则将这些数组 C/C++ 传递给/来自 Java:

      鉴于在 C 代码中数组作为指针 (void update(double *source, double *target, int n)) 传递,我假设它的大小是动态的,如果是这样,您在 Java 中的签名应该是 void update(List&lt;Double&gt; source, List&lt;Double&gt; target, int n)。让调用者决定它是ArrayListVectorLinkedList 还是...

      但如果您喜欢 JNI(将这些数组 C/C++ 传递给/来自 Java),那么也许我们需要考虑其他方面。

      【讨论】:

        【解决方案4】:

        Java 规范说 Java 中的所有内容都是按值传递的。 Java 中没有“按引用传递”之类的东西。 但是,不要被这一点所迷惑,内部工作非常复杂,您实际上可以按照自己的方式操作数组。

        Oracle 的 java 教程中的逐字记录:

        引用数据类型参数,例如对象,也被传递到 按价值的方法。这意味着当方法返回时, 传入的引用仍然引用与以前相同的对象。 但是,对象字段的值可以在 方法,如果他们有适当的访问级别。

        Java 按值复制和传递引用,而不是对象。因此,方法操作将改变对象,因为引用指向原始对象。但由于引用是副本,交换会失败。

        使用的代码类似且简单:

        void update(double source[], double target[], int n)
        {
            for (int i = 0; i < n; i++)
                target[i] = source[i] * i;
        }
        

        为了更好地理解我提到的内容,请查看以下问题:Is Java "pass-by-reference" or "pass-by-value"?

        关于数据结构的问题,请使用数组。看看你的 sn-p,很明显你需要随机访问,所以只要坚持好的 ol' 数组..

        【讨论】:

        • 我想知道为什么这个被否决的原因?任何人不同意我在这里提到的内容,请说出来。我很乐意澄清,但我非常确定,我的回答是完全正确的。
        【解决方案5】:

        Java 使用对数组(和其他对象)的引用。引用的值,而不是数组本身,是在方法调用中传递的,其成本类似于 C 指针。如果您不需要动态扩展它们,简单数组是使用最快的数据结构。

        否则,考虑ArrayList<Double>。但是这些在速度和大小上都更加昂贵,因为每个双精度都被“装箱”在双精度对象中。

        第三种选择是使用具有高性能原始集合的库中的相关可调整大小的列表类,例如Trove's TDoubleArrayList

        没有问的一个问题是,Java 是否会使用处理器的任何相关SIMD 功能来实现这样的简单循环。我很高兴你没有,因为我不知道。但我相当有信心,如果它 足够聪明,可以使用它们,那么它只会用于简单的数组。

        【讨论】:

        • 需要注意的是,您需要以大尺寸作为参数来构造它,如果您扩展它,您可能会因分配一个新数组和复制。
        【解决方案6】:

        Java 使用按对象调用语义,因此没有复制。

        【讨论】:

        • 不!不要说 Java 是通过引用传递的,你会被纯粹主义者撕成碎片! Java 是严格按值传递的,但为非基元传递的是引用的值。
        • 希望他们不会在这里闲逛 :)
        • 是什么让你说Java中的对象是通过引用传递的?我不是这方面的专家,但我可以 100% 自信地说你是大错特错! download.oracle.com/javase/tutorial/java/javaOO/arguments.html
        • 问题是,“传递引用”中的“引用”与“引用类型”中的“引用”不同(object 及其子类是) .该死的术语让每一位程序员都感到困惑......
        猜你喜欢
        • 1970-01-01
        • 2013-02-13
        • 2012-02-27
        • 1970-01-01
        • 2018-01-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多