【问题标题】:Java array of Object that does not require new: allocated contiguosly不需要 new 的 Java 对象数组:连续分配
【发布时间】:2026-01-01 18:20:05
【问题描述】:

在Java 中,您声明一个数组,然后调用new 来分配空间。因此,与大小为 4 的数组相比,具有 4 个命名整数的类占用更少的空间并具有更好的局部性。

有没有办法拥有一个由 4 个元素组成的数组,但它的分配方式与命名变量 a1, a2, a3, a4 的分配方式相同

对于那些了解 C++ 的人,这与要求 int x[4] 而不是 int *x 相同

class X
{
   int x1;
   int x2;
   int x3;
   int x4;
};

[ Class OID ][x1][x2][x3][x4] = 1 ref + 4 int

class Y
{
   int y[];
};

y=new int[4];

[ Class OID ][Y] ======> [Array OID][Array Size][y][y][y][y] = 3 ref + size + 4 int

【问题讨论】:

  • 具有 4 个命名整数的类与 3 个元素的数组占用相同的空间。第 4 个元素空间是数组大小(尽管有时这会被头部吸收,而在其他情况下,它会被分配舍入吞没)。数组的分配本质上和对象带字段的分配是一样的。
  • @HotLicks - 我认为它可能很昂贵 - 类中的指针(在 64 位系统上)是 2 个整数的大小 - 数组对象的运行时信息也是一个指针 - 另一个 2 -加上大小 - 另一个 2 - 然后 4 个整数 = 10 个整数,而不是 4 个相同大小的整数加上负载,这会增加缓存未命中的可能性
  • Java int 是 4 个字节,无论是在数组中还是在常规对象中。数组和常规对象都必须包含类指针。现在,如果您正在谈论将一个对象(无论是数组还是常规对象)嵌入到另一个对象中,而不是使用指针(“引用”)来引用该对象,那是做不到的。如果您想这样做,请使用 C#(尽管我不确定 C# 真的可以做到这一点而不是简单地模拟它)。
  • @HotLicks - 请参阅 Q 中的上图 - 请注意 Y 是如何引用的 - 这很好,因为您可以更改它所指的内容,但不好,因为它增加了很多空间并导致 2获取
  • 是的,正如我所说,你不能在 Java 中将一个对象嵌入到另一个对象中。

标签: java arrays optimization data-structures


【解决方案1】:

你唯一能做的就是这个

int x1, x2, x3, x4;

void set(int i, int v) {
    switch(i) {
    case 0:
        x1 = v;
        break;
    ...
    default:
        throw new IndexOutOfBoundsException();
    }
}

int get(int i) {
...

注意,字节码中的切换是经过优化的,它不会顺序测试i,而是直接进入所需的代码

ILOAD 1
TABLESWITCH
  0: L1
  1: L2
  2: L3
  3: L4
  default: L5

【讨论】:

    【解决方案2】:

    从 Java 6 开始,在理想情况下,对象实际上是在堆栈上而不是在堆上分配的——换句话说,这正是您所要求的。通常,这会尝试用于其引用不离开创建它们的范围的对象。所以事实是,事实上,有时“新”是完全免费的!尽管 Java 代码看起来冗长且效率低下,但有几层优化编译器可以将您编写的内容转化为高效的机器代码。

    【讨论】:

    • 堆栈上的分配可能是一个不错的改进,但您仍然有两个分配而不是 1