【问题标题】:String .contains VS Set<String> .contains VS Regex String.matches()字符串 .contains VS Set<String> .contains VS 正则表达式 String.matches()
【发布时间】:2016-08-09 10:31:34
【问题描述】:

我在两个文件中有两组不是很长(200~500 字)的字符串,如下所示:

File1          File2

this           window
that           good
word           work
java           fine
book           home

所有唯一的词。

现在首先从文件中读取字符串(逐行)并将它们存储在:

  1. Set&lt;String&gt; set1 Set&lt;String&gt; set2:可能看起来像这样:[this, that, word, java, book] [window, good, work, fine, home]

或者

  1. String str1 String str2:可能看起来像这样:str1: thisthatwordjava str2: windowgoodworkfinehome OR 可以是str1: this,that,word,java(用逗号分隔)。

现在有三种方法可以检查出现SetString 的单词home

  1. 使用set1/2.contains("home")
  2. 使用str1/2.contains("home")
  3. 使用str1/2.matches("home")

以上所有方法都可以正常工作,但哪一个是最好的一个

注意:这个问题的目的是因为检查字符串的频率非常高。

【问题讨论】:

  • String.contains() 将为 "ava" 返回 true。
  • 我的直觉是 Set 会更好。因为我猜它使用对象的哈希来比较相等性。
  • @kennytm 如果在检查之前将逗号附加到输入字符串的开头和结尾,则不会。
  • Strings 的Set 中存储单个String 的原因是什么?请阅读How to Ask 并提供minimal reproducible exampleIt's unclear what you're asking.
  • @BahramdunAdil 您是否分析了您的程序并发现 Set 比较是一个热点?

标签: java string set


【解决方案1】:

不要做性能假设

是什么让您认为 String.contains 会有“更好的性能”?

不会,除非是非常简单的情况,即如果:

  • 您的字符串列表很短,
  • 要比较的字符串很短,
  • 您想要进行一次性查找。

对于所有其他情况,Set 方法将更好地扩展和工作。当然,与单个字符串相比,Set 会产生内存开销,但即使您想要存储数百万个字符串并比较长字符串,O(1) 查找也将保持不变。

适合工作的正确数据结构和算法

使用更安全、更健壮的设计,尤其是在这里实施起来并不困难。而且正如你提到的你会经常检查,那么一套方法肯定更适合你。

此外,String.contain 将是不安全的,就好像您的两者都有匹配的字符串和子字符串一样,您的查找将失败。正如 kennytm 在评论中所说,如果我们使用您的示例,并且您的列表中有“java”字符串,则查找“ava”将匹配它,这显然是您不想要的。

选择合适的套装

您可能不想使用简单的 HashSet 或调整其设置。例如,您可以考虑使用 Guava ImmutableSet,如果您的 set 只会创建一次但会经常检查。

示例

这就是我要做的,假设您想要一个不可变的集合(正如您所说的,您从文件中读取字符串列表)。这是临时的,没有验证,所以请原谅缺乏仪式。

使用 Java 8 + Guava

import com.google.common.collect.ImmutableSet;
import com.google.common.io.Files;
import com.google.common.base.Splitter;

final Set<String> lookupTable = ImmutableSet.copyOf(
  Splitter.on(',')
    .trimResults()
    .omitEmptyStrings()
    .split(Files.asCharSource(new File("YOUR_FILE_PATH"), Charsets.UTF_8).read())
);

使用正确的路径、正确的字符集以及是否要允许空格和空字符串进行修剪的季节。

仅使用 Java 8

如果您不想使用 Guava 而只使用 vanilla Java,那么只需在 Java 8 中执行类似的操作(再次道歉,未经测试):

final Set<String> lookupTable =
    Files.lines(Paths.get("YOUR_FILE_PATH"))
      .map(line -> line.split(",+"))
      .map(Arrays::stream)
      .collect(toSet());

使用 Java

如果你的 Java

【讨论】:

  • 如果他只需要进行一次查找,String.contains 可能会更快。在这种情况下,他不需要拆分字符串并将单词添加到Set
  • 正如我评论 kennytm 的评论一样;如果您检查输入不包含任何逗号,然后在字符串的开头和结尾添加逗号,则使用String.contains 将是安全的。
  • 这并不是查找的简单性问题,而是他想要查找的字符串集的大小。考虑到Set 方法的实现的低复杂性,我不明白你为什么会选择String.contains。它不那么安全、不那么健壮、不那么可扩展。它只会提高内存效率。您针对 kennytm 的评论提出的建议只会增加查找的复杂性。我会为正确的工作选择正确的数据结构。
  • 我说如果他需要一次查找。在这种情况下,不值得拆分字符串并将所有元素添加到集合中。如果我可能会问(考虑到我在第二条评论中写的内容),那怎么不安全?
  • 他说他不需要在问题中进行一次查找:Note: The purpose of this question is because the frequency of checking for string is very high.
【解决方案2】:

我猜你还是将文件的行读入了一个字符串,所以如果你只计划一个查询,那么拆分它并将子字符串存储在一个集合中并不是最优的。

【讨论】:

  • 他说:Note: The purpose of this question is because the frequency of checking for string is very high.
  • @haylem 查看同一组词的频率还是他需要查看不同组的词?我以为他的意思是后者。
  • 我理解的方式意味着他会想要在一组(或多组)中经常检查多个字符串。不过这并不重要。即使您理解后者,如果他想多次重新检查相同的集合,仍然最好构建集合(并可能缓存它们)。随着时间的推移,集合方法会更有效,除非他有一个非常狭窄的用例,即一次性查找不被重用的短集合中的短字符串。
  • 遗憾的是,这个问题确实令人困惑,很难正确回答。无论如何我喜欢你的回答,因为它很详细。
【解决方案3】:

如果给定没有逗号的单词,Set 应该占用更多内存空间但执行时间更少(可以通过简单的拆分来完成)

但我真正认为最好的方法是实验证明 System.currentTimeMillis()

【讨论】:

    【解决方案4】:

    如果您想了解有关性能差异的信息。简单地测量一下。这是给你的测试设置。

    final int WORDS = 10000;
    final int SEARCHES = 1000000;
    
    Set<String> strSet = new TreeSet<String>();
    String strStr = "";
    int[] searches = new int[SEARCHES];
    Random randomGenerator = new Random();
    
    // filling set and string
    for(int i = 0; i < WORDS; i++){
        strSet.add(String.valueOf(i));
        strStr += "," + String.valueOf(i);
    }
    
    // creating searches
    for(int i = 0; i < SEARCHES; i++)
        searches[i] = randomGenerator.nextInt(WORDS);
    
    // measure set
    long startTime = System.currentTimeMillis();
    for(int i = 0; i < SEARCHES; i++)
        strSet.contains(String.valueOf(searches[i]));
    System.out.println("set result " + (System.currentTimeMillis() - startTime));
    
    // measure string
    startTime = System.currentTimeMillis();
    for(int i = 0; i < SEARCHES; i++)
        strStr.contains(String.valueOf(searches[i]));
    System.out.println("string result " + (System.currentTimeMillis() - startTime));
    

    对我来说,输出是一个有意义的证据,证明你应该留在Set

    设置结果 350
    字符串结果 14197

    【讨论】:

    • 对于单个搜索,String.contains 可能会更快。您不会得到拆分字符串并将其添加到集合中的开销。
    • 单次搜索可以更快地打印所有单词并让我奶奶搜索:-D
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-05
    • 2013-08-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多