【问题标题】:More efficient than a doubly-nested ArrayList?比双重嵌套的 ArrayList 更高效?
【发布时间】:2013-01-30 07:04:08
【问题描述】:

我正在构建一个每天处理中等数据量的 Java 后端组件。我们有一个 POJO,我们称之为Widget,它上面有大约 10 个属性。我的软件必须处理Widget 列表组:本质上还有其他进程(完全不同的系统)将它们自己的List<Widget> 组合在一起,然后将它们发送到我的软件。我的软件实际上收到了一个如下所示的包装 POJO:

public class Payload {
    private List<Widget> widgets; // <-- what I want
    private String guid; // GUID; my software doesn't need this
    private boolean fizz; // again, my software doesn't need this
    ... many other properties that I don't care about
}

我的软件聚合了所有这些List&lt;Widget&gt;,每个都由不同的系统创建,然后将它们一起大批量处理。

我暂时选择了ArrayList&lt;ArrayList&lt;Widget&gt;&gt;作为保存这批Widget列表的数据结构。 List&lt;Widget&gt;(外层ArrayList)大约有500,000组,每个List&lt;Widget&gt;每组大约有5个Widgets;在内部 ArrayList 中总共有大约 250 万 Widgets。

在最近的一次代码审查中,一些技术主管告诉我,我为这批 o' 小部件选择了错误的数据结构。他们告诉我我应该使用HashMap&lt;String,List&lt;Widget&gt;&gt;,因为它更高效且更易于使用。 hashmap 键是我的软件提供的Payload 中包含的 GUID。并不是说我出于任何原因都需要 GUID,它只是作为将 ~500,000 List&lt;Widget&gt; 分开的关键——我确实需要这样做。

这让我思考:谁是对的?!?我们对这个数据结构所做的唯一操作是“添加”(在ArrayList 的情况下,只需通过add(...) 添加WidgetList&lt;Widget&gt;)然后“读取”(在我的软件中,我必须遍历每个 Widget 并检查它的内容。使用我的嵌套 ArrayList 它的要点是:

for(List<Widget> widgetList : myDoublyNestedArrayOfWidgets) {
    for(Widget widget : widgetList) {
        ...
    }
}

这些是我们唯一需要的操作:将不同的List&lt;Widget&gt;s 添加到一些大的“批处理”数据结构中,然后在稍后检查所有这些并处理每个Widget。该软件在一些具有大量内存和处理能力的增强型服务器上运行。

所以我问:**ArrayList&lt;ArrayList&lt;Widget&gt;&gt; 是正确的选择,HashMap&lt;String,List&lt;Widget&gt;&gt;,还是其他什么...为什么?

【问题讨论】:

  • 我觉得你说了很多不需要回答核心问题的东西。尝试将其视为列出事实而不是讲述故事。
  • 如果您一起处理所有内容,您是否可以使用ArrayList&lt;Widget&gt; 并在 Widget 进入时将它们添加到您的主列表中?此外,在开始处理之前您是否需要所有 500k 集,或者您是否可以处理每个进来的小列表并存储结果。生成一个线程来处理每个小列表,然后在完成后扔掉列表可能会提高内存效率
  • 顺便说一句,你的用户名让我笑了 =)
  • 处理顺序重要吗? IE。你想先处理旧批次吗?如果是这样,则 guid 键控 Map 会破坏此排序(除非您使用 TreeMap 并且可以保证 guid 是有序的)
  • @Dukeling 我部分同意,但是如果你跳到最后,问题就很清楚了。可能会从“TL;DR”标题中受益,但我发现额外的上下文对个人很有帮助。

标签: java performance list data-structures hashmap


【解决方案1】:

所以我问:ArrayList&lt;ArrayList&lt;Widget&gt;&gt; 是正确的选择,HashMap&lt;String,List&lt;Widget&gt;&gt;,还是别的什么...为什么?

最后,重要的是您的软件解决了它应该解决的问题。

