【问题标题】:java read csv + specific sum of subarray - most efficient wayjava读取csv +子数组的特定总和-最有效的方法
【发布时间】:2017-01-13 20:17:27
【问题描述】:

我需要从大型 csv 中读取整数,然后对它们进行特定的求和。目前我有算法:

String csvFile = "D:/input.csv";
String line = "";
String cvsSplitBy = ";";
Vector<Int[]> converted = new Vector<Int[]>();

try (BufferedReader br = new BufferedReader(new FileReader(csvFile))) {

   while ((line = br.readLine()) != null) {
       String[] a = line.split(";",-1);
       int[] b = new int[a.length]; 
       for (int n = 0, n < a.length(), n++){
          b[n] = Integer.parseInt(a[n]);
       }
       converted.add(b);
   }
} 

catch (IOException e) {
e.printStackTrace();
}

int x = 7;
int y = 5;
int sum = 0;    

for (int m = 0; m < converted.size(); m++){
  for (n = 0, n < x, n++){
      sum = sum + converted.get(m)[n];
  }
  System.out.print(sum + " ");



  for (int n = x + y, n < converted.get(m).length, n = n + y){
      sum = 0;
      for (int o = n -y; o < n; o++)
         sum = sum + converted.get(m)[n];
      }
      System.out.print(sum + " ");
  }
  System.out.println("");
}

我尝试做的是获取 csv 行的前 x 个成员的总和,然后是每个 +y 的 x 个成员的总和。 (在这种情况下,第一个 x - 7 的总和(0-6 的总和),然后是下一个 x - 7 的总和,但 y - 5 列之后的总和(5-11 的总和),(10-16 的总和)......并为每一行写下它们。(最后收集最大的行号(0-6的总和),(5-11的总和)......,所以最终结果应该是例如5,9,13,155...... ,这意味着第 5 行的最大总和为 0-6,第 9 行的最大总和为 5-11...)如您所见,这是一种非常低效的方法。首先我已将整个 csv 读入字符串 [] , 然后到 int[] 并保存到 Vector。然后我创建了效率很低的循环来完成这项工作。我需要它尽可能快地运行,因为我将使用具有很多不同 x 和 y 的非常大的 csv。我正在考虑,但不知道怎么做:

  1. 在阅读循环中做这些总和
  2. 以不同的方式求和,并不总是向后循环 x 成员(保存最后一个和,然后减去旧的并添加新成员,或其他更快的方法来进行子数组求和)
  3. 使用 intStream 和并行性(并行可能会很棘手,因为我最终要寻找 max )
  4. 使用与 csv 不同的输入?
  5. 以上所有?

我怎样才能尽快做到这一点?谢谢

【问题讨论】:

    标签: java csv sum sub-array


    【解决方案1】:

    由于总和是每行的,因此您无需先读取内存中的所有内容。

    Path csvFile = Paths.get("D:/input.csv");
    try (BufferedReader br = Files.newBufferedReader(csvFile, StandardCharsets.ISO_8859_1)) {
    
         String line;
         while ((line = br.readLine()) != null) {
             int[] b = lineToInts(line);
             int n = b.length; 
    
             // Sum while reading:
             int sum = 0;
             for (int i = 0; i < 7; ++i) {
                 sum += b[i];
             }
             System.out.print(sum + " ");
    
             sum = 0;
             for (int i = n - 5; i < n; ++i) {
                 sum += b[i];
             }
             System.out.print(sum + " ");
    
             System.out.println();
         }
    }
    
    private static int[] lineToInts(String line) {
         // Using split is slow, one could optimize the implementation.
         String[] a = line.split(";", -1);
         int[] b = new int[a.length]; 
         for (int n = 0, n < a.length(), n++){
             b[n] = Integer.parseInt(a[n]);
         }
         return b;
    }
    

    更快的版本:

    private static int[] lineToInts(String line) {
        int semicolons = 0;
        for (int i = 0; (i = line.indexOf(';', i)) != -1; ++i) {
            ++semicolons;
        }
        int[] b = new int[semicolons + 1];
        int pos = 0;
        for (int i = 0; i < b.length(); ++i) {
            int pos2 = line.indexOf(';', pos);
            if (pos2 < 0) {
                pos2 = line.length();
            }
            b[i] = Integer.parseInt(line.substring(pos, pos2));
            pos = pos2 + 1;
        }
        return b;
    }
    

    顺便说一句:Vector 比较老,最好使用 List 和 ArrayList。

    List<int[]> converted = new ArrayList<>(10_000);
    

    上面给出初始容量的可选参数:万。

    奇怪的 try-with-resource 语法 try (BufferedReader br = ...) { 确保 br 总是自动关闭。即使出现异常或返回。


    并行和重新格式化问题后

    你可以阅读所有行

    List<String> lines = Files.readAllLines(csvFile, StandardCharsets.ISO_8859_1);
    

    而不是使用并行流,例如:

    OptionalInt max = lines.parallelStream()
        .mapToInt(line -> {
            int[] b = lineToInst(line);
            ...
            return sum;
        }).max();
    

    或:

    IntStream.range(0, lines.size()).parallel()
        .mapToObj(i -> {
            String line = lines.get(i);
            ...
            return new int[] { i, sum5, sum7 };
        }); 
    

    【讨论】:

    • 谢谢,我去试试,比较一下速度。这里只是对第二个总和(n-5)的评论,我不需要前 7 和后 5 的总和,但我需要 7 个成员的总和,移动 5。虽然 1 总和是前 7 的总和(数组 pos 0-6),第二个应该是 7 的总和,但移动了 5,所以数组 pos 5-11,然后是 10-16 ...,但仍然是 7 个成员的总和。就像过去 7 天的总和,每 5 天(第一次总和可以在 7 天后完成,但之后每 5 天)至于向量,我使用它,因为 arraylist 不是线程安全的,并且希望增加并行性。跨度>
    • 求和有点眼花缭乱,但也不难吧?从磁盘读取和计算已经比第一次读取然后计算要快,尤其是当数据全部保存在内存中时。但我会添加一些并行性。
    【解决方案2】:

    您可能会在阅读输入时尝试创建一些总和。使用 Integer,Integer 类型的 HashMap 也可能是可行的

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-03-03
      • 2019-07-31
      • 2013-02-24
      • 2014-10-04
      • 2018-01-10
      • 2010-10-24
      • 2019-01-06
      相关资源
      最近更新 更多