2013-03-02 344 views
1

嗨,我正在读取一个文本文件并将每行(用逗号分隔)保存到一个数组中。唯一的问题是数组中的大多数元素都是double值,其中两个元素是字符串。作为这个的结果,我不得不使数组成为一个String []数组。由于这个原因,只要我想对数组中的double值执行一些方程式,我必须首先将它们解析为双精度值。我从字面上运行这些方程的1000多次迭代,因此我的代码不断地将字符串解析为双精度。这是一个代价高昂的方式,它会减慢我的程序。有没有更好的方法可以将字符串数组中的值转换为double值,或者在保存文本文件中的行时应该采取更好的方法吗?由于Java - 将字符串转换为双倍字符串的最有效方法

这里是阵列的一个看起来像我已经从文本文件中读取后:

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

我现在需要乘以第一2种元素,并添加到第3的总和,第4和第七个要素。换句话说,我只使用数字内容(被ofcourse保存为字符串)

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

我必须为文本文件(超过1000行)每一行做到这一点。由于这个,我的程序运行非常缓慢。有没有更好的方法,我可以将字符串值转换为双精度?还是有更好的方法,我应该把它们放在首位呢?

编辑:我已经使用Profiler来检查代码的哪一部分是最慢的,它确实是我上面

+0

为什么不只是将它们** **转换一次**并存储结果? – 2013-03-02 16:38:47

+8

调用'Double.parseDouble()'5000次不会让你的程序运行速度非常慢。 – NPE 2013-03-02 16:38:58

+0

@OliCharlesworth - 每条线都不一样。因此涉及到不同的值 – Matt9Atkins 2013-03-02 16:40:15

回答

4

下面是一个生成输入文件的例子,如您所描述的输入文件长度为10000行,然后将其读回并进行发布的计算并将结果输出到stdout。在读取文件时,我特别禁用了任何缓冲,以获得最差的读取性能。正如其他人所建议的,我也没有做任何缓存。整个过程(包括生成文件,进行计算和打印结果)始终需要大约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; 
    } 
} 
2

你会做的更好,以创建合适的对象,并存储值中的代码这将给你两个主要的好处,1)你的代码会更快,因为你避免不必要的重新计算double值,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(); 
    } 
} 

通知有一个额外的好处,那得分法现在更加健壮。上面的实现对我们想要使用的字段进行了硬编码,但通过解析和存储这些字段作为结构内容,我们能够实现更具可读性,更具可扩展性的分数计算方法。