HashMap 比 ArrayList 更昂贵,如果您不需要通过键访问数据,ArrayList 更可能是最佳选择。 此外,在使用 ArrayList 时,您需要编写的处理代码似乎更加简单和高效。

顺便说一句,ArrayList&lt;ArrayList&lt;Widget&gt;&gt;HashMap&lt;String,List&lt;Widget&gt;&gt; 有点味道。也许您正在建模的是 ArrayList&lt;WidgetGroup&gt;WidgetGroup 包含 List&lt;Widget&gt; (以及所有其他属性,目前您可能不需要)。但是,如果您的 WidgetGroup 只包含一个 ArrayList,请不要引入这个新类(让它更简单)。

这让我思考:谁是对的?!?

在您的解决方案和同行评审者的解决方案之间,我个人非常喜欢您的解决方案。

但是,您可以自己保留它并遵循“技术领导”。如果这是他们的角色,那么重要的是他们的决定和提供这些选择的责任。 (支付你支票的人永远是对的)

【讨论】:

    【解决方案2】:

    有一个名词您一直在使用,但在您的数据模型中缺失:Batch。 如果您真的关心将它们保存在批处理中并保持代码可读,那么将它们封装在 Batch 类中:

    类批次{ 字符串向导; 列出 小部件; }

    而且,如果您不关心批次,那么您是否可以将它们全部展平为一个 List&lt;Widget&gt;

    【讨论】:

      【解决方案3】:

      哈希映射并不比数组列表更有效或更容易使用。如果在某些时候您确实需要通过其 GUID 键查找批次,则该更改可能是合理的。

      哈希映射比数组列表效率低,因为调整它的大小意味着必须重新评估哈希码并将数据重新分配到相当随机的内存位置。另一方面,调整数组大小会将旧数组中的内容线性复制到新数组,这对 CPU 缓存更加友好。

      哈希映射也不容易使用。要访问条目,您必须通过地图的条目集,这会破坏 law of Demeter

      【讨论】:

        【解决方案4】:

        也许您最终想要的是嵌入式(核心)数据库。另一种可能性是类似于 JavaSpaces/NoSQL,将交付和处理解耦。视情况而定。

        【讨论】:

          【解决方案5】:
          如果您必须随机访问内部列表,

          Hashmap 会更高效,并且使用 hashmap 的代码看起来对于在看到嵌套循环时陷入困境的审阅者来说更优雅。但是,如果您必须遍历并访问每个节点,您将不会比 On^2 做得更好。您可以将它们填充到数据库中,但这除了复杂性之外不会为您带来任何好处。它更优雅,就像 hashmap 一样。当然,所有这些都假设您有足够的内存一次保存所有 250 万个小部件。如果必须分页,那么某种 DB SQL 或 NoSQL 可能会更好。

          【讨论】:

            【解决方案6】:

            从你的问题可以看出你正在做这些事情。

            1. 从您的数据中读取数据。
            2. 添加更多小部件。

            问题出现了,从ArrayList&lt;ArrayList&lt;Widget&gt;&gt; to HashMap&lt;String,List&lt;Widget&gt;&gt; 更改您的数据结构将如何影响上述两个活动。

            1) 阅读:您已将它们分为 4 组,因此使用 hashmap 您将使用散列存储您的组,这对于少量数据集(您的case) 所以这里不需要使用 hashmap。

            2) 添加更多小部件 :您将访问要添加的列表,因此同样您可以阅读。使用ArrayListObj.get(index) 不会有什么坏处。

            现在使用ArrayList 将始终按顺序读取widgets。哪个不会使用Hashmap 完成,但无论如何我认为这不是你关心的问题,或者是吗? :-)

            【讨论】:

              猜你喜欢
              • 2013-10-20
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2023-03-11
              • 1970-01-01
              • 2014-10-09
              • 1970-01-01
              • 2021-02-03
              相关资源
              最近更新 更多