Java词频统计总结

思路:

  • 1.先对它进行分片
  • 2.对它根据分片位置进行词频统计
  • 3.对结果进行合并
    所用的一些方法:

1.在分片的过程中,解决分片的位置在句子中间的问题:

try (RandomAccessFile randomAccessFile = new RandomAccessFile(filePath, “r”)) {

while ((position + splice) < randomAccessFile.length()) {
    randomAccessFile.seek(position);

randomAccessFile.readLine();
filePoints.add(randomAccessFile.getFilePointer());
position = randomAccessFile.getFilePointer() + splice;
}
filePoints.add(randomAccessFile.length());
}
2.用线程池运行线程并获取到返回的结果:

    // 创建容量为poolNumber的线程池。

ExecutorService pool = Executors.newFixedThreadPool(poolNumber);

ArrayList<Future<Map<String, Integer>>> futures = new ArrayList<Future<Map<String, Integer>>>();

for (int i = 0; i < filePosition.size() - 1; i++) {
Map<String, Integer> wordFrequencyMap = getMap(wordList);
CountThread countThread = new CountThread(filePosition.get(i), filePosition.get(i + 1), filePath, wordFrequencyMap);
Future<Map<String, Integer>> f = pool.submit(countThread);
futures.add(f);

}
Map<String, Integer> wordFrequencyResMap = getMap(wordList);
System.out.println(“获取结果中…”);
for (Future<Map<String, Integer>> f : futures) {
try {
AddWordFrequency(wordFrequencyResMap, f.get());

} catch (Exception e) {
e.printStackTrace();
}
}
// 关闭线程池。
pool.shutdown();

优化:

原始代码:

//获取到只包含单词的word数组
String[] wordArray = words.split("[^a-zA-Z]");//40M的时候,所花时间4571
List wordList = new ArrayList(wordArray.length);
Collections.addAll(wordList, wordArray);
Stream wordStream = wordList.stream().filter(//过滤掉长度为null的字符串
word -> word.length() > 0);
wordList = wordStream.collect(Collectors.toList());

Iterator<Map.Entry<String, Integer>> it = wordFrequencyMap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, Integer> entry = it.next();
String key = entry.getKey();
entry.setValue(Collections.frequency(wordList, key));//直接将统计到的数据放到COllections中
优化后:

StringTokenizer st = new StringTokenizer(words);
while (st.hasMoreTokens()) {

String key = st.nextToken();
Integer value = wordFrequencyMap.get(key);
if (value != null) {
wordFrequencyMap.put(key, value + 1);
}

}

优化前所花费时间(40M):

统计开始时间:1597652148013
统计结束时间:4571
一个线程的统计结果:{xiaomi=489882, huawei=489620, oppo=490377, vivo=488909}
开始了?
得到结果.
xiaomi:489882
huawei:489620
oppo:490377
vivo:488909
startTime: 1597652147871
endTime: 1597652152802
所花时间:4931

优化后,所花时间(40M):

获取结果中…
统计开始时间:1597654371527
统计结束时间:526
得到结果.
xiaomi:489882
huawei:489620
oppo:490377
vivo:488909
startTime: 1597654371383
endTime: 1597654372053
所花时间:670

前片大小调参结果:

//2M 5713 3M 5886 4M的切片 6221 40M的切片7828
总结:

并不是封装的越好的代码越快,jdk6的正则表达式进行分割的速度是真的慢

另外:其他同时的hashMap优化,记小本本上:

while (st.hasMoreTokens()) {
String key = st.nextToken();

Integer value = wordFrequencyMap.get(key);
if (value != null) {
wordFrequencyMap.put(key, value + 1);
}
}
他这个优化,真的强
while (token.hasMoreTokens()) {
//将处理结果放到一个HashMap中
word = token.nextToken();

MutableInteger initValue = new MutableInteger(1);
// 利用 HashMap 的put方法弹出旧值的特性
MutableInteger oldValue = counter.put(word, initValue);
if (oldValue != null) {
initValue.set(oldValue.get() + 1);
}
}

