使用Java更新具有大量数据的csv中的特定列

时间:2019-01-04 07:14:34

标签: java csv

我有一个带有800 K条记录的csv文件“主列表”,每个记录有13个值。 cell [0]和cell [1]的组合给出了唯一的记录,我需要为每条记录更新cell [12]的值,说出状态。

我还有另一个csv文件,说“更新的子集列表”。这是文件“主列表”的子集。对于第二个csv中数量较少的所有记录(例如10000),我需要更新每个匹配记录的cell [11]或状态列值。

我尝试了直接BufferedReader,commons-csv的CsvParser和univocity.parsers的CsvParser。 但是读取整个文件并创建800K列表会发出内存不足异常。

相同的代码将部署在不同的服务器上,因此我希望有一个有效的代码来读取巨大的csv文件并更新相同的文件。

部分读取大文件并写入同一文件可能会损坏数据。

有关如何执行此操作的任何建议。 ??

文件inputF =新文件(inputFilePath);

if (inputF.exists()) {
InputStream inputFS = new FileInputStream(inputF);
BufferedReader br = new BufferedReader(new InputStreamReader(inputFS));
// skip the header of the file
String line = br.readLine();
mandatesList = new ArrayList<DdMandates>();

while ((line = br.readLine()) != null) {
    mandatesList.add(mapToItem(line));
}

br.close();

}

通过分批执行来解决内存问题。读单行和写单行可能会导致花费更多时间。我没有尝试过,因为我的问题已解决,可以使用批处理的10万条记录并在写入10万条记录后清除列表

现在的问题是更新状态占用了太多循环。...

我有两个csv。母版表(母版列表)有800 K记录,然后我有一个子集csv,也说它有10 k记录。此子集csv是从其他系统更新的,并且其更新状态为“确定”和“不正确”。我需要在主表中更新此状态。我怎样才能最好地做到这一点。 ???我使用的最愚蠢的方式是:–

 // Master list have batches but it contains 800 k records and 12 columns
    List<DdMandates> mandatesList = new ArrayList<DdMandates>();
// Subset list have updated status 
List<DdMandates> updatedMandatesList = new ArrayList<DdMandates>();
// Read Subset csv file and map DdMandates item and then add to updated mandate list


    File inputF = new File(Property.inputFilePath);
if(inputF.exists()) {
InputStream inputFS = new FileInputStream(inputF);
BufferedReader br = new BufferedReader(new InputStreamReader(inputFS, "UTF-8"));

checkFilterAndmapToItem(br);

br.close();

In Method checkFilterAndmapToItem(BufferedReader br)

    private static void checkFilterAndmapToItem(BufferedReader br) {
        FileWriter fileWriter = null;
        try {
            // skip the header of the csv
            String line = br.readLine();
            int batchSize = 0, currentBatchNo=0;
            fileWriter = new FileWriter(Property.outputFilePath);
            //Write the CSV file header
            fileWriter.append(FILE_HEADER.toString());
            //Add a new line separator after the header
            fileWriter.append(NEW_LINE_SEPARATOR);
            if( !Property.batchSize.isEmpty()) {
                batchSize = Integer.parseInt(Property.batchSize.trim());
            }
            while ((line = br.readLine()) != null) {

                DdMandates item = new DdMandates(); 
                String[] p = line.concat(" ").split(SEPERATOR);
                Parse each p[x] and map to item of type DdMandates\
                        Iterating here on updated mandate list to check if this item is present in updated mandate list
                        then get that item and update that status to item . so here is a for loop for say 10K elements
                mandatesList.add(item);

                if (batchSize != 0 && mandatesList.size() == batchSize) {
                    currentBatchNo++;
                    logger.info("Batch  no. : "+currentBatchNo+" is executing...");
                    processOutputFile(fileWriter);
                    mandatesList.clear();
                }
            }
            processing output file here for the last batch ...
        }

它将具有while循环(800 K迭代){每个元素内部循环10K迭代

因此至少需要800K * 10K循环

请帮助获取最佳方法并减少迭代。

预先感谢

2 个答案:

答案 0 :(得分:2)

假设您正在以50K的批量读取“主数据文件”:

  • 使用cell [0]和cell [1]作为键并将其余列作为值存储在Java HashMap中。

  • 大多数情况下,获取和放置的复杂度为O(1)。 see here

  • 因此在该特定批次中搜索1万条记录的复杂度为O(10K)。

    HashMap<String, DdMandates> hmap = new HashMap<String, DdMandates>();
    
  • 使用key = DdMandates.get(0)+ DdMandates.get(1)

注意:如果50K条记录超过了HashMap的内存限制,则创建较小的批处理。

  • 要进一步提高性能,可以通过创建小批量并在不同线程上处理它们来使用多线程。

答案 1 :(得分:1)

第一个建议是,当您创建ArrayList时,列表容量为10。因此,如果要处理大量数据,请首先进行初始化,例如:

private static final int LIST_CAPACITY = 800000;
mandatesList = new ArrayList<DdMandates>(LIST_CAPACITY);

第二个建议是,不要将数据存储在内存中,逐行读取数据,满足业务逻辑需求,然后释放内存,例如:

FileInputStream inputStream = null;
Scanner sc = null;
try {
    inputStream = new FileInputStream(path);
    sc = new Scanner(inputStream, "UTF-8");
    while (sc.hasNextLine()) {
        String line = sc.nextLine();
        /* your business rule here */
    }
    // note that Scanner suppresses exceptions
    if (sc.ioException() != null) {
        throw sc.ioException();
    }
} finally {
    if (inputStream != null) {
        inputStream.close();
    }
    if (sc != null) {
        sc.close();
    }
}