java读取csv+子数组的特定总和-最有效的方法

标签 java csv sum sub-array

我需要从大型 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 读入 string[] ,然后到 int[] 并保存到 Vector。然后我创建了相当低效的循环来完成这项工作。我需要它尽可能快地运行,因为我将使用非常大的 csv 和许多不同的 x 和 y。我什么一直在想,但不知道该怎么做:

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

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

最佳答案

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

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 };
    }); 

关于java读取csv+子数组的特定总和-最有效的方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39347890/

相关文章:

java - NestedTree 上的测试 CheckBox 失败

java - RGB 创建时没有语句错误

SQL师给出错误答案

MYSQL sum() 排除记录

python - 快速删除大型 .csv 文件中的 header

MYSQL - 三重内部连接和 SUM

Java LinkedHashSet 向后迭代

java - 如何将 @params 文件类型实现到此代码选择中?

csv - 在 PyCharm 中对齐 CSV 列

python - 如何将 pandas 数据框转换为每列格式不同的 CSV 格式?