我有一个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是一个问题吗?
答案 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);
}
}