【问题标题】:Passing dynamic list of primitives to a Java method将原语的动态列表传递给 Java 方法
【发布时间】:2011-12-02 00:03:08
【问题描述】:

我需要将原语的动态列表传递给 Java 方法。这可能是 (int, int, float) 或 (double, char) 或其他。我知道这是不可能的,所以我正在考虑解决这个问题的有效方法。

由于我在 Android 上开发游戏,我希望尽可能避免垃圾收集,因此我不想使用任何对象(例如,因为自动装箱),而只想使用原始数据类型。因此,在我的情况下,原始类对象(例如整数)的集合或数组不是一个选项。

所以我在考虑是否可以将一个类对象传递给我的方法,该方法将包含我需要的所有原始值。但是,这既不是我的问题的解决方案,因为如前所述,原语列表是可变的。因此,如果我在我的方法中采用这种方式,那么我将不知道如何访问这个动态的原语列表(至少在没有任何对象转换的情况下,这是我想要避免的)。

现在我感觉有点失落了。我不知道Java中有任何其他可能的方法来解决我的问题。我希望这只是我缺乏知识。你们有谁知道没有对象转换的解决方案?

【问题讨论】:

  • 您确定您的项目没有过早优化吗?您可以传递三种不同类型的三个数组。传递第四个类型指示符数组。然后将值从适当的偏移量拉到给定类型的数组中。
  • 坦率地说我不认为我优化得太快了,因为我已经知道在我的项目中我必须经常调用这个方法。而且我也知道不幸的是,自动(取消)装箱通常会过多地触发垃圾收集器。
  • 你能解释一下为什么你需要这个要求吗?我用 Java 编写了很多游戏,这对我来说从来没有必要,所以我很确定有一种解决方法可以避免分配。
  • @mikera:请在此处的“避免创建不必要的对象”下查看:developer.android.com/guide/practices/design/performance.html

标签: java android object primitive


【解决方案1】:

提供更多背景信息并准确说明您希望使用此技术的目的可能会很有用,因为这可能是决定最佳方法所必需的。

从概念上讲,您正在尝试做一些在托管堆栈上传递参数的任何语言中总是困难的事情。你希望可怜的编译器做什么?它要么允许您将任意数量的参数压入堆栈并使用一些堆栈指针算法访问它们(在 C 中很好,它可以让您随心所欲地使用指针,而在 Java 这样的托管语言中则不太好),或者它会需要在别处传递对存储的引用(这意味着分配或某种形式的缓冲区)。

幸运的是,有几种方法可以在 Java 中进行有效的原始参数传递。以下是我列出的最有前途的方法,大致按照您应该考虑的顺序:

  • 重载 - 有多种方法和不同的原始参数来处理所有可能的组合。如果参数数量相对较少,可能是最好/最简单/最轻量级的选项。性能也很好,因为编译器会静态计算出要调用的重载方法。
  • 原始数组 - 传递任意数量的原始参数的好方法。请注意,您可能需要保留一个原始数组作为缓冲区(否则您必须在需要时分配它,这违背了您避免分配的目标!)。如果您使用部分填充的原始数组,您还需要将偏移量和/或计数参数传递到数组中。
  • 使用原始字段传递对象 - 如果原始字段集事先相对众所周知,则效果很好。请注意,您还必须保留该类的一个实例以充当缓冲区(否则您必须在需要时分配它,这违背了您避免分配的目标!)。
  • 使用专门的原始集合库 - 例如Trove 库。出色的性能并节省您编写大量代码的时间,因为这些通常是精心设计和维护的库。如果这些原语集合将长期存在,那么这是一个不错的选择,也就是说,您创建集合并不是纯粹为了传递一些参数。
  • NIO 缓冲区 - 在性能方面大致相当于使用数组或原始集合。它们有一些开销,但如果您出于其他原因需要 NIO 缓冲区(例如,如果原语在使用相同缓冲区类型的网络代码或 3D 库代码中传递,或者如果数据需要被传递到/从本机代码)。他们还为您处理偏移量和计数,这很有帮助。
  • 代码生成 - 编写代码,为专门的原始方法(提前或动态)生成适当的字节码。这不适合胆小的人,而是获得绝对最佳性能的一种方法。您可能想要使用像 ASM 这样的库,或者选择一种可以轻松为您生成代码的 JVM 语言(想到 Clojure)。

