什么工作得更快:二维数组或列表列表

时间:2011-11-23 15:13:43

标签: java performance multidimensional-array jvm nested-lists

我手头有表现情况。

我有大量的数据要以二维表格式(12000 X 2000)保存在内存中。现在据我所知,我可以使用int[][]List<List<Integer>>。当然,我使用int[i][j]list.get(i).get(j)来访问这些值。我循环遍历整个数据至少五次。

您认为哪一个会更快地运作,如果您能回答,为什么?还有什么办法可以加快执行速度吗?

我的java -version给出:
java version "1.6.0_29"
Java(TM) SE Runtime Environment (build 1.6.0_29-b11)
Java HotSpot(TM) Client VM (build 20.4-b02, mixed mode, sharing)

操作系统是Windows Vista。

8 个答案:

答案 0 :(得分:6)

阵列几乎肯定会更快。

使用ArrayList会使性能更加直观,因为它是由实际数组支持的。

编辑以总结评论

  • 列表具有重要性。可能是也可能不是问题。
  • 性能差异趋于朝着最小化。
  • 应该以确定基准为基准。

对于这个用例,我相信阵列的速度会快得多。它是否足够快重要是一个不同的问题,我对实际解决的问题知之甚少。

答案 1 :(得分:2)

1)将您的申请作为一个整体进行基准测试。不要以为您知道应用程序中的性能瓶颈在哪里。经验一次又一次地表明,人类通常会对此感到厌烦。在与生产相同的硬件和系统上执行此操作,或者您在浪费时间。

2)不要忘记以JIT编译器为您关注的代码开始的方式构建基准。在编译方法之前,通常需要10000次迭代的方法。对解释模式代码进行基准测试是完全浪费时间。

3)在已经处理了最严重瓶颈的应用程序中,许多应用程序将处于性能配置文件由处理器L1高速缓存未命中数量支配的状态。您可以将此视为您的应用程序合理调整的点。然而,你的算法可能仍然很糟糕,系统中可能仍有大量的繁忙工作可以解决。

4)假设你的算法没有糟糕,并且你没有大量繁忙的工作可以摆脱,如果数组/列表的差异对你来说真的很重要,那么你就会在这一点上开始在穿孔号码中看到它。

5)在大多数情况下,您会发现阵列的L1缓存情况比列表更好。但是,这是一般建议,不要误解为实际的性能调整建议。生成自己的性能数据并进行分析。

tl; dr version :阅读长版本。 tl; dr在Java性能讨论中没有地位 - 这是微妙而复杂的东西,细微差别很重要。

答案 2 :(得分:1)

如果列表实现RandomAccess(例如ArrayList),它几乎不会导致任何性能下降。如果您使用LinkedList随机访问其成员可能会非常昂贵。

列表为您带来非常严重的好处:它们可以自动增长。列表是一些集合,可以为您提供从一个集合复制到另一个集合(例如从一个映射到另一个列表等)的一些好处。

因此,您的选择应取决于您是否需要自动增长列表以及性能问题 对您来说非常重要。在大多数情况下,他们不是。

最后一句话。我认为N维数组和列表都不是最佳选择。如果您需要N维,其中N&gt; 1创建类并将其实例存储到一维数组或集合中。

答案 3 :(得分:1)

...当然,int [] []也会使用更少的内存。如果可能,请尝试使用byte [] []或short [] []来进一步减少内存使用量。

假设32位架构,12000x2000相当于91MB。如果字节足够,那么它将是1/4的大小。此外,还可能有性能改进(取决于架构)。

答案 4 :(得分:1)

这取决于您使用的List实施。如果您使用ArrayList(大多数人使用的那个),那么性能将与数组基本相同。但是如果你使用的是LinkedList,那么性能会明显变差,因为LinkedLists在随机访问方面非常慢。

在创建数据时,如果使用ArrayList,则应通过将数字传递给构造函数来初始化其内部数组的大小。否则,初始化ArrayList将比初始化数组慢得多。这是因为,当ArrayList的内部数组空间不足时,ArrayList会创建一个更大的新数组。然后它将旧数组中的所有元素复制到新数组中。这会导致显着的性能损失。

int list[][] = new int[12000][2000];
//--or--
List<List<Integer>> list = new ArrayList<List<Integer>>(12000);
for (int i = 0; i < 12000; i++){
  list.add(new ArrayList<Integer>(2000));
}

答案 5 :(得分:1)

这是一个简单的基准测试,可以更快地显示原始数组。 拳击的成本会使阵列变慢。

结果:

Results summary: 
Geo. Mean Primitive Array time:  0.7010723914083877 ms
Geo. Mean Boxed Array time:  2.517326382701606 ms
Geo. Mean ArrayList time:  1.1690484729741475 ms
Geo. Mean LinkedList time:  2.3522075667709146 ms

代码:

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 * User: shams
 * Date: 11/23/11
 * Time: 9:30 AM
 */
public class Benchmark {

