gpt4 book ai didi

java - 如何加快将文件解析为 HashMap 的速度

转载 作者:行者123 更新时间:2023-12-02 02:13:06 30 4
gpt4 key购买 nike

我编写了以下函数来解析大文本文件(大约 2 GB)逐行放入 Map 中,有效地计算每个单词的出现次数。我只对单词感兴趣(小写以避免条目重复),没有标点符号或空格。然而,在大文件上执行以下代码大约需要 3 分钟。我想知道为什么以及是否有办法加快速度。

import java.util.*;

public class Stream {

Map<String, Integer> map = new HashMap();

public void getLines() {

try (BufferedReader fileReader = new BufferedReader(new FileReader("resources/hugeFile"))) {
String line ;
while ((line = fileReader.readLine()) != null) {
String[] words = line.toLowerCase().replaceAll("[^a-z ]", "").split("\\s+");
for (int i = 0; i < words.length; i++) {
if (map.get(words[i]) == null) {
map.put(words[i], 1);
}
else {
int newValue = Integer.valueOf(String.valueOf(map.get(words[i])));
newValue++;
map.put(words[i], newValue);
}
}
}

} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

最佳答案

首先,如果您认真对待优化,则必须衡量性能。因为很多看似“改进”的“改进”可能被证明要么什么也没有带来,甚至使性能变得更差。在许多情况下,编译器比人类更好地优化代码。所以你必须做基准测试,请看下面的问题:

How do I write a correct micro-benchmark in Java?

我在下面发布了两个代码草图。这些实际上只是草图,只是为了提供一个粗略的想法。我既没有测试过它们,也没有进行过基准测试。

一个提示是您访问 map 的次数过多。您可以使用 map.get 检查它,然后使用 map.put 有条件地输入值。您可以使用 putIfAbsentcomputeIfAbsent 代替。此外,增加现有值(value)的方式也可能会得到改善。在这种情况下,我将使用可变的 AtomicInteger 而不是不可变的 Integer。所以我建议如下:

    Map<String, AtomicInteger> map = new HashMap<>();

Consumer<String> countWords = word -> map.computeIfAbsent(word, (w) -> new AtomicInteger(0)).incrementAndGet();

try (BufferedReader fileReader = new BufferedReader(new FileReader("resources/hugeFile"))) {
String line;
while ((line = fileReader.readLine()) != null) {
splitAndConsumeWords(line, countWords);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

接下来,您使用了 line.toLowerCase().replaceAll("[^a-z ]", "").split("\\s+") 将字符串转换为小写,仅保留字母和空格并将字符串拆分为单词。如果没有基准测试,我不确定,但我怀疑这可能是代码中最耗时的操作。而且不用正则表达式重写也没什么大不了的。您所需要的只是迭代字符串的字符,将它们转换为小写,附加到当前单词或丢弃。我就是这样做的。

我会创建一个数组,将每个字符映射到其替换字符。相同字符表示 a-z 或空格,小写表示 A-Z。所有其他字符将被映射到 0 这意味着它们应该被丢弃:

private static char[] ONLY_LETTERS_TO_LOWERCASE = new char[65535];

static {
ONLY_LETTERS_TO_LOWERCASE[' '] = ' ';
for (char c = 'a'; c <= 'z'; c++) {
ONLY_LETTERS_TO_LOWERCASE[c] = c;
}
for (char c = 'A'; c <= 'Z'; c++) {
ONLY_LETTERS_TO_LOWERCASE[c] = Character.toLowerCase(c);
}
}

然后您只需查找每个字符的替换并构建单词即可:

public static void splitAndConsumeWords(String line, Consumer<String> wordsConsumer) {

char[] characters = line.toCharArray();
StringBuilder sb = new StringBuilder(16);
for (int index = 0; index < characters.length; index++) {
char ch = characters[index];
char replacementCh = ONLY_LETTERS_TO_LOWERCASE[ch];
// If we encounter a space
if (replacementCh == ' ') {
// And there is a word in string builder
if (sb.length() > 0) {
// Send this word to the consumer
wordsConsumer.accept(sb.toString());
// Reset the string builder
sb.setLength(0);
}
} else if (replacementCh != 0) {
sb.append(replacementCh);
}
}
// Send the last word to the consumer
if (sb.length() > 0) {
wordsConsumer.accept(sb.toString());
}
}

ONLY_LETTERS_TO_LOWERCASE 映射表的替代方案是 if 语句,如下所示:

        if (ch >= 'a' && ch <= 'z' || ch == ' ') {
replacementCh = ch;
} else if (ch >= 'A' && ch <= 'Z') {
replacementCh = Character.toLowerCase(ch);
}
else {
replacementCh = 0;
}

我不确定什么会更好,我认为数组中的查找一定更快,但我不确定。这就是您最终需要基准测试的原因。

关于java - 如何加快将文件解析为 HashMap 的速度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49677564/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com