【问题标题】:Converting to a column oriented array in Java在 Java 中转换为面向列的数组
【发布时间】:2010-04-29 10:57:37
【问题描述】:

虽然我在标题中使用了 Java,但这可能适用于任何 OO 语言。 我想知道一些新想法,以提高我正在尝试做的事情的性能。

我有一个不断接收 Object[] 数组的方法。我需要通过多个数组(列表或其他东西)拆分此数组中的对象,以便我为该方法接收的所有数组的每一列都有一个独立的列表。

例子:

List<List<Object>> column-oriented = new ArrayList<ArrayList<Object>>();

public void newObject(Object[] obj) {
    for(int i = 0; i < obj.length; i++) {
        column-oriented.get(i).add(obj[i]);
    }
}

注意:为简单起见,我省略了对象和内容的初始化。

我上面显示的代码当然很慢。我已经尝试了其他一些方法,但想听听一些新的想法。

如果你知道它对性能非常敏感,你会怎么做?

编辑:

我测试了一些东西,发现:

我没有使用 ArrayList(或任何其他集合),而是将 Object[] 数组包装在另一个对象中以存储各个列。如果此数组达到其容量,我将创建另一个大小为两倍的数组,并使用 System.copyArray 将内容从一个复制到另一个。令人惊讶的是(至少对我而言)这比使用 ArrayList 存储内列要快...

【问题讨论】:

  • Object[] obj 中包含什么?它实际上是同质类型的数组,还是您将其用作某种异构记录? obj 应该是数据库表中的一行吗? “面向列”真的是数据库中的一张表,每一个嵌套的列表就是一整列吗?
  • 是否绝对有必要更改值的存储方式?如果你有足够的抽象,那么你可以只改变访问策略而不用改变数据结构。
  • @polygenelubricants 是的,您可以将其视为数据库中的一条记录,我想将其转换为面向列的表。就像你说的那样。
  • @halfwarp: ...为什么不直接使用数据库???为什么要将这个列表列表存储在 Java 中?
  • @halfwarp:在内存中使用这种面向列的表示有什么好处?看起来你有足够的抽象,因为你说你用自己的Object[] 进行了列试验。也许您实际上并不需要一个实际的面向列的存储,而可以只为列提供一个视图 Iterator&lt;?&gt;

标签: java performance algorithm column-oriented


【解决方案1】:

答案取决于数据和使用情况。您在此类集合中有多少数据?读/写的比例是多少(添加对象数组)?这会影响内部列表的结构更好以及许多其他可能的优化。

复制数据的最快方法是完全避免复制。如果您知道调用者代码没有进一步修改 obj 数组(这是重要条件),可能的技巧之一是实现您自定义的 List 类以用作内部列表。您将在内部存储共享的List&lt;Object[]&gt;。每次调用我们只是将新数组添加到该列表中。自定义内部列表类会知道它代表哪一列(假设它是n),当它被要求在位置m处给出项目时,它将转置mn并查询内部结构以获得@987654328 @。这种实现是不安全的,因为调用者的限制很容易忘记,但在某些情况下可能会更快(但在其他情况下可能会更慢)。

【讨论】:

  • 我已经想到了类似的东西,但还没有测试过。考虑到我最近所做的一些重构,这可能对我有用。我会测试并告诉你。
【解决方案2】:

我会尝试将 LinkedList 用于内部列表,因为它应该具有更好的插入性能。也许将 Object arra 包装到集合中并使用 addAll 也可能会有所帮助。

【讨论】:

  • “因为它应该具有更好的插入性能” ...并且索引访问的性能要慢得多。 ArrayList javadocs 说:“添加操作在摊销的常数时间内运行,也就是说,添加 n 个元素需要 O(n) 时间。”通常 ArrayList 的性能比 LinkedList 好得多,因为它不必像 LinkedList 那样为每个插入分配新节点。它需要不时地重新分配其数组,但不那么频繁。您也可以提前预留足够的空间。 (Tbh,我不会担心它根本除非探查器在这里显示问题)
  • @Peter Štibraný 嗨,看看代码,没有基于索引的访问,所以我想这可能是一个公平的权衡......顺便说一句。我认为提问的人对此感到担忧,他还有什么其他的理由?
  • 代码仅显示列表创建。我猜 OP 不想创建新列表只是为了将其保留在未使用的堆上。 :) 可以以更少的开销创建数组列表,并且访问速度更快。当您进行多次删除或需要实现堆栈/队列时,链表会更好,但此处并非如此。
  • @Peter Štibraný 通过迭代器顺序访问 LinkedList 也可能是 O(1),所以这取决于...但我承认插入可能没有那么快,我没有实际的测量/经验为单个对象分配成本。
  • 在这两个方面都同意...在这两种情况下顺序访问都是 O(1),如果您需要插入到列表中间,您可能应该重新考虑您对 ArrayList 的选择。
【解决方案3】:

由于复制数组,ArrayList 可能会很慢(它使用与您自己编写的集合类似的方法)。

作为替代解决方案,您可以先尝试简单地存储行,并在必要时创建列。这样,列表中内部数组的复制减少到最低限度。

例子:

//Notice: You can use a LinkedList for rows, as no index based access is used.
List<Object[]> rows =... 

List<List<Object>> columns;

public void processColumns() {
  columns = new ArrayList<List<Object>>();
  for(Object[] aRow : rows){

    while (aRow.size() > columns.size()){
      //This ensures that the ArrayList is big enough, so no copying is necessary
      List<Object> newColumn = new ArrayList<Object>(rows.size())
      columns.add(newColumn); 
    }

    for (int i = 0; i < aRow.length; i++){
      columns.get(i).add(aRow[i]);
    }
  }
}

根据列数,外部列表仍有可能在内部复制数组,但普通表包含的行数远多于列数,因此它应该只是一个小数组。

【讨论】:

    【解决方案4】:

    使用LinkedList 来实现列列表。它随数据线性增长,为 O(1)。 (如果你使用 ArrayList 它必须不时调整内部数组的大小)。

    收集值后,您可以将该链表转换为数组。如果 N 是您将从为每个列表保存 3*N 引用(每个 LInkedList 具有 prevRef/nextRef/itemRef)传递到仅 N 引用的行数。

    最好有一个数组来保存不同的列列表,但当然,这不是一个很大的改进,只有在你事先知道列数的情况下才能做到这一点。

    希望对你有帮助!

    Edit 测试和理论表明 ArrayList 在摊销成本方面更好,即总成本除以处理的项目数......所以不要听我的“建议”:)

    【讨论】:

    • ArrayList 也是 O(1) 摊销成本,如果已知所需大小,您可以轻松指定初始容量。
    • 其实我已经测试过性能,ArrayList 更快。
    • 嗯...哇...我从没想过摊销成本...你说得对!当然......如果你知道初始容量...... presized ArrayList 更好。
    猜你喜欢
    • 1970-01-01
    • 2018-09-07
    • 2011-02-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-13
    • 2012-03-23
    相关资源
    最近更新 更多