【问题标题】:What is the best List implementation for Large lists in javajava中大型列表的最佳列表实现是什么
【发布时间】:2010-12-17 21:35:06
【问题描述】:

我必须创建一个包含 n 个元素的大列表(最多可达 100,000 个)。列表中的每个元素都是一个与列表索引等效的整数。在此之后,我必须在此列表上调用 Collections.shuffle。我的问题是,应该使用哪个列表实现(java 集合或 apache 集合)。我的直觉是 ArrayList 可以在这里很好地使用。 所有的想法都值得赞赏。 谢谢!

感谢您的意见。我想我坚持使用 ArrayList。我目前正在使用带有 initialCapacity 参数的 ArrayList 构造函数,并且我传递了列表的大小。因此,如果原始列表是 100000,我使用 new ArrayList(100000); 创建这个新列表;因此,我认为我没有创建数组并执行 asList,因为不会有任何调整大小。此外,像 GrowthList 和 LazyList 这样的大多数 apache 集合列表都没有实现 RandomAccess。这肯定会减慢 shuffle(根据 javadocs)。 FastArrayList 确实实现了 RandomAccess,但 apache 对这个类有一个注释说“这个类不是跨平台的。在某些架构上使用它可能会导致意外失败”。

【问题讨论】:

  • 您能详细说明您想要达到的目标吗?
  • 添加和改组后的列表如何处理?您是否在中间添加/删除元素?您是否在末尾添加/删除元素?您是按任意顺序访问中间的元素,还是从一端到另一端进行单次传递?在不知道你将要做什么的情况下,真的很难做出决定。如果您只想连续添加数字并随机播放,我会说 ArrayList 就是答案。
  • 100000 这些天并没有那么大。以最简单的方式使用数组列表在我的机器上花费不到 100 毫秒(Intel Core2 T5600 的单核 @ 1.83GHz)。

标签: java list apache-commons shuffle


【解决方案1】:

ArrayList 很可能每个列表元素的开销最小,因此应该是最佳选择。如果您经常需要删除列表中间的项目,这可能是一个更糟糕的选择。

【讨论】:

  • 其实 int[] 开销会小一些,具体怎么选就看他的需要了。
  • @rsp: int[] 没有实现 List - 你需要一个围绕它的包装器。
  • 只有当你坚持使用 Collections.Shuffle :-) 但点了。
【解决方案2】:

ArrayList<T> 可能会很好,是的 - 但是你使用什么标准来衡量“最佳”呢?无论如何它必须有多好?无论这些标准是什么,您在复杂性和“好”之间的权衡是什么?

