【问题标题】:Anyway to optimize a large (127K) reading english words txt file无论如何优化一个大(127K)阅读英文单词txt文件
【发布时间】:2022-01-21 15:19:18
【问题描述】:

这是我的功能:

public void addToList() throws IOException {
    String urlString = "http://web.stanford.edu/class/archive/cs/cs106l/cs106l.1102/assignments/dictionary.txt";
    URL url = new URL(urlString);
    Scanner scannerWords = new Scanner(url.openStream());
    while (scannerWords.hasNextLine()) {
        words.add(scannerWords.nextLine());
    }
}

这需要: 执行时间为 32.8 秒。

无论如何我可以优化它(也许每 10 行读取一次)?

【问题讨论】:

  • 你应该测量哪个部分需要这么长时间。我认为将行添加到列表的循环不是耗时的部分。再说一次,两者都不是传输 127kb 的文件。
  • @f1sh 它是。 while 循环是消耗部分。
  • 你能确保网络连接不是这里的瓶颈吗?
  • 您可以尝试给出的答案或使用老派的方式 - 完全避免Scanner
  • 这个问题被打破了。你想达到什么目的?您还需要查看多少优化解决方案?

标签: java arrays time


【解决方案1】:

这是我的尝试。我没有使用扫描仪,而是逐字阅读。这减少了使用 Scanner 的开销和层数。

        String urlString = "http://web.stanford.edu/class/archive/cs/cs106l/cs106l.1102/assignments/dictionary.txt";
        InputStream stream = new URL(urlString).openStream();
        
        
        BufferedInputStream bufferedStream = new BufferedInputStream(stream);
        ArrayList<String> words = new ArrayList<>();
        char[] chars = new char[100];
        int index = 0;
        
        
        long currentTimeMillis = System.currentTimeMillis();
        while(true){
            int c = bufferedStream.read();
            if (c == '\n'){
                words.add(new String(chars, 0, index));
                index=0;
            } else if (c < 0){
                words.add(new String(chars, 0, index));
                break;
            } else {
                chars[index++]  = (char) c;
            }
        }
        long currentTimeMillis1 = System.currentTimeMillis();
        
        stream.close();
        
        System.out.println("Time       = " + (currentTimeMillis1-currentTimeMillis) + " ms");
        System.out.println("Word count = " + words.size());
        System.out.println( "First word = "  +  words.get(0));
        System.out.println( "Last word  = " + words.get(words.size()-1));

    }

输出

run:
Time       = 707 ms
Word count = 127142
First word = aa
Last word  = zyzzyvas
BUILD SUCCESSFUL (total time: 0 seconds)

【讨论】:

  • 逐个字符将是可怕的。您绝对应该使用某种缓冲流,否则您将在系统调用上浪费大量时间。 (尽管 url 有可能返回一个缓冲流。)
  • @matt 你是对的。将其转换为缓冲输入流似乎可以提高性能。
  • 这是一个经验法则。我用 BufferedInputStream、InputStreamReader 和 BufferedReader 试了一下,得到的时间和你差不多。
  • 平均而言,我使用缓冲流获得了大约 100% 的改进。
【解决方案2】:
  1. 一起获取所有数据
  2. 应用您的过滤器来获得预期的字词

  public static void main(String[] args) throws IOException {
       printWords(new ArrayList<>(150000));
    }

  private static void printWords(List<String> list) throws IOException {
        final long l = System.currentTimeMillis();
        String urlString = "http://web.stanford.edu/class/archive/cs/cs106l/cs106l.1102/assignments/dictionary.txt";
        URL url = new URL(urlString);
        final long l2;
        final long l3;
        Charset encoding=Charset.defaultCharset();
        try (Scanner scanner = new Scanner(url.openStream(), String.valueOf(encoding))) {
            l2 = System.currentTimeMillis();
            String content = scanner.useDelimiter("\\A").next();
            list = Arrays.asList(content.split("\\n"));
            l3 = System.currentTimeMillis();
            //System.out.println(list);
        }
        final long l4 = System.currentTimeMillis();
        System.out.println(String.format("Total Time: %d",l4-l));
        System.out.println(String.format("Data fetching Time: %d",l2-l));
        System.out.println(String.format("Data collection Time: %d",l3-l2));
    }

输出:

Total Time: 2482
Data fetching Time: 465
Data collection Time: 2017

【讨论】:

  • 不好,还需要很长时间,我不需要特定的过滤。
  • 尝试预定义初始容量。
【解决方案3】:

嗯,显而易见的事情是只下载一次单词列表并使用本地副本,而不是每次运行程序时都通过网络获取它。

您有泄漏,因为您从未关闭URL.openStream() 返回的流(如果您将当前代码更改为使用文件,则会存在同样的问题)。这很容易通过在循环后添加scannerWords.close(); 来解决,但更好的、异常安全的方法是使用try-with-resources

不过,我会完全放弃Scanner,而只使用BufferedReader。比如:

import java.net.URL;
import java.util.*;
import java.util.stream.*;
import java.io.*;

// ...


private List<String> readLinesFromURL(String url) throws IOException {
    try (BufferedReader br
         = new BufferedReader(new InputStreamReader(new URL(url).openStream()))) {         
        return br
            .lines()
            .collect(Collectors.toCollection(ArrayList<String>::new));
    }
}

【讨论】:

  • 总时间:5156ms
  • @4EACH 600ms (Mine) vs 900ms (OP's) 在我不科学的基准上。我怀疑他们正在拨号连接或在算盘上运行 JVM 或其他东西。
猜你喜欢
  • 2011-03-10
  • 2012-04-14
  • 1970-01-01
  • 2017-04-24
  • 1970-01-01
  • 1970-01-01
  • 2019-01-27
  • 2019-12-01
  • 2012-05-11
相关资源
最近更新 更多