有效地从文本文件中解析和提取唯一值

时间:2015-03-29 18:21:56

标签: java performance file data-structures

我有两个tsv文件来解析和提取每个文件的值。每行每行可以有4-5个属性。这两个文件的内容如下:

1   44539   C   T   19.44  
1   44994   A   G   4.62  
1   45112        TATGG  0.92  
2   43635   Z   Q    0.87  
3   5672    AAS      0.67

每个文件中都有一些记录,它们具有相同但不同值的前3个或4个属性。我希望保留更高的此类记录值,并准备具有所有唯一值的新文件。例如:

1   44539   C   T   19.44

1   44539   C   T   25.44

我需要在上述案例记录中保留一个值为25.44

的值

我已为此草拟了代码,但几分钟后程序运行缓慢。我正在从形成键值对的文件中读取每个记录,其中前3或4条记录作为键,最后一条记录作为值并将其存储在hashmap中并使用它再次写入文件。有更好的解决方案吗? 另外我如何测试我的代码是否在文件中给出了正确的输出?

一个文件大小为498 MB,包含23822225条记录,另一个文件大小为515 MB,包含24500367条记录。

我在线程" main"中得到了Exception。 java.lang.OutOfMemoryError:大小为515 MB的文件的Java堆空间错误。

有没有更好的方法可以编写代码来有效地执行程序而不增加堆大小。

我将来可能不得不处理更大的文件,解决这些问题的诀窍是什么?

public class UniqueExtractor {
    private int counter = 0; 

    public static void main(String... aArgs) throws IOException {
        UniqueExtractor parser = new UniqueExtractor("/Users/xxx/Documents/xyz.txt");
        long startTime = System.currentTimeMillis();
        parser.processLineByLine();

        parser.writeToFile();
        long endTime = System.currentTimeMillis();

        long total_time = endTime - startTime;

        System.out.println("done in " + total_time/1000 + "seconds ");
    }


    public void writeToFile()
    {
        System.out.println("writing to a file");

        try {
            PrintWriter writer = new PrintWriter("/Users/xxx/Documents/xyz_unique.txt", "UTF-8");
            Iterator it = map.entrySet().iterator();
            StringBuilder sb = new StringBuilder();
            while (it.hasNext()) {
                sb.setLength(0);
                Map.Entry pair = (Map.Entry)it.next();
                sb.append(pair.getKey());
                sb.append(pair.getValue());
                writer.println(sb.toString());
                writer.flush();
                it.remove();
            }
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }

    public UniqueExtractor(String fileName) 
    {
        fFilePath = fileName;
    }

    private HashMap<String, BigDecimal> map = new HashMap<String, BigDecimal>();

    public final void processLineByLine() throws IOException {
        try (Scanner scanner =  new Scanner(new File(fFilePath))) {
            while (scanner.hasNextLine())
            {
                //System.out.println("ha");
                System.out.println(++counter);
                processLine(scanner.nextLine());
            }
        }
    }

    protected void processLine(String aLine)
    {
        StringBuilder sb = new StringBuilder();
        String[] split = aLine.split("  ");
        BigDecimal bd = null;
        BigDecimal bd1= null;

        for (int i=0; i < split.length-1; i++)
        {
            //System.out.println(i);
            //System.out.println();

            sb.append(split[i]);
            sb.append(" ");
        }
        bd= new BigDecimal((split[split.length-1]));
        //System.out.print("key is" + sb.toString());
        //System.out.println("value is "+ bd);

        if (map.containsKey(sb.toString()))
        {
            bd1 = map.get(sb.toString());
            int res = bd1.compareTo(bd);
            if (res == -1)
            {
                System.out.println("replacing ...."+ sb.toString() + bd1 + " with " + bd);
                map.put(sb.toString(), bd);
            }
        }
        else
        {
            map.put(sb.toString(), bd);
        }
        sb.setLength(0);
    }

    private String fFilePath;
}

1 个答案:

答案 0 :(得分:0)

您可能需要考虑几个主要方面来改善此计划的效果。

避免BigDecimal

虽然BigDecimal非常有用,但它在速度和空间要求方面都有很多开销。根据你的例子,你没有太多的精确度担心,所以我建议切换到普通浮点数或双打。这些只占空间的一小部分(因此您可以处理更大的文件)并且可能会更快地使用。

避免StringBuilder

这不是一般规则,但在这种情况下适用:您似乎正在解析然后在aLine中重建processLine。这是非常昂贵的,可能是不必要的。相反,您可以使用aLine.lastIndexOf('\t')aLine.substring来减少开销,从而减少字符串。

这两个应该可以显着提高代码的性能,但不能解决整个算法问题。

数据集拆分

您正在尝试处理足够的数据,而您可能需要考虑不要将所有数据保留在内存中。

例如,您可以根据第一个字段将数据集拆分为多个文件,在每个文件上运行程序,然后将文件重新加入一个文件。如果需要更多拆分,可以使用多个字段执行此操作。这需要更少的内存使用,因为拆分程序不必一次在内存中保留多行,而后者程序只需要将一大块原始数据保存在内存中,而不是整个内存。

您可能想尝试上面概述的特定优化,然后看看是否需要更高的效率,在这种情况下尝试进行数据集拆分。