Java 更快的字符串匹配/迭代方法?

Java 更快的字符串匹配/迭代方法?,java,performance,Java,Performance,在我目前正在进行的程序中,有一部分需要花费一些时间。基本上,我有一个字符串列表和一个目标短语。例如,假设目标短语是“产成品库存”。现在,在过滤掉停止词(of)之后,我想从包含三个词之一的列表中提取所有字符串:“inventory”、“finished”和“goods”。现在,我实现了如下想法: String[] targetWords; // contains "inventory", "finished", and "goods" ArrayList<String> extract

在我目前正在进行的程序中,有一部分需要花费一些时间。基本上,我有一个字符串列表和一个目标短语。例如,假设目标短语是“产成品库存”。现在,在过滤掉停止词(of)之后,我想从包含三个词之一的列表中提取所有字符串:“inventory”、“finished”和“goods”。现在,我实现了如下想法:

String[] targetWords; // contains "inventory", "finished", and "goods"
ArrayList<String> extractedStrings = new ArrayList<String>();

for (int i = 0; i < listOfWords.size(); i++) {
    String[] words = listOfWords.get(i).split(" ");
    outerloop:
    for (int j = 0; j < words.length; j++) {
        for (int k = 0; k < targetWords.length; k++) {
            if (words[j].equalsIgnoreCase(targetWords[k])) {
                extractedStrings.add(listOfWords.get(i));
                break outerloop;
            }
        }
    }
}
String[]targetWords;//包含“库存”、“成品”和“货物”
ArrayList extractedStrings=新的ArrayList();
对于(int i=0;i

该列表包含超过10万个单词,完成每个目标短语的任务大约需要0.4到0.8秒。问题是,我有很多这样的目标短语需要处理,而且秒数真的加起来了。因此,我想知道是否有人知道更有效的方法来完成这项任务?提前谢谢你的帮助

我会尝试使用
ExecutorService
来实现它,以便并行搜索每个单词。

例如,对于固定的线程池大小:

Executors.newFixedThreadPool(20);

您的100k单词列表可以(一次)添加到哈希集中。使用
wordSet.contains()
-哈希集提供恒定的时间性能,因此不受列表大小的影响,而不是遍历列表。

您可以将庞大的单词列表添加到哈希映射中,然后当短语出现时,只需在短语中的单词上循环,并对照哈希映射进行检查。目前,你正在做一个线性搜索,我的建议是将其缩减为一个固定时间搜索


关键是最小化查找。使用此技术,您可以有效地为庞大的单词列表编制索引,以便快速查找。

我有点困惑,您是想要整个短语,还是只想要列表中的单个单词。如果您试图从listOfWords中获取字符串,如果您的目标单词之一在该字符串中,那么这应该适合您

    String[] targetWords= new String[]{"inventory", "finished", "goods"};
    List<String> listOfWords = new ArrayList<String>();

    // build lookup map
    Map<String, ArrayList<String>> lookupMap = new HashMap<String, ArrayList<String>>();
    for(String words : listOfWords) {
        for(String word : words.split(" ")) {
            if(lookupMap.get(word) == null) lookupMap.put(word, new ArrayList<String>());
            lookupMap.get(word).add(words);
        }
    }

    // find phrases
    Set<String> extractedStrings = new HashSet<String>();
    for(String target : targetWords) {
        if(lookupMap.containsKey(target)) extractedStrings.addAll(lookupMap.get(target));
    }
String[]targetWords=新字符串[]{“库存”、“成品”、“商品”};
List listOfWords=new ArrayList();
//构建查找映射
Map lookupMap=newhashmap();
for(字符串:ListofWord){
for(字符串字:words.split(“”){
if(lookupMap.get(word)==null)lookupMap.put(word,newarraylist());
查找地图。获取(单词)。添加(单词);
}
}
//查找短语
Set extractedStrings=new HashSet();
for(字符串目标:targetWords){
if(lookupMap.containsKey(target))extractedStrings.addAll(lookupMap.get(target));
}

您正在通过
targetWords
中的每个元素,而不是同时检查targetWords中的所有单词。此外,您在每次迭代中拆分单词列表,而实际上并不需要它,这会造成开销

我建议您将
targetWords
合并为一个(已编译):

不要忘记在正则表达式字符串中双引号引空格

import java.util.regex.*;
...
Pattern targetPattern = Pattern.compile("(?xi)\\b(inventory|finished|goods)\\b");
for (String singleString : listOfWords) {
  if (targetPattern.matcher(singleString).find()) {
    extractedStrings.add(singleString);
  }
}

如果您对正则表达式的速度不满意(尽管正则表达式引擎通常针对性能进行了优化),则需要使用自己的高速多字符串搜索。对于在文本中搜索多个固定字符串进行了优化,但是与简单地创建一个模式相比,实现此算法当然是相当费劲的。

这是O(N^3)。通过使用HashMap而不是内部循环,可以将其缩减为O(N^2)。但是我对
j
上的循环感到困惑。为什么你的单词列表还没有一个单词列表?为什么要再次拆分每个项目?对不起,我应该更好地命名变量-listOfWords实际上包含短语,因此我拆分短语以获得每个短语中的每个单词。我认为他的单词是短语,而不是单词,因此contains无法在字符串中找到字符串。@denov OK,可能需要一个更复杂的结构,如
HashMap
-关键是要做一次预处理(而不是在每个循环中拆分常量数据),并尝试避免重复。很抱歉,listOfWords包含短语,我将它们拆分为单个单词,以便与目标短语中的单词进行比较。如果我没弄错的话,您的解决方案是否会为具有多个单词匹配的短语创建重复项?例如,假设targetWords是相同的,如果我遇到短语“货物库存”,extractedStrings将包含该短语两次,因为单词“库存”和“货物”都在查找图中?我应该迭代并删除所有重复项吗?我更新了我的代码,所以extractedStrings是一个集合,这样你就不会有重复项了。这真的很聪明。我喜欢+1我很好奇,看看这在非常大的列表、字符串非常长的列表上是否更快,以及与我使用HashMap进行查找的答案相比,您是否需要进行多次查找。有人想写一个测试???@denov用战争与和平测试,包含65007行。targetWords与问题中的一样。当通过简单地检查currentTimeMillis来计时时,基于HashMap的解决方案的计时时间为350ms,正则表达式的计时时间为200ms,首先是正则表达式(因此VM仍在预热)。在regex之前切换HashMap时,其390ms HashMap与160ms regex。我没有测量内存占用(HashMap解决方案的内存占用也应该更高)。@nd。谢谢你编写了一个测试,你是在大海捞针还是在大海捞针。是的,我肯定有
import java.util.regex.*;
...
Pattern targetPattern = Pattern.compile("(?xi)\\b(inventory|finished|goods)\\b");
for (String singleString : listOfWords) {
  if (targetPattern.matcher(singleString).find()) {
    extractedStrings.add(singleString);
  }
}