   public static void main(String[] args) {

      final int ROW_SIZE = 1200;
      final int COL_SIZE = 200;
      final int numIterations = 10;

      final List<Double> arrayPrimitiveTimes = new LinkedList<Double>();
      final List<Double> arrayBoxedTimes = new LinkedList<Double>();
      final List<Double> linkedListTimes = new LinkedList<Double>();
      final List<Double> arrayListTimes = new LinkedList<Double>();

      for (int i = 0; i < numIterations; i++) {

         {
            tryGarbageCollection();
            startReportingTime();
            final int[][] dataArray = new int[ROW_SIZE][COL_SIZE];
            runPrimitiveArrayCode(dataArray);
            arrayPrimitiveTimes.add(endReportingTime("Primitive Array time: "));
         }
         {
            tryGarbageCollection();
            startReportingTime();
            final Integer[][] dataArray = new Integer[ROW_SIZE][COL_SIZE];
            runBoxedArrayCode(dataArray);
            arrayBoxedTimes.add(endReportingTime("Boxed Array time: "));
         }
         {
            tryGarbageCollection();
            startReportingTime();
            final List<List<Integer>> arrayList = new ArrayList<List<Integer>>(ROW_SIZE);
            for (int r = 0; r < ROW_SIZE; r++) {
               arrayList.add(new ArrayList<Integer>(COL_SIZE));
            }
            runListCode(arrayList);
            arrayListTimes.add(endReportingTime("ArrayList time: "));
         }
         {
            tryGarbageCollection();
            startReportingTime();
            final List<List<Integer>> arrayList = new LinkedList<List<Integer>>();
            for (int r = 0; r < ROW_SIZE; r++) {
               arrayList.add(new LinkedList<Integer>());
            }
            runListCode(arrayList);
            linkedListTimes.add(endReportingTime("LinkedList time: "));
         }
      }

      System.out.println("\n\n Results summary: ");
      printResult("Geo. Mean Primitive Array time: ", getMiddleGeoMeanTime(arrayPrimitiveTimes));
      printResult("Geo. Mean Boxed Array time: ", getMiddleGeoMeanTime(arrayBoxedTimes));
      printResult("Geo. Mean ArrayList time: ", getMiddleGeoMeanTime(arrayListTimes));
      printResult("Geo. Mean LinkedList time: ", getMiddleGeoMeanTime(linkedListTimes));
   }

   private static void runPrimitiveArrayCode(final int[][] dataArray) {
      for (int i = 0; i < dataArray.length; i++) {
         int[] cached = dataArray[i];
         for (int j = 0; j < cached.length; j++) {
            cached[j] = cached[j] + i + j;
         }
      }
   }

   private static void runBoxedArrayCode(final Integer[][] dataArray) {
      for (int i = 0; i < dataArray.length; i++) {
         Integer[] cached = dataArray[i];
         for (int j = 0; j < cached.length; j++) {
            Integer oldData = cached[j]; // dummy read
            cached[j] = i + j + (oldData == null ? 0 : 1);
         }
      }
   }

   private static void runListCode(final List<List<Integer>> dataArray) {
      for (int i = 0; i < dataArray.size(); i++) {
         final List<Integer> cached = dataArray.get(i);
         for (int j = 0; j < cached.size(); j++) {
            cached.set(j, cached.get(j) + i + j);
         }
      }
   }


   public static void tryGarbageCollection() {
      int count = 0;
      int limit = 2;
      while (count < limit) {
         count += 1;
         // println("enforceGarbageCollection: starting enforce of GC")

         int attempts = 0;
         WeakReference<Object> wr = new WeakReference<Object>(new Object());
         while (wr.get() != null && attempts < 25) {
            // add some delay
            int busy = 0;
            while (busy < 100) {
               busy += 1;
               wr.get();
            }
            new Object();
            System.out.print(".");
            System.gc();
            attempts += 1;
         }
         // println("enforceGarbageCollection: done GC")
      }
   }

   private static long startTime = 0;

   public static void startReportingTime() {
      startTime = System.nanoTime();
   }

   public static double endReportingTime(String msg) {
      long newTime = System.nanoTime();
      double execTime = (newTime - startTime) / 1e6;
      System.out.println(msg + execTime + "ms");
      return execTime;
   }

   public static double getBestTime(List data) {
      if (data.isEmpty()) {
         return 0;
      } else {
         java.util.Collections.sort(data);
         return ((Double) data.get(0)).doubleValue();
      }
   }

   public static double getMiddleGeoMeanTime(List<Double> data) {
      java.util.Collections.sort(data);
      List<Double> sortedResult = data;
      double midValuesProduct = 1.0;
      int midValuesCount = 0;
      for (int i = 1; i < sortedResult.size() - 1; i++) {
         midValuesCount += 1;
         midValuesProduct *= sortedResult.get(i).doubleValue();
      }
      final double average;
      if (midValuesCount > 0) {
         average = Math.pow(midValuesProduct, 1.0 / midValuesCount);
      } else {
         average = 0.0;
      }
      return average;
   }

   public static void printResult(String msg, double timeInMs) {
      System.out.println(msg + " " + timeInMs + " ms");
   }
}

答案 6 :(得分:0)

我认为在大多数情况下二维数组会更快,但为什么不根据具体问题对其进行测试呢?

答案 7 :(得分:0)

这里有一个广泛的讨论:

Array or List in Java. Which is faster?

以下是基准结论:

  

我写了一个小基准来比较ArrayLists和Arrays。在我的   老式的笔记本电脑,时间穿越5000元素的arraylist,   1000倍,比同等阵列慢大约10毫秒   代码。