2019-8-29 [Java] 面试题:读取一个文档,并统计出其中重复性单词的TopN

文章目录

  • 1.需求:
  • 2.分析
  • 3.实现代码
  • 4.比较器
  • 5.API
  • 6.思想
  • 7.效果

1.需求:

读取一个文档,并统计出其中重复性单词的TopN。
2019-8-29 [Java] 面试题:读取一个文档,并统计出其中重复性单词的TopN_第1张图片
这个文档364万行,那么我们如何统计呢?

2.分析

利用IO流将文档的单词读取
将其存为map的K,V,新的单词记为K,出现次数记为V,
利用比较器进行比较,遇到重复的单词找到他对应的V加一。

3.实现代码

本代码选用的文档地址E:\words.txt
建议将自己的文档改为.txt结尾,这样不会报错
修改此地址即可

import java.io.BufferedReader;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class topn {

	public static void main(String[] args) throws Exception {

		/*
		 * 首先将文件的内容读取到缓冲区bufferedreader中,
		 * 利用的是BufferedReader中的readline()读取文件中的每一个文本行,
		 * readline()并不是一行一行读取的,而是一个文本行一个文本行读取。 什么是文本行?
		 * 看JavaAPI中BufferedReader类总中的readline()的解释。
		 */
		BufferedReader readfile = new BufferedReader(new FileReader("E:\\words.txt"));
		StringBuffer sb = new StringBuffer();
		String text = null;
		while ((text = readfile.readLine()) != null) {
			// 将从文件中读出来的字符串追加,形成一个字符串
			sb.append(text);
		}
		readfile.close();
		
		// 用Pattern类中的complie()方法,将正则表达式编译到模式中
		Pattern patten = Pattern.compile("[a-zA-Z]+");
		
		// 用Pattern类中的matcher()方法,生成一个匹配器对象,Matcher类是匹配器类
		String sbstring = sb.toString();
		Matcher matcher = patten.matcher(sbstring);
		Map<String, Integer> tp = new TreeMap<String, Integer>();
		while (matcher.find()) {
			// 用Matcher类中的find()方法,查找与模式匹配的下一个子序列 
			String word = matcher.group();
			// 用Matcher类中的group()方法, 返回匹配的子序列 
			if (tp.containsKey(word)) {
				// 统计每个单词出现的次数
				Integer wordfrequency = tp.get(word);
				tp.put(word, wordfrequency + 1);
			} else {
				tp.put(word, 1);
			}
		}
				
		/*
		 * 将treemap中的键值对的set视图存入ArrayList中,其中的类型必须是Map.Entry,
		 * 因为TreeMap中的entrySet()方法的返回类型就是Map.Entry类型,其实Map.Entry就是个接口。
		 * 将treemap存入ArrayList的目的就是用Collections类中的sort()方法进行排序,
		 * 其中的sort(Listlist,Comparator)是按照指定的比较器进行排序
		 */
		List<Map.Entry<String, Integer>> list = new ArrayList<Map.Entry<String, Integer>>(tp.entrySet());
				
		/*
		 * 重写Comparator比较器,目的是让TreeMap按照value进行降序排列,这里的重写比较器用的是匿名类,
		 * 先创建实现Comparator接口的类,并重写其中的compare方法,并不是接口实例化了。
		 */
		Comparator<Map.Entry<String, Integer>> comparator = new Comparator<Map.Entry<String, Integer>>() {						
			// 如果是实现升序就是return(param1.getValue().compareTo(param2.getValue());
			public int compare(Map.Entry<String, Integer> param1, Map.Entry<String, Integer> param2) {
				return (param2.getValue().compareTo(param1.getValue()));				
			}
		};		
		// 按照指定的比较器,对list列表进行升序或者降序排序
		Collections.sort(list, comparator);
		Scanner intn = new Scanner(System.in);
		System.out.println("请输入想要前几名单词排行");
		int n = intn.nextInt();
		System.out.println();
		for (int i = 0; i < n; i++) {
			String key = list.get(i).getKey();
			Integer value = list.get(i).getValue();
			System.out.println((i+1)+":"+ key + ":" + value);
		}
	}
}

4.比较器

比较器中的compare方法只是负责将两个参数比较的结果返回,并不负责进行排序。进行排
序的是sort()方法,它根据compare()方法的返回值进行相应排序。

第一个参数小于第二个参数 返回负数 ;第一个参数大于第二个参数 返回正数;第一个参数等于第二个参数 返回0。

5.API

sort(List <T> list, Comparator <? super T> c)

根据指定比较器产生的顺序对指定列表进行排序。

Comparator里的compare方法在api里解释是:比较用来排序的两个参数。

根据第一个参数小于、等于或大于第二个参数分别返回负整数、零或正整数。

6.思想

问题:

重写Comparator里的compare方法,compare根据传进去的两个对象进行对比后返回一个整数,compare只是返回了一个整数来表明<,=,>,
但是并没有根据compare的返回结果对这两个对象进行某种排序,那么最后比较器是怎么产生顺序的?

一般思路都是从升序开始(可能人的意识是看到排序最初的印象是升序),但是因为sort本身是个模板,也就是希望sort不改变,由用户传入的比较器来决定排序。

所以6L的伪代码就是基于升序的基础去处理的,sort里面只需要关心比较器的返回值,如果返回值大于0,则说明比较参数1大于比较参数2,则把参数1"沉底"(换过来说就是参数2冒泡),

这样当用户改变比较器,原本参数1大于参数2应该返回大于0的,可用户却故意返回小于0,

这样相当于比较器欺骗了sort,sort因为只关心结果,于是就认为参数2大于参数1,于是把参数2"沉底"(参数1冒泡),这样就相当于把“小”的放到最底,于是也就成了“降序”排序。

所以,排序的模板一般来说都是基于升序实现的,如果要达到“降序”,就由用户去实现比较器然后传给sort方法就可以了

7.效果

可以看到,我尝试输出前10的排名还是很成功的
2019-8-29 [Java] 面试题:读取一个文档,并统计出其中重复性单词的TopN_第2张图片

你可能感兴趣的:(java)