本次学习应用于文本处理方面,前面我们说到实现统计不同种类的字符频率
,本次目的是为了实现查询指定字词或统计全文字频。
在上一篇博客中我们讲到了正则表达式的实际应用。
在这里我们将要与数据打交道,所以我们将要学习JAVA中的“容器”(其中的Map接口
)。
现实生活中,我们经常需要成对存储某些信息。比如,我们使用的微信,一个手机号只能对应一个微信账户。这就是一种成对存储的关系。
Map
就是用来存储“键(key)-值(value) 对”的。 Map
类中存储的“键值对”通过键来标识,所以“键对象”不能重复。
Map
接口的实现类有HashMap
、TreeMap
、HashTable
、Properties
等。
HashTable类和HashMap用法几乎一样,底层实现几乎一样,只不过HashTable
的方法添加了synchronized
关键字确保线程同步检查,效率较低。
HashMap与HashTable的区别
:
HashMap
: 线程不安全,效率高。允许key或value为null。
HashTable
: 线程安全,效率低。不允许key或value为null。
TreeMap是红黑二叉树的典型实现。 TreeMap的put()/remove()
方法大量使用了红黑树的理论。
TreeMap和HashMap实现了同样的接口Map,因此,用法对于调用者来说没有区别。HashMap
效率高
于TreeMap;在需要排序的Map时才选用TreeMap
。
BufferedReader/BufferedWriter
处理流:将Reader/Writer对象进行包装,增加缓存功能,提高读写效率。使用InputStreamReader/OutputStreamWriter
处理流:将字节流对象转化成字符流对象。/* 项目名称:Task_Shao
* 创建时间:2019年2月21日
* 创建者:Administrator_wz
* 创建地点:kmust
* 功能:文本处理中的特定字频统计(java 排序容器HashMap统计方法)
*/
import java.io.*;//导入java.io包中的所有类
import java.util.*;//导入java.util包中的所有类
import java.util.regex.Matcher;//导入java.util包中的Matcher类
import java.util.regex.Pattern;//导入java.util包中的Pattern类
public class character_frequency_statistics {//创建类名
public static void main(String[] args) throws IOException { //程序的主函数入口
long startTime=System.currentTimeMillis();//定义开始时间,用于统计程序的运行时长
try{//用try-catch语句将逻辑语句包起来,并读取指定的文件
Scanner s1 = new Scanner(System.in);//获取键盘输入并赋值给s1字符串
System.out.println("请输入想要打开的文本文档:");//输入提示信息
String a = s1.nextLine();//定义字符串变量,并赋值为用户输入的信息
InputStreamReader isr = new InputStreamReader(new FileInputStream(a),"utf-8"); //指定文件格式为utf-8
BufferedReader br = new BufferedReader(isr);//用于读取指定文件
StringBuffer c = new StringBuffer();//定义
Scanner s2 = new Scanner(System.in);//获取键盘输入并赋值给s2字符串
System.out.println("请选择是否单个字符(串)查询:确定(1)/拒绝(2)");//提示选择查询方式
int b = s2.nextInt();//定义一个int类型变量,选择查询方式
switch(b){//查询方式类
case 1://查询类型1,单字符查询
Scanner s3 = new Scanner(System.in);//获取键盘输入并赋值给s3字符串
System.out.println("请输入想要查询的字符(串):");//输入提示信息
String d = s3.nextLine();//定义字符串变量,并赋值为用户输入的信息
String str1 = null;//定义一个字符串类型变量str1
int n1 = 0; //定义一个int类型变量n1
while((str1=br.readLine())!=null){//readLine()方法, 用于读取一行,只要读取内容不为空就一直执行
c.append(str1);//添加进字符串c
}
Pattern p = Pattern.compile(d);//创建正则表达式
Matcher m = p.matcher(c);//创建合适的匹配器
while(m.find()) {//匹配函数,true时执行
n1++;//数值加1
}
br.close();//关闭数据流
System.out.println("该字符(串)在文本中出现了"+n1+"次");//输出总的出现次数
//System.out.println("测试1");//测试1
break;
case 2://查询类型2,文本统计并写出
Scanner s4 = new Scanner(System.in);//获取键盘输入并赋值给s4字符串
System.out.println("请输入想要写入内容的文本文档:");//输入提示信息
String e = s4.nextLine();//定义字符串变量,并赋值为用户输入的信息
File file=new File(e);;//写入待写入文件
if(!file.exists()) {//if语句的条件,若指定路径下该文件不存在
file.createNewFile();//则在指定路径下新建该文件
}
int i = 0;//定义一个int类型变量i
String str2 = null;//定义一个字符串类型变量
while((str2 = br.readLine()) != null) {//readLine()方法, 用于读取一行,只要读取内容不为空就一直执行
c.append(str2);//将该行内容追加到字符串b的后面
}
HashMap<Character,Integer>map =Count(c.toString());//调用HashMap函数
PrintStream out = new PrintStream(file);//输出并存储
System.setOut(out);//改变输出流并将内容保存到指定路径
System.out.println("汉字统计结果:"+map);//输出结果
Iterator<Integer>it = map.values().iterator();//获取tm中values值并迭代
while(it.hasNext()) {//检查序列中是否含有元素,若有则为true
Integer j=(Integer)it.next();//定义变量获取元素
i+=j;//迭代求总字符数
}
br.close();//关闭数据流
System.out.println("总汉字数为"+i);//输出总的汉字数
//System.out.println("测试2");//测试2
break;
default:
System.out.println("该字符(串)有误");//输出错误提示
break;
}
}catch(Exception e){//当代码异常时用catch捕获异常
e.printStackTrace();//printStackTrace()方法是打印异常信息在程序中出错的位置及原因
}
long endTime=System.currentTimeMillis();//定义一个结束时间
long Time=endTime-startTime;//所需时间为结束时间-开始时间
System.out.println("耗时:"+Time+"毫秒");//输出所用时间
}
public static HashMap<Character,Integer>Count(String str) {//构造TreeMap统计方法
//String t = null;//定义一个字符串类型变量
char charArray[] = str.toCharArray();//将字符串转换为字符数组
HashMap<Character,Integer> map = new HashMap<Character,Integer>();//定义一个HashMap集合
for(int x = 0;x < charArray.length;x++) {//循环遍历字符数组
String t=Character.toString(charArray[x]);//返回一个字符串对象
if (t.matches("[\\u4e00-\\u9fa5]")) {//if语句的条件,判断是否为汉字
if(!map.containsKey(charArray[x])) {//if语句的条件,判断该汉字是否在 map中
map.put(charArray[x], 1);//若该汉字不在 map中则初始化其value值为1
} else {//若该汉字在tem中
int count = map.get(charArray[x])+1;//其出现次数增加1
map.put(charArray[x],count);//若汉字在 map中则其value值为count
}
}
}
return map;//返回map
}
}
另:demo1.txt文件是测试文件,xiyouji.txt文件为最终实现文件。
HashMap
底层实现采用了哈希表,这是一种非常重要的数据结构。对于我们以后理解很多技术都非常有帮助(比如:redis数据库的核心技术和HashMap一样),因此,非常有必要让大家理解。
数据结构中由数组和链表来实现对数据的存储,他们各有特点。
(1) 数组:占用空间连续。 寻址容易,查询速度快。但是,增加和删除效率非常低。
(2) 链表:占用空间不连续。 寻址困难,查询速度慢。但是,增加和删除效率非常高。
那么,我们能不能结合数组和链表的优点(即查询快,增删效率也高)呢? 答案就是“哈希表”。 哈希表的本质就是“数组+链表”。
当添加一个元素(key-value)时,首先计算key的hash值,以此确定插入数组中的位置,但是可能存在同一hash值的元素已经被放在数组同一位置了,这时就添加到同一hash值的元素的后面,他们在数组的同一位置,就形成了链表,同一个链表上的Hash值是相同的,所以说数组存放的是链表。 JDK8中,当链表长度大于8时,链表就转换为红黑树,这样又大大提高了查找的效率。(仅限于了解)
总之,实现Map接口
的类用来存储键(key)-值(value) 对。Map 接口的实现类有HashMap
和TreeMap
等。Map类中存储的键-值对通过键来标识,所以键值不能重复。
参考链接:
https://blog.csdn.net/youyou_yo/article/details/49284169
https://blog.csdn.net/qq_36843413/article/details/81511998
https://blog.csdn.net/qq_35529801/article/details/78757280
https://blog.csdn.net/qq_34944851/article/details/52625607
https://blog.csdn.net/qingxili/article/details/44427449
https://blog.csdn.net/ahua001/article/details/81169960
https://blog.csdn.net/wyf2017/article/details/80256729
https://blog.csdn.net/u013838592/article/details/83419005
https://blog.csdn.net/haishu_zheng/article/details/50434157
https://blog.csdn.net/New_feature/article/details/78340875