Java 结构的数组,还是数组的结构?

Java 结构的数组,还是数组的结构?,java,data-structures,Java,Data Structures,嗯,我有一个表,它是我需要存储在Java中的结构数组。天真的不用担心记忆的方法是这样做的: public class Record { final private int field1; final private int field2; final private long field3; /* constructor & accessors here */ } List<Record> records = new ArrayList<Record&

嗯,我有一个表,它是我需要存储在Java中的结构数组。天真的不用担心记忆的方法是这样做的:

public class Record {
  final private int field1;
  final private int field2;
  final private long field3;
  /* constructor & accessors here */
}

List<Record> records = new ArrayList<Record>();
编辑:

  • 假设记录的#是不经常更改或从不更改的
  • 我可能不打算使用OptimizedRecordStore方法,但我想了解存储成本问题,以便能够自信地做出决策
  • 显然,如果我在上面的OptimizedRecordStore方法中添加/更改#个记录,我要么用新对象替换整个对象,要么删除“final”关键字
  • kd304提出了我脑海中的一个好观点。在与此类似的其他情况下,我需要对记录进行列访问,例如,如果field1和field2是“时间”和“位置”,那么将这些值作为数组用于MATLAB对我来说很重要,这样我就可以高效地绘制/分析它们

请注意,第二种方法可能会对缓存行为产生负面影响。如果您想一次访问一条记录,最好不要将该记录分散在各个地方

另外,在第二种方法中,您赢得的唯一内存(可能)是由于成员对齐。(并且必须分配一个单独的对象)。 否则,它们具有完全相同的内存使用,渐近地。第一个选项由于位置的原因要好得多,IMO

第二个版本差得多。在执行插入或删除操作时,不是调整一个数组的大小,而是调整三个数组的大小。更重要的是,第二个版本将导致创建更多的临时对象,它将在访问时这样做。这可能会导致大量垃圾(从GC的角度来看)。不太好

一般来说,在考虑性能之前,您应该考虑如何使用对象。因此,您有一个包含三个字段或三个数组的记录。哪一个更准确地描述了您正在建模的内容?我的意思是,当你插入或删除一个项目时,你是在做三个数组中的一个,还是三个数组都是一个块

我怀疑是后者,在这种情况下,前者更有意义


如果您真的关心插入/删除性能,那么可能需要不同的数据结构,可能是SortedSet或Map或SortedMap。

我也会选择ArrayList版本,因此我不需要担心它的增长。您是否需要具有类似于访问值的列?你的问题背后的情节是什么

编辑您还可以使用通用的
long[][]
矩阵。
我不知道如何将列传递给Matlab,但我猜使用基于列的存储不会获得太多的速度,更有可能会在java计算中降低速度。

因为要将int[]字段设置为最终值,所以只能对数组进行一次初始化。因此,如果您想要10^6个field1,Java将需要为每个int[]分隔大量内存,因为您无法重新分配这些数组的大小。对于ArrayList,如果您事先不知道记录的数量,并且可能会删除记录,那么在删除记录之前和之后都会节省大量空间。

每当我尝试用Java进行数字运算时,我总是不得不恢复到C风格的编码(即接近您的选项2)。它最大限度地减少了系统中浮动的对象数量,因为您只有3个对象,而不是1000000个对象。我能够使用C风格对实时声音数据进行FFT分析,但使用对象时速度太慢。

如何访问数据?如果对字段的访问总是耦合的,那么使用第一个选项,如果您要自己处理字段,那么第二个选项更好

请参阅维基百科上的这篇文章:


关于何时使用单独的数组更方便的一个很好的例子是模拟,其中数字数据被打包在同一个数组中,以及其他属性,如名称、颜色等,这些属性仅用于在其他数组中表示数据。

我选择第一种方法(结构数组)除非您访问存储的频率相对较低,并且遇到严重的内存压力问题

第一个版本基本上以“自然”形式存储对象(+1 BTW用于使用不可变记录)。由于每个对象的开销(可能是8-16字节,具体取决于您的JVM),这会占用更多的内存,但非常适合在一个简单的步骤中以方便且人类可以理解的形式访问和返回对象

第二个版本总体上使用更少的内存,但是在每个“get”上分配一个新对象是一个非常难看的解决方案,如果访问频繁,它将无法很好地执行

应考虑的其他一些可能性:

一个有趣的“极端”变体是采用第二个版本,但编写算法/访问方法以直接与底层阵列交互。这显然会导致复杂的相互依赖关系和一些难看的代码,但如果您真的需要,它可能会为您提供绝对最佳的性能。在密集的图形应用程序中使用这种方法是很常见的,例如操纵大量的三维坐标

“混合”选项是将底层数据存储在第二个版本中的数组结构中,但将访问的对象缓存在HashMap中,以便仅在第一次访问特定索引时生成该对象。如果只有一小部分对象可能被访问,但所有数据都需要“以防万一”,这可能是有意义的。

(不是直接的答案,而是我认为应该给出的答案)

从你的评论来看

“克莱特斯——我非常尊重你的想法和观点,但你给了我高级编程和软件设计的观点,这不是我想要的
public class OptimizedRecordStore {
  final private int[] field1;
  final private int[] field2;
  final private long[] field3;

  Record getRecord(int i) { return new Record(field1[i],field2[i],field3[i]); }
  /* constructor and other accessors & methods */
}