我有一个HashMap<String, Integer> vocabulary,其中包含单词及其权重(不重要,此处仅字符串很重要):

   vocabulary = ["this movie"=5, "great"=2, "bad"=2, ...]


和一个标记字符串作为列表:

String str = "this movie is great";
List<String> tokens = tokenize(str) // tokens = ["this", "movie", "is", "great", "this movie", "is great", ...]


现在,我需要一种快速的方法来为此标记化字符串创建一个向量,该向量计算词汇表的每个条目,该词在标记化字符串中的出现次数

HashMap<String, Integer> vec = new HashMap();
Iterator it = vocabulary.entrySet().iterator();
while (it.hasNext()) {
   Map.Entry pair = (Map.Entry) it.next();
   String word = (String) pair.getKey();
   int count = 0;
   for (String w : tokens) {
      if (w.equals(word)) {
         count += 1;
      }
   }
   vec.put(word, count);
}


所以vec应该是["this movie"=1, "great"=1, bad = 0]

有更好的执行方法吗?我在更大的范围内遇到了性能问题,并假设此问题必须在此处,因为词汇量大约有300'000个条目。普通的标记化文本包含大约100个单词。

词汇表是hashMap是否存在问题?

最佳答案

计算tokens每个元素的出现次数:

Map<String, Long> tokensCount = tokens.stream().collect(
  Collectors.groupingBy(Function.identity(), Collectors.counting()));


然后,只需从此地图中查找即可,而不是从内部循环中查找:

count = tokensCount.getOrDefault(word, 0L).intValue();


这更快,因为在映射中的查找为O(1),而迭代tokens寻找相等的元素为O(#令牌)。



还要注意,除了获取密钥之外,您没有使用pair,因此可以迭代vocabulary.keySet(),而不是vocabulary.entrySet()

另外,如果您不使用原始迭代器,则不需要显式强制转换:

Iterator<Map.Entry<String, Integer>> it = ...




编辑,现在您已经添加了两个集合的相对大小:

您可以简单地迭代tokens,然后查看vocabulary是否包含以下内容:

Map<String, Integer> vec = new HashMap<>();
for (String token : tokens) {
  if (vocabulary.contains(token)) {
    vec.merge(token, 1, (old,v) -> old+v);
  }
}

09-12 06:39