21xrx.com
2024-12-23 00:45:15 Monday
登录
文章检索 我的文章 写文章
Java实现千万级数据导出CSV文件
2023-06-16 13:25:07 深夜i     --     --
Java 千万级数据 CSV文件 流式写入 分批次写入 多线程写入

CSV(Comma-Separated Values)是一种常见的数据存储格式,它以逗号作为字段分隔符,以换行符作为行分隔符。在数据集很大的情况下,导出CSV文件可能会面临内存溢出等问题。本文将介绍如何使用Java实现千万级数据导出CSV文件的方法。

1. 使用流式写入文件

在处理大量数据时,使用流式写入文件是一个比较好的选择。这种方法可以避免一次性将所有数据加载到内存中,而是使用缓存区一行一行地写入数据。以下代码演示了使用流式写入文件的具体实现过程:


public static void writeToCSV(List dataList, String filePath) {

  try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath))) {

    for (Data data : dataList) {

      writer.append(data.getId())

         .append(",")

         .append(data.getName())

         .append(",")

         .append(data.getAge())

         .append("\n");

    }

  } catch (IOException e) {

    e.printStackTrace();

  }

}

以上代码中,文件写入过程通过BufferedWriter实现,每写入一行数据,就刷新缓存区。这种方式可以在写入数据时不产生内存溢出的情况下导出CSV文件。

2. 分批次写入文件

除了使用流式写入文件的方式外,另一种常见的处理大量数据的方法是分批次写入文件。对于数据量较大的情况,可以将数据分为多个批次,每个批次只写入几万条数据。这样可以减轻单次写入数据的负担,避免一次性将大量数据写入文件中。

以下代码演示了分批次写入文件的具体实现过程:


public static void writeToCSV(List dataList, String filePath, int batchSize) {

  int totalCount = dataList.size();

  int batchCount = (totalCount % batchSize == 0) ? totalCount / batchSize : (totalCount / batchSize + 1);

  try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath))) {

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

      int startIndex = i * batchSize;

      int endIndex = Math.min((i + 1) * batchSize, totalCount);

      List subList = dataList.subList(startIndex, endIndex);

      for (Data data : subList) {

        writer.append(data.getId())

           .append(",")

           .append(data.getName())

           .append(",")

           .append(data.getAge())

           .append("\n");

      }

      writer.flush();

    }

  } catch (IOException e) {

    e.printStackTrace();

  }

}

以上代码中,数据按照batchSize进行分批处理,每个批次均在同一个文件中进行写入。当批次数量很多时,可以考虑将每个批次写入到不同的文件中,避免一个文件过大。

3. 使用多线程方式写入文件

在数据量较大的情况下,单线程写入数据可能会导致效率较低。使用多线程方式写入数据可以大幅提升导出CSV文件的效率。以下代码演示了如何使用多线程方式写入文件:


public static void writeToCSV(List dataList, String filePath, int threadCount) {

  int totalCount = dataList.size();

  try {

    ExecutorService executorService = Executors.newFixedThreadPool(threadCount);

    List > tasks = new ArrayList<>();

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

      final int batchIndex = i;

      tasks.add(new Callable () {

        @Override

        public Void call() throws Exception {

          try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath + "_" + batchIndex))) {

            for (int i = batchIndex; i < totalCount; i += threadCount) {

              Data data = dataList.get(i);

              writer.append(data.getId())

                 .append(",")

                 .append(data.getName())

                 .append(",")

                 .append(data.getAge())

                 .append("\n");

            }

          }

          return null;

        }

      });

    }

    executorService.invokeAll(tasks);

    executorService.shutdown();

  } catch (InterruptedException | IOException e) {

    e.printStackTrace();

  }

}

以上代码中,使用ExecutorService创建了多个线程,每个线程将数据按照线程数进行分组,然后写入到不同的文件中。这样可以避免线程间竞争同一个文件写入数据的问题,同时也可以大幅提高导出CSV文件的效率。

  
  

评论区

{{item['qq_nickname']}}
()
回复
回复
    相似文章