我有两个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;
}
答案 0 :(得分:0)
您可能需要考虑几个主要方面来改善此计划的效果。
BigDecimal
虽然BigDecimal
非常有用,但它在速度和空间要求方面都有很多开销。根据你的例子,你没有太多的精确度担心,所以我建议切换到普通浮点数或双打。这些只占空间的一小部分(因此您可以处理更大的文件)并且可能会更快地使用。
StringBuilder
这不是一般规则,但在这种情况下适用:您似乎正在解析然后在aLine
中重建processLine
。这是非常昂贵的,可能是不必要的。相反,您可以使用aLine.lastIndexOf('\t')
和aLine.substring
来减少开销,从而减少字符串。
这两个应该可以显着提高代码的性能,但不能解决整个算法问题。
您正在尝试处理足够的数据,而您可能需要考虑不要将所有数据保留在内存中。
例如,您可以根据第一个字段将数据集拆分为多个文件,在每个文件上运行程序,然后将文件重新加入一个文件。如果需要更多拆分,可以使用多个字段执行此操作。这需要更少的内存使用,因为拆分程序不必一次在内存中保留多行,而后者程序只需要将一大块原始数据保存在内存中,而不是整个内存。
您可能想尝试上面概述的特定优化,然后看看是否需要更高的效率,在这种情况下尝试进行数据集拆分。