【讨论】:

    【解决方案3】:

    引自 Collections.shuffle javadoc:

    此方法以线性时间运行。如果指定列表没有实现 RandomAccess 接口并且很大,则此实现在打乱之前将指定列表转储到数组中,并将打乱后的数组转储回列表中。这避免了由于将“顺序访问”列表改组而导致的二次行为。

    因此,如果您没有其他需求,我会选择实现 RandomAccess 的 ArrayList。

    【讨论】:

      【解决方案4】:

      ArrayList 将是最好的列表。因为数组支持对于交换 shuffle 中使用的元素非常有效。

      但是,如果您真的追求性能,您可能希望考虑使用 int[] 或基于 int[] 的自定义列表,就像所有 List 和 List 标准实现一样,您会将整数装箱和拆箱为整数。

      这不会是 suffle 的问题,因为这只是重新排序指针,但您可能不需要创建 100,000 个对象。假设您在创建之前知道列表的大小,您可以很容易地创建一个包装原始数组的新 List 类。如果用作 java.util.List,您仍然需要将任何 get 方法的返回值装箱。

      【讨论】:

        【解决方案5】:

        创建一个Integer 数组,然后用Arrays.asList 包装它比常规ArrayList 的开销更少。

        List<Integer> makeList(int size){
            if (size < 0) throw new IllegalArgumentException();
            Integer[] arr = new Integer[size];
            for (int i = 0; i < arr.length; ++i) arr[i] = i;
            List<Integer> list = Arrays.asList(arr);
            Collection.shuffle(list);
            return list;
        }
        

        您节省了整个int 的空间(......在这种情况下绝对没有什么),但它执行的范围检查确实比“真正的”ArrayList 少,因此访问会稍微快一些。不过,您可能不会注意到任何事情:)

        【讨论】:

        • 想必当你使用List时,你不会自己分配一个数组。你只是,好吧,使用List
        • 嗯,这取决于你怎么做,这恰好是这个问题的本质——如何以很少的开销创建一个大列表。
        【解决方案6】:

        Javolution 声称拥有 java 中最快的 List 实现。但是我在这个库中找不到任何 shuffle 实现,所以你必须手动完成。

        【讨论】:

        【解决方案7】:

        Google 的 Guava 库有一些非常好的原始处理,包括一个 Ints.asList() 方法返回一个可能被打乱的列表。

        Guava 项目仍处于初步部署阶段,尽管代码已经过仔细审查并在 Google 中大量使用。您需要从 SVN 检索代码并构建 com.google.common.primitive 类。

        【讨论】:

          【解决方案8】:

          您还可以使用基于内存映射文件的列表实现。在这样的实现中,列表并不完全存在于内存中,但只有一部分巨大的列表将在内存中处于活动状态。 如果您达到堆空间限制(主要在 32 位 jvm 中),您可能需要使用比普通文件 I/O 更快的内存映射文件使列表无缝推送数据。 此google code 中描述了一种此类实现,并在此link 中进行了解释。

          【讨论】:

            【解决方案9】:

            有一个名为 GlueList 的新 List 实现,它比 ArrayList 和 LinkedList 快。

            免责声明:我已经创建了实现。

            【讨论】:

            • 如果你是作者,习惯上会声明你是作者。
            【解决方案10】:

            这是关于您对FastArrayList 问题的更新。

            FastArrayList 确实实现了RandomAccess,但是 apache 有一个关于这个类的注释说“这个类不是跨平台的。在某些架构上使用它可能会导致意外失败”。

            FastArrayList 类 (javadoc) 是一个并发列表类。这就是 javadoc 所说的:

            一个定制的 java.util.ArrayList 实现,旨在操作 在大多数方法的多线程环境中 调用是只读的,而不是结构更改。经营时 “快速”模式,读取调用是非同步的,写入调用执行 以下步骤:

            1. 克隆现有集合
            2. 对克隆进行修改
            3. 用(修改的)克隆替换现有集合

            [...]

            注意:如果您只在一个 单线程,你应该直接使用 java.util.ArrayList (没有 同步),以获得最佳性能。

            注意:这个类不是跨平台的[由于问题 快速模式和多线程]

            现在您的用例(如上所述)是单线程的。所以:

            • “跨平台”问题无关紧要,因为它只影响多线程用例。
            • 第一个“注意”(清楚地)表明,对于单线程应用程序,最好使用ArrayList

            简而言之,FastArrayList 中的“快速”是相对于(比如说)这样做的:

              List<String> myConcurrentlList = Collections.synchronizedList(new ArrayList<>());
            

            回到你原来的问题。 ArrayList 是最简单的快速方法,我怀疑任何其他 List 类都会击败它。但是,以下方法 可能会更快。

              String[] array = new String[...];
              // populate array
              // shuffle array ... using same algorithm as Collections.shuffle
              for (int i = array.length; i > 1; i--)
                  swap(array, i - 1, rnd.nextInt(i));
              }
              List<String> list = Arrays.asList(array);
            

            为什么会更快?因为数组上的交换操作比ArrayList 上的要快。

            总体上会更快吗?很难说。这取决于:

            • 您是否创建/填充这样的数组是/不是额外的工作
            • ArrayList 相比,asList 包装器上的列表操作的性能...以及您执行的操作等。

            我的建议是提防“过早的优化”。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2012-12-13
              • 1970-01-01
              • 2010-10-13
              • 1970-01-01
              • 1970-01-01
              • 2010-10-02
              • 1970-01-01
              相关资源
              最近更新 更多