【讨论】:

  • 谢谢,尤其是最后一个想法可能很有趣。由于我正在编写一个框架,它应该并且将在多个应用程序中使用(这就是这个问题的原因),这可能是值得的。我会检查一下!
  • 没问题 - 请注意,最后一种方法非常困难/复杂。如果你不小心,它也会让你的框架的用户生活困难。在你走这条路之前,确保你真的真的需要它!
【解决方案2】:

根本没有。在方法中拥有可变 number 个参数的唯一方法是使用不支持原语的 ... 运算符。所有泛型也只支持原语。

我唯一能想到的就是这样一个类:

class ReallyBadPrimitives {
    char[] chars;
    int[] ints;
    float[] floats;
}

并在添加数组时调整数组的大小。但这真的非常糟糕,因为您基本上失去了系统中的所有引用完整性。

我不会担心垃圾收集——如果你必须的话,我会使用对象和自动装箱来解决你的问题(或者更好的是,避免这种“未知的输入参数集”并得到一个可靠的协议)。一旦你有了一个工作原型,看看你是否遇到了性能问题,然后然后进行必要的调整。您可能会发现 JVM 处理这些对象的能力比您最初想象的要好。

【讨论】:

  • 首先,我无法避免未知的输入参数集,因为我正在编写一个必须支持一组未定义的原始值的框架(这实际上正是这个框架的目的) .其次,不幸的是我不得不担心垃圾收集。在 Android 2.2 之前,如果频繁调用,GC 确实会显着降低快速 OpenGL 应用程序的帧速率。在我的情况下,这会发生,因为在游戏中这个方法经常被调用......
  • @Matthias 关键是,除非您对其进行编码并对其进行分析,否则您不知道这是否是一个问题。应该很容易编写代码来查看是否有问题。
  • 我已经知道 tmp.在 Android 下,对象创建确实是一个问题,至少在帧数很重要的游戏中是这样。请看这里:developer.android.com/guide/practices/design/performance.html
【解决方案3】:

尝试使用... 运算符:

static int sum (int ... numbers)
        {
           int total = 0;
           for (int i = 0; i < numbers.length; i++)
                total += numbers [i];
           return total;
        }

【讨论】:

  • 这将自动装箱整数,这是 OP 希望避免的。
【解决方案4】:

您可以使用类似于 C++ 位字段的 BitSet。 http://docs.oracle.com/javase/1.3/docs/api/java/util/BitSet.html

【讨论】:

  • 这确实可以解决我的问题。谢谢,我会玩一下:)
【解决方案5】:

您也可以将所有原语转换为double,然后传入double 的数组。唯一的窍门是你不能使用boolean 类型。

【讨论】:

  • true 可以转换为 1.0,false 可以转换为 0.0 =)
  • 当然可以。 d == 0 为假,d != 0 为真。很简单。
  • 这是一个很好的约定,但是:double doublePrimitive = (double)booleanPrimitive; 不会在 Java 6 中编译。
【解决方案6】:

Fwiw,像 sum(int... numbers) 这样的东西不会自动装箱。它将创建一个 int[] 来保存它们,因此会有一个对象分配;但它不会是 per int。

public class VarArgs {
    public static void main(String[] args) {
        System.out.println(variableInts(1, 2));
        System.out.println(variableIntegers(1, 2, 3));
    }   

    private static String variableInts(int... args) {
        // args is an int[], and ints can't have getClass(), so this doesn't compile
        // args[0].getClass();
        return args.getClass().toString() + " ";
    }   

    private static String variableIntegers(Integer... args) {
        // args is an Integer[], and Integers can have getClass()
        args[0].getClass();
        return args.getClass().toString();
    }   
}

输出:

class [I 
class [Ljava.lang.Integer;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-28
    • 1970-01-01
    • 1970-01-01
    • 2020-11-14
    相关资源
    最近更新 更多