思路:
- 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 这个注解是表明它是过期的意思,而不是描述