创建单词出现计数向量的快速方法

时间:2017-12-28 10:32:38

标签: java

我有一个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是一个问题吗?

2 个答案:

答案 0 :(得分:3)

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

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

然后从这张地图而不是你的内循环中查找:

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

这更快,因为地图中的查找是O(1),而迭代寻找相等元素的tokens是O(#token)。

另请注意,除了获取密钥之外,您还没有使用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);
  }
}

答案 1 :(得分:0)

如果vocabulary已经是HashMap,则无需迭代它。只需使用方法contains,在HashMap的情况下,它是常量(O(1)),因此您只需迭代令牌列表。

for(String w : tokens) {
    if(vocabulary.contains(w)) {
        vec.put(w, vec.get(w) + 1);
    }
}