老同事再优化:
1.收获点,真的是化繁为简,因为,全用的[], 但是想想着实没毛病,既然是固定的,就用[]了,比较hashmap它本身底层就是[],而且还多了寻址,而ArrayList底层也是[],为了动态扩展性在添加元素的时候,总要看一下大小,或多或少都会影响性能。

2.他这里用了AtomicInteger的静态变量,用它提供的cas方法来维持它的一致性

3.在分片的时候,它用了尾递归的写法(在c里面它会进行优化,使得新调用的函数会复用原来的栈,而不会出现栈溢出的情况,但看网上说Java不会,至少8之前没有,之后的信息并不多,等一下,测试一下)

4,最精准的优化:

     内存映射,读取文件

MappedByteBuffer mapBuffer = rAccessFile.getChannel().map(FileChannel.MapMode.READ_ONLY, start, this.sliceSize);
int hashCode = 0;
int index = 0;
while (index < this.sliceSize) {
// 3.按字节流读取,遍历每个字节,找出纯数字,计算加入片段总和
index++;
char c = (char) mapBuffer.get();
if (c != ‘\r’ && c != ‘\n’ && c != ’ ') {
hashCode = 31*hashCode+c;
} else {
if(hashCode != 0) {
if (hashCode == “vivo”.hashCode()) {
count[0]++;
} else if (hashCode == “oppo”.hashCode()) {
count[2]++;
} else if (hashCode == “xiaomi”.hashCode()) {
count[3]++;
} else if (hashCode == “huawei”.hashCode()) {
count[1]++;
}else if(hashCode == “lenovo”.hashCode()){
count[4]++;
}else if(hashCode == “oneplus”.hashCode()){
count[5]++;
}else if(hashCode == “iqoo”.hashCode()){
count[6]++;
}
//清空等下一次
hashCode = 0;
}
}
}
主要是他这里,就文件流而言就读取了一次,而像我的代码里是先把它读出来再分割,然后再取出来比较,而他这里就是直接一次性就整完了,读完了处理也就完了,神了

@deprecated 这个注解是表明它是过期的意思,而不是描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现词频统计的一种简单方法是使用Java中的HashMap数据结构,将每个单词作为键,出现次数作为值。具体实现步骤如下: 1. 读取文本文件,将文件内容存储在一个字符串中。 2. 使用正则表达式将字符串中的非字母字符替换为空格,以便将文本分割成单词。 ```java String content = readFileAsString("input.txt"); // 读取文本文件 content = content.replaceAll("[^a-zA-Z]", " "); // 将非字母字符替换为空格 ``` 3. 将文本分割成单词,并统计每个单词出现的次数。 ```java Map<String, Integer> wordCount = new HashMap<>(); String[] words = content.split("\\s+"); for (String word : words) { word = word.toLowerCase(); // 将单词转换为小写字母,以便不区分大小写 if (!word.isEmpty()) { int count = wordCount.getOrDefault(word, 0); wordCount.put(word, count + 1); } } ``` 4. 输出每个单词及其出现次数。 ```java for (String word : wordCount.keySet()) { int count = wordCount.get(word); System.out.println(word + ": " + count); } ``` 完整代码如下: ```java import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; public class WordCount { public static void main(String[] args) throws IOException { String content = readFileAsString("input.txt"); // 读取文本文件 content = content.replaceAll("[^a-zA-Z]", " "); // 将非字母字符替换为空格 Map<String, Integer> wordCount = new HashMap<>(); String[] words = content.split("\\s+"); for (String word : words) { word = word.toLowerCase(); // 将单词转换为小写字母,以便不区分大小写 if (!word.isEmpty()) { int count = wordCount.getOrDefault(word, 0); wordCount.put(word, count + 1); } } for (String word : wordCount.keySet()) { int count = wordCount.get(word); System.out.println(word + ": " + count); } } private static String readFileAsString(String filePath) throws IOException { return new String(Files.readAllBytes(Paths.get(filePath))); } } ``` 其中,`readFileAsString`方法用于读取文本文件并返回字符串。如果需要统计多个文件的词频,可以将整个统计过程放到一个循环中,每次读取一个文件并更新词频统计结果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值