1

嗨,我正在读取文本文件并将每一行(用逗号分隔)保存到一个数组中。唯一的问题是数组中的大多数元素都是双精度值,而两个元素是字符串。因此,我不得不将数组设为 String[] 数组。因此,每当我想对数组中的双精度值执行一些方程式时,我必须首先将它们解析为双精度值。我实际上是在运行这些方程的 1000 多次迭代,因此我的代码不断地将字符串解析为双精度。这是一种代价高昂的方式,它减慢了我的程序。有没有更好的方法可以将字符串数组中的值转换为双精度值,或者在保存文本文件中的行时应该采取更好的方法?谢谢

这是我从文本文件中读取后其中一个数组的样子:

String[] details = {"24.9", "100.0", "19.2" , "82.0", "Harry", "Smith", "45.0"};

我现在需要将前 2 个元素相乘并将其添加到第 3、第 4 和第 7 个元素的总和中。换句话说,我只使用数字元素(当然保存为字符串)

double score = (Double.parseDouble(details[0]) * Double.parseDouble(details[1])) + Double.parseDouble(details[2]) + Double.parseDouble(details[3]) + Double.parseDouble(details[6]);

我必须对文本文件中的每一行(1000 多行)执行此操作。因此,我的程序运行非常缓慢。有没有更好的方法可以将字符串值转换为双精度值?还是有更好的方法我应该首先存储它们?

编辑:我使用分析器检查代码的哪一部分是最慢的,它确实是我上面显示的代码

4

2 回答 2

4

这是一个生成输入文件的示例,例如您描述的 10000 行长的输入文件,然后将其读回并执行您发布的计算并将结果打印到标准输出。为了获得最差的读取性能,我在读取文件时专门禁用了任何缓冲。正如其他人所建议的那样,我也根本没有做任何缓存。整个过程,包括生成文件、进行计算和打印结果,始终需要大约 520-550 毫秒。这几乎不是“慢”,除非您对数百或数千个文件重复相同的过程。如果您看到与此截然不同的性能,那么可能是硬件问题。出现故障的硬盘可能会使读取性能下降到几乎为零。

import java.io.*;
import java.util.Random;

public class ReadingDoublesFromFileEfficiency {
    private static Random random = new Random();

    public static void main(String[] args) throws IOException {
        long start = System.currentTimeMillis();
        String filePath = createInputFile();
        BufferedReader reader = new BufferedReader(new FileReader(filePath), 1);
        String line;
        while ((line = reader.readLine()) != null) {
            String[] details = line.split(",");
            double score = (Double.parseDouble(details[0]) * Double.parseDouble(details[1])) + Double.parseDouble(details[2]) + Double.parseDouble(details[3]) + Double.parseDouble(details[6]);
            System.out.println(score);
        }
        reader.close();
        long elapsed = System.currentTimeMillis() - start;
        System.out.println("Took " + elapsed + " ms");
    }

    private static String createInputFile() throws IOException {
        File file = File.createTempFile("testbed", null);
        PrintWriter writer = new PrintWriter(new FileWriter(file));
        for (int i = 0; i < 10000; i++) {
            writer.println(randomLine());
        }
        writer.close();
        return file.getAbsolutePath();
    }

    private static String randomLine() {
        return String.format("%f,%f,%f,%f,%s,%s,%f",
                score(), score(), score(), score(), name(), name(), score());
    }

    private static String name() {
        String name = "";
        for (int i = 0; i < 10; i++) {
            name += (char) (random.nextInt(26) + 97);
        }
        return name;
    }

    private static double score() {
        return random.nextDouble() * 100;
    }
}
于 2013-03-02T18:02:23.907 回答
2

您最好创建一个适当的对象并将值存储在其中 - 这给您带来两个主要好处,1)您的代码会更快,因为您避免不必要地重新计算双精度值和 2)您的代码会更清晰,因为字段将被命名,而不是details[0]在完全不清楚[0]所指内容的情况下进行调用。

由于 2) 我不知道这些字段应该是什么,所以显然你的课程看起来会有所不同,但想法是一样的:

public class PersonScore {
    private double[] multipliers = new double[2];
    private double[] summers = new double[3];
    private String first;
    private String last;

    // expects a parsed CSV String
    public PersonScore(String[] arr) {
        if(arr.length != 7)
            throw new InvalidArgumentException("Must pass exactly 7 fields");
        multipliers[0] = Double.parseDouble(arr[0]);
        multipliers[1] = Double.parseDouble(arr[1]);
        summers[0] = Double.parseDouble(arr[2]);
        summers[0] = Double.parseDouble(arr[3]);
        summers[0] = Double.parseDouble(arr[6]);
        first = arr[4];
        last = arr[5];
    }

    public double score() {
        double ret = 1;
        for(double mult : multipliers)
            ret *= mult;
        for(double sum : summers)
            ret += sum;
        return ret;
    }

    public String toString() {
        return first+" "+last+": "+score();
    }
}

请注意,还有一个额外的好处,即 score 方法现在更加健壮。您在上面的实现对我们想要使用的字段进行了硬编码,但是通过将字段解析并存储为结构内容,我们能够实现更易读、更可扩展的分数计算方法。

于 2013-03-02T16:58:16.833 回答